use 005 chanmodes and extban to follow ircd capabilities, so better support for unreal and inspire ircd

This commit is contained in:
Nicolas Coevoet 2013-12-01 12:30:34 +01:00
parent 34fbf7ba56
commit e6593efbe1

243
plugin.py
View File

@ -91,7 +91,7 @@ def matchHostmask (pattern,n):
return n.prefix return n.prefix
return None return None
def matchAccount (pattern,pat,negate,n): def matchAccount (pattern,pat,negate,n,extprefix):
# for $a, $~a, $a: extended pattern # for $a, $~a, $a: extended pattern
result = None result = None
if negate: if negate:
@ -100,27 +100,27 @@ def matchAccount (pattern,pat,negate,n):
else: else:
if len(pat): if len(pat):
if n.account != None and ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.account): if n.account != None and ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.account):
result = '$a:'+n.account result = '%sa:%s' % (extprefix,n.account)
else: else:
if n.account != None: if n.account != None:
result = '$a:'+n.account result = '%sa:%s' % (extprefix,n.account)
return result return result
def matchRealname (pattern,pat,negate,n): def matchRealname (pattern,pat,negate,n,extprefix):
# for $~r $r: extended pattern # for $~r $r: extended pattern
if n.realname == None: if n.realname == None:
return None return None
if negate: if negate:
if len(pat): if len(pat):
if not ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.realname): if not ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.realname):
return '$r:'+n.realname return '%sr:%s' % (extprefix,n.realname.replace(' ','?'))
else: else:
if len(pat): if len(pat):
if ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.realname): if ircutils.hostmaskPatternEqual('*!*@%s' % pat, '*!*@%s' % n.realname):
return '$r:'+n.realname return '%sr:%s' % (extprefix,n.realname.replace(' ','?'))
return None return None
def matchGecos (pattern,pat,negate,n): def matchGecos (pattern,pat,negate,n,extprefix):
# for $~x, $x: extended pattern # for $~x, $x: extended pattern
if n.realname == None: if n.realname == None:
return None return None
@ -130,7 +130,7 @@ def matchGecos (pattern,pat,negate,n):
if n.ip != None: if n.ip != None:
tests.append('%s!%s@%s' % (nick,ident,n.ip)) tests.append('%s!%s@%s' % (nick,ident,n.ip))
for test in tests: for test in tests:
test = '%s#%s' % (test,n.realname) test = '%s#%s' % (test,n.realname.replace(' ','?'))
if negate: if negate:
if not ircutils.hostmaskPatternEqual(pat,test): if not ircutils.hostmaskPatternEqual(pat,test):
return test return test
@ -139,11 +139,17 @@ def matchGecos (pattern,pat,negate,n):
return test return test
return None return None
def match (pattern,n): def match (pattern,n,irc):
# check if given pattern match an Nick # check if given pattern match an Nick
if pattern.startswith('$'): extprefix = ''
extmodes = ''
if 'extban' in irc.state.supported:
ext = irc.state.supported['extban']
extprefix = ext.split(',')[0]
extmodes = ext.split(',')[1]
if pattern.startswith(extprefix):
p = pattern[1:] p = pattern[1:]
negate = p[0] == '~' negate = extmodes.find(p[0]) == -1
if negate: if negate:
p = p[1:] p = p[1:]
t = p[0] t = p[0]
@ -152,32 +158,34 @@ def match (pattern,n):
# remove ':' # remove ':'
p = p[1:] p = p[1:]
if t == 'a': if t == 'a':
return matchAccount (pattern,p,negate,n) return matchAccount (pattern,p,negate,n,extprefix)
elif t == 'r': elif t == 'r':
return matchRealname (pattern,p,negate,n) return matchRealname (pattern,p,negate,n,extprefix)
elif t == 'x': elif t == 'x':
return matchGecos (pattern,p,negate,n) return matchGecos (pattern,p,negate,n,extprefix)
else: else:
log.error('%s unknown pattern' % pattern) log.error('%s pattern is not supported' % pattern)
p = pattern[(pattern.rfind(':')+1):]
return matchHostmask(p,n)
elif pattern.find(':') != -1: elif pattern.find(':') != -1:
p = pattern[(pattern.find(':')+1):] p = pattern[(pattern.rfind(':')+1):]
return matchHostmask(p,n) return matchHostmask(p,n)
else: else:
if ircutils.isUserHostmask(pattern): if ircutils.isUserHostmask(pattern):
return matchHostmask(pattern,n) return matchHostmask(pattern,n)
else: else:
if pattern.find('$'): if pattern.find(extprefix):
# channel forwards # channel forwards
pattern = pattern.split('$')[0] pattern = pattern.split(extprefix)[0]
if ircutils.isUserHostmask(pattern): if ircutils.isUserHostmask(pattern):
return matchHostmask(pattern,n) return matchHostmask(pattern,n)
else: else:
log.error('%s unknown pattern' % pattern) log.error('%s pattern is not supported' % pattern)
else: else:
log.error('%s unknown pattern' % pattern) log.error('%s pattern is not supported' % pattern)
return None return None
def getBestPattern (n): def getBestPattern (n,irc):
# return best pattern for a given Nick # return best pattern for a given Nick
results = [] results = []
if not n.prefix or not ircutils.isUserHostmask(n.prefix): if not n.prefix or not ircutils.isUserHostmask(n.prefix):
@ -217,10 +225,16 @@ def getBestPattern (n):
k = '*!%s@%s' % (ident,host) k = '*!%s@%s' % (ident,host)
if not k in results: if not k in results:
results.append(k) results.append(k)
extprefix = ''
extmodes = ''
if 'extban' in irc.state.supported:
ext = irc.state.supported['extban']
extprefix = ext.split(',')[0]
extmodes = ext.split(',')[1]
if n.account: if n.account:
results.append('$a:%s' % n.account) results.append('%sa:%s' % (extprefix,n.account))
if n.realname: if n.realname:
results.append('$r:%s' % n.realname.replace(' ','?')) results.append('%sr:%s' % (extprefix,n.realname.replace(' ','?')))
return results return results
def clearExtendedBanPattern (pattern): def clearExtendedBanPattern (pattern):
@ -367,7 +381,7 @@ class Ircd (object):
return [] return []
results = [] results = []
current = time.time() current = time.time()
results.append('[%s][%s] %s sets +%s %s' % (channel,floatToGMT(begin_at),oper,kind,mask)) results.append('[%s] [%s] %s sets +%s %s' % (channel,floatToGMT(begin_at),oper,kind,mask))
if not removed_at: if not removed_at:
if begin_at == end_at: if begin_at == end_at:
results.append('set forever') results.append('set forever')
@ -602,7 +616,7 @@ class Ircd (object):
elif prefix == irc.prefix and ct.registryValue('announceBotMark',channel=item.channel): elif prefix == irc.prefix and ct.registryValue('announceBotMark',channel=item.channel):
f = ct._logChan f = ct._logChan
if f: if f:
f(irc,channel,'[%s][#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message)) f(irc,channel,'[%s] [#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message))
b = True b = True
c.close() c.close()
return b return b
@ -625,7 +639,7 @@ class Ircd (object):
c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,message)) c.execute("""INSERT INTO comments VALUES (?, ?, ?, ?)""",(uid,prefix,current,message))
db.commit() db.commit()
if logFunction: if logFunction:
logFunction(irc,channel,'[%s][#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message)) logFunction(irc,channel,'[%s] [#%s +%s %s] marked by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],message))
b = True b = True
c.close() c.close()
return b return b
@ -724,7 +738,7 @@ class Ircd (object):
if scheduleFunction: if scheduleFunction:
scheduleFunction(irc,newEnd) scheduleFunction(irc,newEnd)
if logFunction: if logFunction:
logFunction(irc,channel,'[%s][#%s +%s %s] edited by %s: %s' % (channel,uid,kind,mask,prefix.split('!')[0],reason)) 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
@ -752,7 +766,7 @@ class Ircd (object):
if commits > 0: if commits > 0:
db.commit() db.commit()
if logFunction: if logFunction:
logFunction(irc,channel,'[%s][%s] %s removed: %s' % (channel,mode,commits, ' '.join(msgs))) logFunction(irc,channel,'[%s] [%s] %s removed: %s' % (channel,mode,commits, ' '.join(msgs)))
c.close() c.close()
class Chan (object): class Chan (object):
@ -833,13 +847,13 @@ class Chan (object):
L.append(nick) L.append(nick)
for nick in L: for nick in L:
n = self.ircd.getNick(self.ircd.irc,nick) n = self.ircd.getNick(self.ircd.irc,nick)
m = match(value,n) m = match(value,n,self.ircd.irc)
if m: if m:
i.affects.append(n.prefix) i.affects.append(n.prefix)
# insert logs # insert logs
index = 0 index = 0
logs = [] logs = []
logs.append('%s matched by %s' % (n,m)) logs.append('%s matched by %s' % (getBestPattern(n,self.ircd.irc),m))
for line in n.logs: for line in n.logs:
(ts,target,message) = n.logs[index] (ts,target,message) = n.logs[index]
index += 1 index += 1
@ -1057,7 +1071,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
else: else:
irc.error('item not found, already removed or not enough rights') irc.reply('item not found, already removed or not enough rights to modify it')
self.forceTickle = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
edit = wrap(edit,['user',commalist('int'),any('getTs')]) edit = wrap(edit,['user',commalist('int'),any('getTs')])
@ -1071,7 +1085,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.replies(results,None,None,False,None,True) irc.replies(results,None,None,False,None,True)
else: else:
irc.error('item not found or not enough rights') irc.reply('item not found or not enough rights to see information')
self._tickle(irc) self._tickle(irc)
info = wrap(info,['private','user','int']) info = wrap(info,['private','user','int'])
@ -1084,7 +1098,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.replies(results,None,None,False,None,True) irc.replies(results,None,None,False,None,True)
else: else:
irc.error('item not found or not enough rights') irc.reply('item not found or not enough rights to see detail')
self._tickle(irc) self._tickle(irc)
detail = wrap(detail,['private','user','int']) detail = wrap(detail,['private','user','int'])
@ -1097,7 +1111,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.replies(results,None,None,False,None,True) irc.replies(results,None,None,False,None,True)
else: else:
irc.error('item not found or not enough rights') irc.reply('item not found or not enough rights to see affected users')
self._tickle(irc) self._tickle(irc)
affect = wrap(affect, ['private','user','int']) affect = wrap(affect, ['private','user','int'])
@ -1122,7 +1136,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
else: else:
irc.error('item not found or not enough rights') irc.reply('item not found or not enough rights to mark it')
self.forceTickle = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
mark = wrap(mark,['user',commalist('int'),'text']) mark = wrap(mark,['user',commalist('int'),'text'])
@ -1136,7 +1150,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.replies(results,None,None,False,None,True) irc.replies(results,None,None,False,None,True)
else: else:
irc.error('nothing found') irc.reply('nothing found')
query = wrap(query,['private','user','text']) query = wrap(query,['private','user','text'])
def pending (self, irc, msg, args, channel, mode, pattern): def pending (self, irc, msg, args, channel, mode, pattern):
@ -1155,11 +1169,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.reply(' '.join(results), private=True) irc.reply(' '.join(results), private=True)
else: else:
irc.error('no results') irc.reply('no results')
pending = wrap(pending,['op',additional('letter'),optional('hostmask')]) pending = wrap(pending,['op',additional('letter'),optional('hostmask')])
def do (self,irc,msg,args,channel,mode,items,seconds,reason): def do (self,irc,msg,args,channel,mode,items,seconds,reason):
"""[<channel>] <mode> <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1s> or empty means forever] <reason> """[<channel>] <mode> <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>
+<mode> targets for duration <reason> is mandatory""" +<mode> targets for duration <reason> is mandatory"""
if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel): if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel):
@ -1168,14 +1182,14 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern, or pattern already active') irc.reply('unknown pattern or pattern already active')
else: else:
irc.error('selected mode is not supported by config') irc.reply('selected mode is not supported by config')
do = wrap(do,['op','letter',commalist('something'),any('getTs',True),rest('text')]) do = wrap(do,['op','letter',commalist('something'),any('getTs',True),rest('text')])
def q (self,irc,msg,args,channel,items,seconds,reason): def q (self,irc,msg,args,channel,items,seconds,reason):
"""[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1s> or empty means forever] <reason> """[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>
+q targets for duration reason is mandatory""" +q targets for duration reason is mandatory"""
b = self._adds(irc,msg,args,channel,'q',items,getDuration(seconds),reason) b = self._adds(irc,msg,args,channel,'q',items,getDuration(seconds),reason)
@ -1183,11 +1197,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern, or pattern already active') irc.reply('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):
"""[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1s> or empty means forever] <reason> """[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>
+b targets for duration reason is mandatory""" +b targets for duration reason is mandatory"""
b = self._adds(irc,msg,args,channel,'b',items,getDuration(seconds),reason) b = self._adds(irc,msg,args,channel,'b',items,getDuration(seconds),reason)
@ -1195,11 +1209,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern, or pattern already active') irc.reply('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, reason): def i (self, irc, msg, args, channel, items, seconds, reason):
"""[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1s> or empty means forever] <reason> """[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>
+I targets for duration reason is mandatory""" +I targets for duration reason is mandatory"""
b = self._adds(irc,msg,args,channel,'I',items,getDuration(seconds),reason) b = self._adds(irc,msg,args,channel,'I',items,getDuration(seconds),reason)
@ -1207,11 +1221,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern, or pattern already active') irc.reply('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):
"""[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1s> or empty means forever] <reason> """[<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>
+e targets for duration reason is mandatory""" +e targets for duration reason is mandatory"""
b = self._adds(irc,msg,args,channel,'e',items,getDuration(seconds),reason) b = self._adds(irc,msg,args,channel,'e',items,getDuration(seconds),reason)
@ -1219,7 +1233,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown pattern, or pattern already active') irc.reply('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):
@ -1231,7 +1245,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown patterns, already removed or unsupported mode') irc.reply('unknown patterns, already removed or unsupported mode')
undo = wrap(undo,['op','letter',many('something')]) undo = wrap(undo,['op','letter',many('something')])
def uq (self, irc, msg, args, channel, items): def uq (self, irc, msg, args, channel, items):
@ -1243,7 +1257,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown patterns, already removed or unsupported mode') irc.reply('unknown patterns, already removed or unsupported mode')
uq = wrap(uq,['op',many('something')]) uq = wrap(uq,['op',many('something')])
def ub (self, irc, msg, args, channel, items): def ub (self, irc, msg, args, channel, items):
@ -1255,7 +1269,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown patterns, already removed or unsupported mode') irc.reply('unknown patterns, already removed or unsupported mode')
ub = wrap(ub,['op',many('something')]) ub = wrap(ub,['op',many('something')])
def ui (self, irc, msg, args, channel, items): def ui (self, irc, msg, args, channel, items):
@ -1267,7 +1281,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown patterns, already removed or unsupported mode') irc.reply('unknown patterns, already removed or unsupported mode')
ui = wrap(ui,['op',many('something')]) ui = wrap(ui,['op',many('something')])
def ue (self, irc, msg, args, channel, items): def ue (self, irc, msg, args, channel, items):
@ -1279,7 +1293,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if b: if b:
irc.replySuccess() irc.replySuccess()
return return
irc.error('unknown patterns, already removed or unsupported mode') irc.reply('unknown patterns, already removed or unsupported mode')
ue = wrap(ue,['op',many('something')]) ue = wrap(ue,['op',many('something')])
def remove (self,irc,msg,args,channel,nick,reason): def remove (self,irc,msg,args,channel,nick,reason):
@ -1299,13 +1313,13 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
"""[<channel>] <pattern> """[<channel>] <pattern>
returns a list of affected users by a pattern""" returns a list of affected users by a pattern"""
if ircutils.isUserHostmask(pattern) or pattern.find('$') != -1 or pattern.find(':') != -1: if ircutils.isUserHostmask(pattern) or pattern.find(self.getIrcdExtbansPrefix(irc)) != -1:
results = [] results = []
i = self.getIrc(irc) i = self.getIrc(irc)
for nick in irc.state.channels[channel].users: for nick in irc.state.channels[channel].users:
if nick in i.nicks: if nick in i.nicks:
n = self.getNick(irc,nick) n = self.getNick(irc,nick)
m = match(pattern,n) m = match(pattern,n,irc)
if m: if m:
results.append('[%s - %s]' % (nick,m)) results.append('[%s - %s]' % (nick,m))
else: else:
@ -1313,7 +1327,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if len(results): if len(results):
irc.reply(' '.join(results)) irc.reply(' '.join(results))
else: else:
irc.error('nobody will be affected') irc.reply('nobody will be affected')
else: else:
irc.error('invalid pattern') irc.error('invalid pattern')
check = wrap (check,['op','text']) check = wrap (check,['op','text'])
@ -1324,7 +1338,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
returns a list of hostmask's pattern, best first, mostly used for debug""" returns a list of hostmask's pattern, best first, mostly used for debug"""
i = self.getIrc(irc) i = self.getIrc(irc)
if prefix in i.nicks: if prefix in i.nicks:
irc.reply(' '.join(getBestPattern(self.getNick(irc,prefix)))) irc.reply(' '.join(getBestPattern(self.getNick(irc,prefix),irc)))
else: else:
n = Nick(0) n = Nick(0)
if prefix.find('#') != -1: if prefix.find('#') != -1:
@ -1336,9 +1350,9 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
else: else:
n.setPrefix(prefix) n.setPrefix(prefix)
if ircutils.isUserHostmask(prefix): if ircutils.isUserHostmask(prefix):
irc.reply(' '.join(getBestPattern(n))) irc.reply(' '.join(getBestPattern(n,irc)))
return return
irc.error('nick not found or wrong hostmask given') irc.reply('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): def isvip (self,irc,msg,args,channel,nick):
@ -1349,7 +1363,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if nick in i.nicks: if nick in i.nicks:
irc.reply(self._isVip(irc,channel,self.getNick(irc,nick))) irc.reply(self._isVip(irc,channel,self.getNick(irc,nick)))
else: else:
irc.error('nick not found') irc.reply('nick not found')
isvip = wrap(isvip,['op','nick']) isvip = wrap(isvip,['op','nick'])
def isbad (self,irc,msg,args,channel,nick): def isbad (self,irc,msg,args,channel,nick):
@ -1359,21 +1373,53 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
i = self.getIrc(irc) i = self.getIrc(irc)
if nick in i.nicks: if nick in i.nicks:
chan = self.getChan(irc,channel) chan = self.getChan(irc,channel)
irc.reply(chan.isWrong(getBestPattern(self.getNick(irc,nick))[0])) irc.reply(chan.isWrong(getBestPattern(self.getNick(irc,nick),irc)[0]))
else: else:
irc.error('nick not found') irc.reply('nick not found')
isbad = wrap(isbad,['op','nick']) isbad = wrap(isbad,['op','nick'])
def supported (self,irc,msg,args):
"""return supported modes by ircd """
r = []
for item in irc.state.supported:
r.append('[%s: %s]' % (item,irc.state.supported[item]))
irc.reply(', '.join(r))
supported = wrap(supported,['owner'])
def getIrcdMode (self,irc,mode,pattern):
# here we try to know which kind of mode and pattern should be computed :
# based on supported modes and extbans on the ircd
if 'chanmodes' in irc.state.supported:
cm = irc.state.supported['chanmodes'].split(',')[0]
if not mode in cm:
if 'extban' in irc.state.supported:
extban = irc.state.supported['extban']
prefix = extbans.split(',')[0]
modes = extbans.split(',')[1]
if mode in modes:
old = mode
mode = 'b'
pattern = prefix + old + ':' + pattern
elif mode == 'q' and 'm' in modes:
# inspire ?
pattern = prefix + 'm' + ':' + pattern
return [mode,pattern]
def getIrcdExtbansPrefix (self,irc):
if 'extban' in irc.state.supported:
return irc.state.supported['extban'].split(',')[0]
return ''
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 = []
if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel): if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel):
for item in items: for item in items:
if ircutils.isUserHostmask(item) or item.find(':') != -1 or item.find('$') != -1: if ircutils.isUserHostmask(item) or item.find(self.getIrcdExtbansPrefix(irc)) != -1:
targets.append(item) targets.append(item)
elif channel in irc.state.channels and item in irc.state.channels[channel].users: elif item in i.nicks or item in irc.state.channels[channel].users:
n = self.getNick(irc,item) n = self.getNick(irc,item)
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
# when resync patterns may be empty, until the bot computed WHO # when resync patterns may be empty, until the bot computed WHO
if len(patterns): if len(patterns):
targets.append(patterns[0]) targets.append(patterns[0])
@ -1401,14 +1447,14 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
count = 0 count = 0
if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel): if mode in self.registryValue('modesToAsk',channel=channel) or mode in self.registryValue('modesToAskWhenOpped',channel=channel):
for item in items: for item in items:
if ircutils.isUserHostmask(item) or item.find(':') != -1 or item.find('$') != -1: if ircutils.isUserHostmask(item) or item.find(self.getIrcdExtbansPrefix(irc)) != -1:
targets.append(item) targets.append(item)
elif channel in irc.state.channels and item in irc.state.channels[channel].users: elif item in i.nicks or item in irc.state.channels[channel].users:
n = self.getNick(irc,item) n = self.getNick(irc,item)
L = chan.getItemsFor(mode) L = chan.getItemsFor(mode)
# here we check active items against Nick and add everything pattern which matchs him # here we check active items against Nick and add everything pattern which matchs him
for pattern in L: for pattern in L:
m = match(pattern,n) m = match(pattern,n,irc)
if m: if m:
targets.append(pattern) targets.append(pattern)
elif item == '*': elif item == '*':
@ -1448,23 +1494,27 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
i = self.getIrc(irc) i = self.getIrc(irc)
if not channel in i.channels: if not channel in i.channels:
# restore channel state, loads lists # restore channel state, loads lists
i.lowQueue.enqueue(ircmsgs.ping(channel))
modesToAsk = ''.join(self.registryValue('modesToAsk',channel=channel)) modesToAsk = ''.join(self.registryValue('modesToAsk',channel=channel))
modesWhenOpped = ''.join(self.registryValue('modesToAskWhenOpped',channel=channel)) modesWhenOpped = ''.join(self.registryValue('modesToAskWhenOpped',channel=channel))
if channel in irc.state.channels: if channel in irc.state.channels:
if irc.nick in irc.state.channels[channel].ops: if irc.nick in irc.state.channels[channel].ops:
if len(modesToAsk) or len(modesWhenOpped): if len(modesToAsk) or len(modesWhenOpped):
modes = modesToAsk+modesWhenOpped for m in modesWhenOpped:
i.queue.enqueue(ircmsgs.IrcMsg('MODE %s %s' % (channel,modes))) i.queue.enqueue(ircmsgs.IrcMsg('MODE %s %s' % (channel,m)))
for m in modesToAsk:
i.lowQueue.enqueue(ircmsgs.IrcMsg('MODE %s %s' % (channel,m)))
elif len(modesToAsk): elif len(modesToAsk):
i.lowQueue.enqueue(ircmsgs.IrcMsg('MODE %s %s' % (channel,modesToAsk))) for m in modesToAsk:
i.lowQueue.enqueue(ircmsgs.IrcMsg('MODE %s %s' % (channel,m)))
# schedule that for later # schedule that for later
# prevent the bot to disconnect itself is server takes too much time to answer # prevent the bot to disconnect itself is server takes too much time to answer
i.lowQueue.enqueue(ircmsgs.ping(channel)) i.lowQueue.enqueue(ircmsgs.ping(channel))
# loads extended who # loads extended who
i.lowQueue.enqueue(ircmsgs.IrcMsg('WHO ' + channel +' %tnuhiar,42')) # some ircd may not like this i.lowQueue.enqueue(ircmsgs.IrcMsg('WHO ' + channel +' %tnuhiar,42')) # some ircd may not like this
# fallback, TODO maybe uneeded as supybot do it by itself on join, but necessary on plugin reload ... # fallback, TODO maybe uneeded as supybot do it by itself on join, but necessary on plugin reload ...
i.lowQueue.enqueue(ircmsgs.ping(channel))
i.lowQueue.enqueue(ircmsgs.IrcMsg('WHO %s' % channel)) i.lowQueue.enqueue(ircmsgs.IrcMsg('WHO %s' % channel))
self.forceTickle = True
return i.getChan (irc,channel) return i.getChan (irc,channel)
def getNick (self,irc,nick): def getNick (self,irc,nick):
@ -1553,7 +1603,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
for value in list(chan._lists[mode].keys()): for value in list(chan._lists[mode].keys()):
item = chan._lists[mode][value] item = chan._lists[mode][value]
if item.expire != None and item.expire != item.when and not item.asked and item.expire <= t: if item.expire != None and item.expire != item.when and not item.asked and item.expire <= t:
if mode == 'q' and item.value.find('$') == -1 and self.registryValue('useChanServForQuiets',channel=channel) and not irc.nick in irc.state.channels[channel].ops: if mode == 'q' and item.value.find(self.getIrcdExtbansPrefix(irc)) == -1 and self.registryValue('useChanServForQuiets',channel=channel) and not irc.nick in irc.state.channels[channel].ops:
s = self.registryValue('unquietCommand') s = self.registryValue('unquietCommand')
s = s.replace('$channel',channel) s = s.replace('$channel',channel)
s = s.replace('$hostmask',item.value) s = s.replace('$hostmask',item.value)
@ -1793,7 +1843,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if 'LIST' in i.caps and 'extended-join' in i.caps['LIST'] and len(msg.args) == 3: if 'LIST' in i.caps and 'extended-join' in i.caps['LIST'] and len(msg.args) == 3:
n.setRealname(msg.args[2]) n.setRealname(msg.args[2])
n.setAccount(msg.args[1]) n.setAccount(msg.args[1])
best = getBestPattern(n)[0] best = getBestPattern(n,irc)[0]
if msg.nick == irc.nick: if msg.nick == irc.nick:
return return
for channel in channels: for channel in channels:
@ -1845,7 +1895,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
n = self.getNick(irc,msg.nick) n = self.getNick(irc,msg.nick)
n.setPrefix(msg.prefix) n.setPrefix(msg.prefix)
reason = '' reason = ''
best = getBestPattern(n)[0] best = getBestPattern(n,irc)[0]
if len(msg.args) == 2: if len(msg.args) == 2:
reason = msg.args[1].lstrip().rstrip() reason = msg.args[1].lstrip().rstrip()
canRemove = True canRemove = True
@ -1904,7 +1954,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
def _rmNick (self,irc,n): def _rmNick (self,irc,n):
def nrm(): def nrm():
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
i = self.getIrc(irc) i = self.getIrc(irc)
if not len(patterns): if not len(patterns):
return return
@ -1933,7 +1983,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
removeNick = True removeNick = True
if not isBot: if not isBot:
n = self.getNick(irc,msg.nick) n = self.getNick(irc,msg.nick)
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
if len(patterns): if len(patterns):
best = patterns[0] best = patterns[0]
if reason: if reason:
@ -1991,7 +2041,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
n = self.getNick(irc,newNick) n = self.getNick(irc,newNick)
n.addLog('ALL','%s is now known as %s' % (oldNick,newNick)) n.addLog('ALL','%s is now known as %s' % (oldNick,newNick))
best = None best = None
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
if len(patterns): if len(patterns):
best = patterns[0] best = patterns[0]
if not best: if not best:
@ -2072,7 +2122,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
for mode in modes: for mode in modes:
items = chan.getItemsFor(mode) items = chan.getItemsFor(mode)
for item in items: for item in items:
f = match(items[item].value,n) f = match(items[item].value,n,irc)
if f: if f:
found = items[item] found = items[item]
if found: if found:
@ -2083,11 +2133,11 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
duration = -1 duration = -1
if found.expire and found.expire != found.when: if found.expire and found.expire != found.when:
duration = int(found.expire-time.time()) duration = int(found.expire-time.time())
self._act (irc,channel,found.mode,getBestPattern(n)[0],duration,'evade of [#%s +%s %s]' % (found.uid,found.mode,found.value)) self._act (irc,channel,found.mode,getBestPattern(n,irc)[0],duration,'evade of [#%s +%s %s]' % (found.uid,found.mode,found.value))
f = None f = None
if self.registryValue('announceBotMark',channel=found.channel): if self.registryValue('announceBotMark',channel=found.channel):
f = self._logChan f = self._logChan
i.mark(irc,found.uid,'evade with %s --> %s' % (msg.prefix,getBestPattern(n)[0]),irc.prefix,self.getDb(irc.network),f) i.mark(irc,found.uid,'evade with %s --> %s' % (msg.prefix,getBestPattern(n,irc)[0]),irc.prefix,self.getDb(irc.network),f)
self.forceTickle = True self.forceTickle = True
self._tickle(irc) self._tickle(irc)
@ -2115,7 +2165,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if msg.nick == irc.nick: if msg.nick == irc.nick:
return return
n = self.getNick(irc,msg.nick) n = self.getNick(irc,msg.nick)
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
best = False best = False
if len(patterns): if len(patterns):
best = patterns[0] best = patterns[0]
@ -2192,7 +2242,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
items = chan.getItemsFor(ignore) items = chan.getItemsFor(ignore)
if items: if items:
for item in items: for item in items:
if match(item,n): if match(item,n,irc):
vip = True vip = True
break break
if vip: if vip:
@ -2216,7 +2266,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
patterns = None patterns = None
if ircutils.isUserHostmask(msg.prefix): if ircutils.isUserHostmask(msg.prefix):
n = self.getNick(irc,msg.nick) n = self.getNick(irc,msg.nick)
patterns = getBestPattern(n) patterns = getBestPattern(n,irc)
if len(patterns): if len(patterns):
best = patterns[0] best = patterns[0]
# if it fails here stacktrace # if it fails here stacktrace
@ -2326,7 +2376,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
message = None message = None
if 'm' in irc.state.channels[channel].modes: if '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: if not msg.nick in irc.state.channels[channel].voices and not msg.nick in irc.state.channels[channel].ops:
message = '[%s][+m] <%s> %s' % (channel,msg.prefix,text) message = '[%s] [+m] <%s> %s' % (channel,msg.prefix,text)
if not message: if not message:
if not msg.nick in irc.state.channels[channel].voices and not msg.nick in irc.state.channels[channel].ops: if not msg.nick in irc.state.channels[channel].voices and not msg.nick in irc.state.channels[channel].ops:
modes = self.registryValue('modesToAsk',channel=channel) modes = self.registryValue('modesToAsk',channel=channel)
@ -2334,7 +2384,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
for mode in modes: for mode in modes:
items = chan.getItemsFor(mode) items = chan.getItemsFor(mode)
for item in items: for item in items:
f = match(items[item].value,n) f = match(items[item].value,n,irc)
if f: if f:
found = [items[item],f] found = [items[item],f]
if found: if found:
@ -2342,7 +2392,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if found: if found:
break break
if found: if found:
message = '[%s][#%s +%s %s] <%s> %s' % (channel,found[0].uid,found[0].mode,found[0].value,msg.nick,text) message = '[%s] [#%s +%s %s] <%s> %s' % (channel,found[0].uid,found[0].mode,found[0].value,msg.nick,text)
if message: if message:
self._logChan(irc,channel,message) self._logChan(irc,channel,message)
@ -2416,7 +2466,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
if m in self.registryValue('modesToAskWhenOpped',channel=channel) or m in self.registryValue('modesToAsk',channel=channel): if m in self.registryValue('modesToAskWhenOpped',channel=channel) or m in self.registryValue('modesToAsk',channel=channel):
item = chan.addItem(m,value,msg.prefix,now,self.getDb(irc.network)) item = chan.addItem(m,value,msg.prefix,now,self.getDb(irc.network))
if msg.nick != irc.nick and self.registryValue('askOpAboutMode',channel=channel) and ircdb.checkCapability(msg.prefix, '%s,op' % channel): if msg.nick != irc.nick and self.registryValue('askOpAboutMode',channel=channel) and ircdb.checkCapability(msg.prefix, '%s,op' % channel):
irc.queueMsg(ircmsgs.privmsg(msg.nick,'Could you edit or mark [#%s +%s %s in %s] ?' % (item.uid,m,value,channel))) i.lowQueue.enqueue(ircmsgs.privmsg(msg.nick,'Could you edit or mark [#%s +%s %s in %s] ?' % (item.uid,m,value,channel)))
if overexpire > 0: if overexpire > 0:
# overwrite expires # overwrite expires
if msg.nick != irc.nick: if msg.nick != irc.nick:
@ -2431,7 +2481,7 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
for affected in item.affects: for affected in item.affects:
nick = affected.split('!')[0] nick = affected.split('!')[0]
kicked = False kicked = False
if m in self.registryValue('kickMode',channel=channel): if m in self.registryValue('kickMode',channel=channel): # and not value.startswith(self.getIrcdExtbans(irc)) works for unreal
if nick in irc.state.channels[channel].users and nick != irc.nick: if nick in irc.state.channels[channel].users and nick != irc.nick:
chan.action.enqueue(ircmsgs.kick(channel,nick,self.registryValue('kickMessage'))) chan.action.enqueue(ircmsgs.kick(channel,nick,self.registryValue('kickMessage')))
self.forceTickle = True self.forceTickle = True
@ -2440,6 +2490,8 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
acts = [] acts = []
if nick in irc.state.channels[channel].ops and not nick == irc.nick: if nick in irc.state.channels[channel].ops and not nick == irc.nick:
acts.append(('-o',nick)) acts.append(('-o',nick))
if nick in irc.state.channels[channel].halfops and not nick == irc.nick:
acts.append(('-h',nick))
if nick in irc.state.channels[channel].voices and not nick == irc.nick: if nick in irc.state.channels[channel].voices and not nick == irc.nick:
acts.append(('-v',nick)) acts.append(('-v',nick))
if len(acts): if len(acts):
@ -2473,14 +2525,20 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
n.addLog(channel,'sets %s %s' % (mode,value)) n.addLog(channel,'sets %s %s' % (mode,value))
if item: if item:
if '+' in mode: if '+' in mode:
if len(item.affects) != 1: if not len(item.affects):
if self.registryValue('announceMode',channel=channel):
msgs.append('[#%s %s %s]' % (str(item.uid),mode,value))
elif len(item.affects) != 1:
if self.registryValue('announceMode',channel=channel): if self.registryValue('announceMode',channel=channel):
msgs.append('[#%s %s %s - %s users]' % (str(item.uid),mode,value,str(len(item.affects)))) msgs.append('[#%s %s %s - %s users]' % (str(item.uid),mode,value,str(len(item.affects))))
else: else:
if self.registryValue('announceMode',channel=channel): if self.registryValue('announceMode',channel=channel):
msgs.append('[#%s %s %s - %s]' % (str(item.uid),mode,value,item.affects[0])) msgs.append('[#%s %s %s - %s]' % (str(item.uid),mode,value,item.affects[0]))
else: else:
if len(item.affects) != 1: if not len(item.affects):
if self.registryValue('announceMode',channel=channel):
msgs.append('[#%s %s %s %s]' % (str(item.uid),mode,value,str(utils.timeElapsed(item.removed_at-item.when))))
elif len(item.affects) != 1:
if self.registryValue('announceMode',channel=channel): if self.registryValue('announceMode',channel=channel):
msgs.append('[#%s %s %s - %s users, %s]' % (str(item.uid),mode,value,str(len(item.affects)),str(utils.timeElapsed(item.removed_at-item.when)))) msgs.append('[#%s %s %s - %s users, %s]' % (str(item.uid),mode,value,str(len(item.affects)),str(utils.timeElapsed(item.removed_at-item.when))))
else: else:
@ -2645,9 +2703,6 @@ class ChanTracker(callbacks.Plugin,plugins.ChannelDBHandler):
self._ircs = ircutils.IrcDict() self._ircs = ircutils.IrcDict()
def doError (self,irc,msg): def doError (self,irc,msg):
if irc and irc.network in self._ircs:
del self._ircs[irc]
else:
self._ircs = ircutils.IrcDict() self._ircs = ircutils.IrcDict()