###
# Copyright (c) 2002-2005, Jeremiah Fincher
# Copyright (c) 2009, James Vega
# Copyright (c) 2010, Nicolas Coevoet
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import os
import time
import string
import re
import sys
import operator
import supybot.irclib as irclib
import supybot.log as log
import supybot.conf as conf
import supybot.ircdb as ircdb
import supybot.utils as utils
from supybot.commands import *
import supybot.ircmsgs as ircmsgs
import supybot.schedule as schedule
import supybot.callbacks as callbacks
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.commands as commands
import socket
import random
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this plugin. \
Download it at <http://pysqlite.org/>'
class SpamQueue(object):
timeout = 0
def __init__(self, timeout=None, queues=None):
if timeout is not None:
self.timeout = timeout
if queues is None:
queues = ircutils.IrcDict()
self.queues = queues
def __repr__(self):
return 'SpamQueue(timeout=%r,queues=%s)' % (self.timeout,repr(self.queues))
def reset (self,data):
q = self._getQueue(data,insert=False)
if q is not None:
q.reset()
key = self.key(data)
self.queues[key] = q
def key (self,data):
return data[0]
def getTimeout(self):
if callable(self.timeout):
return self.timeout()
else:
return self.timeout
def _getQueue(self,data,insert=True):
try:
return self.queues[self.key(data)]
except KeyError:
if insert:
getTimeout = lambda : self.getTimeout()
q = utils.structures.TimeoutQueue(getTimeout)
self.queues[self.key(data)] = q
return q
else:
return None
def enqueue(self,data,what=None):
if what is None:
what = data
q = self._getQueue(data)
q.enqueue(what)
def len (self,data):
q = self._getQueue(data,insert=False)
if q is not None:
return len(q)
else:
return 0
def has (self,data,what):
q = self._getQueue(data,insert=False)
if q is not None:
if what is None:
what = data
for elt in q:
if elt == what:
return True
return False
class Chan (object):
def __init__(self):
object.__init__(self)
self.nicks = {}
self.logChannel = None
self.logSize = 20
self.opChannel = None
self.evadeBanCheck = False
self.evadeKickMessage = ''
self.evadeBanDuration = -1
self.synchro = False
self.schedules = {}
self.activeBans = {}
self.pendingBans = {}
self.activeQuiets = {}
self.pendingQuiets = {}
self.removed = {}
self.floodCheck = False
self.floodPermit = -1
self.floodLife = -1
self.floodQuietDuration = -1
self.floodQueue = None
self.lowFloodCheck = False
self.lowFloodPermit = -1
self.lowFloodLife = -1
self.lowFloodQuietDuration = -1
self.lowFloodQueue = None
self.floodMessage = None
self.repeatCheck = False
self.repeatQueue = None
self.repeatPermit = -1
self.repeatLife = -1
self.repeatQuietDuration = -1
self.repeatMessage = None
self.highlightCheck = False
self.highlightPermit = -1
self.highlightMessage = None
self.highlightQuietDuration = -1
self.noticeCheck = False
self.noticeQueue = None
self.noticePermit = -1
self.noticeLife = -1
self.noticeQuietDuration = -1
self.noticeMessage = None
self.badUserQueue = None
self.badUserLife = -1
self.badUserPermit = -1
self.badUserMessage = None
self.badUserBanDuration = -1
self.massjoinCheck = False
self.massjoinQueue = None
self.massjoinLife = -1
self.massjoinPermit = -1
self.massjoinMode = None
self.massjoinUnMode = None
self.massjoinDuration = -1
self.cycleCheck = False
self.cycleQueue = None
self.cyclePermit = -1
self.cycleLife = -1
self.cycleBanDuration = -1
self.attacks = SpamQueue(60)
self.netsplit = False
self.regexps = []
self.warnLife = -1
self.warnSchedule = None
self.commandCheck = False
self.commandPermit = -1
self.commandLife = -1
self.commandQueue = None
self.commandDisableDuration = -1
class Nick (object):
def __init__(self):
object.__init__(self)
self.nick = None
self.host = None
self.mask = None
self.logs = utils.structures.smallqueue()
self.warns = 0
def getmask (irc,nickormask):
if ircutils.isUserHostmask(nickormask):
hostmask = nickormask
return hostmask
else:
try:
hostmask = irc.state.nickToHostmask(nickormask)
except:
return None
return ircdb.getmask(hostmask)
def isgatewayweb(s):
return s.find('gateway/web') != -1
_iptohexa = {}
def iptohexa(s):
if s in _iptohexa:
return _iptohexa[s]
try:
_iptohexa[s] = ''.join(["%02X" % long(i) for i in s.split('.')])
return _iptohexa[s]
except:
return None
def splitmessage(s,n):
l = []
for i in range(0, len(s), n):
l.append(s[i:i+n])
return l
def getduration (text):
duration = -1
if len(text) > 1:
if text.isdigit():
try:
duration = int(text)
except:
duration = -1
else:
multi = -1
a = []
if text.find('s') != -1:
multi = 1
a = text.split('s')
elif text.find('m') != -1:
a = text.split('m')
multi = 60
elif text.find('h') != -1:
a = text.split('h')
multi = 3600
elif text.find('d') != -1:
a = text.split('d')
multi = 86400
elif text.find('w') != -1:
a = text.split('w')
multi = 604800
elif text.find('M') != -1:
a = text.split('M')
multi = 2419200
elif text.find('Y') != -1:
a = text.split('Y')
multi = 31536000
if len(a) > 1:
if a[0].isdigit():
try:
duration = int(a[0])*multi
except:
duration = multi
else:
if text.isdigit():
try:
duration = int(text)
except:
duration = -1
return duration
class Channel(callbacks.Plugin,plugins.ChannelDBHandler):
threaded = True
noIgnore = True
def __init__(self, irc):
self.__parent = super(Channel, self)
self.__parent.__init__(irc)
self.ircs = {}
self.dbCache = {}
self.invites = {}
self.rerepeat = re.compile(r'(.)\1{15}')
def ops (self,irc,msg,args,channel,text):
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
if c.opChannel in irc.state.channels:
if not text:
text = ''
users = ', '.join(irc.state.channels[c.opChannel].users)
irc.queueMsg(ircmsgs.privmsg(c.opChannel,'<%s|!OPS|%s> %s %s' % (msg.nick, channel, text, users)))
if msg.nick != irc.nick:
irc.replySuccess()
ops = wrap(ops, ['channel',additional('text')])
def notice(self, irc, msg, args, target, text):
if ircutils.nickEqual(target,irc.nick):
return
if target not in irc.state.nicksToHostmasks and not ircdb.checkCapability(msg.prefix, 'owner'):
return
irc.reply(text, to=target, private=True, notice=True)
notice = wrap(notice, ['owner','something', 'text'])
def private(self, irc, msg, args, target, text):
if target.lower() == 'me':
target = msg.nick
if ircutils.isChannel(target):
irc.queueMsg(ircmsgs.privmsg(target,text))
return
if not ircutils.isNick(target):
return
if ircutils.nickEqual(target, irc.nick):
return
if target not in irc.state.nicksToHostmasks and not ircdb.checkCapability(msg.prefix, 'owner'):
return
irc.reply(text, to=target, private=True, notice=False)
private = wrap(private, ['owner','something', 'text'])
def _sendMsg(self, irc, msg):
irc.sendMsg(msg)
irc.noReply()
def _sendMsgs(self, irc, nicks, f):
numModes = irc.state.supported.get('modes', 1)
for i in range(0, len(nicks), numModes):
irc.sendMsg(f(nicks[i:i + numModes]))
irc.noReply()
def mode(self, irc, msg, args, channel, modes):
"""[<channel>] <mode> [<arg> ...]
Sets the mode in <channel> to <mode>, sending the arguments given.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
self._sendMsg(irc, ircmsgs.IrcMsg('MODE %s %s' % (channel, modes)))
mode = wrap(mode, ['op', ('haveOp', 'change the mode'), 'text'])
def limit(self, irc, msg, args, channel, limit):
"""[<channel>] [<limit>]
Sets the channel limit to <limit>. If <limit> is 0, or isn't given,
removes the channel limit. <channel> is only necessary if the message
isn't sent in the channel itself.
"""
if limit:
self._sendMsg(irc, ircmsgs.mode(channel, ['+l', limit]))
else:
self._sendMsg(irc, ircmsgs.mode(channel, ['-l']))
limit = wrap(limit, ['op', ('haveOp', 'change the limit'),
additional('nonNegativeInt', 0)])
def key(self, irc, msg, args, channel, key):
"""[<channel>] [<key>]
Sets the keyword in <channel> to <key>. If <key> is not given, removes
the keyword requirement to join <channel>. <channel> is only necessary
if the message isn't sent in the channel itself.
"""
networkGroup = conf.supybot.networks.get(irc.network)
networkGroup.channels.key.get(channel).setValue(key)
if key:
self._sendMsg(irc, ircmsgs.mode(channel, ['+k', key]))
else:
self._sendMsg(irc, ircmsgs.mode(channel, ['-k']))
key = wrap(key, ['op', ('haveOp', 'change the keyword'),
additional('somethingWithoutSpaces', '')])
def op(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will give all the <nick>s
you provide ops. If you don't provide any <nick>s, this will op you.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
if not channel in irc.state.channels:
irc.error("i'm not in %s" % channel)
return
else:
if not irc.state.channels[channel].synchro:
irc.error("i'm not synchronized in %s" % channel)
return
if not nicks:
nicks = [msg.nick]
def f(L):
return ircmsgs.ops(channel, L)
self._sendMsgs(irc, nicks, f)
op = wrap(op, ['op', ('haveOp', 'op someone'), any('nickInChannel')])
def deop(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will remove operator
privileges from all the nicks given. If no nicks are given, removes
operator privileges from the person sending the message.
"""
if not channel in irc.state.channels:
irc.error("i'm not in %s" % channel)
return
else:
if not irc.state.channels[channel].synchro:
irc.error("i'm not synchronized in %s" % channel)
return
if irc.nick in nicks:
irc.error('I cowardly refuse to deop myself. If you really want '
'me deopped, tell me to op you and then deop me '
'yourself.', Raise=True)
if not nicks:
nicks = [msg.nick]
def f(L):
return ircmsgs.deops(channel, L)
self._sendMsgs(irc, nicks, f)
deop = wrap(deop, ['op', ('haveOp', 'deop someone'),
any('nickInChannel')])
def voice(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,voice capability, this will voice all the
<nick>s you provide. If you don't provide any <nick>s, this will
voice you. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
if nicks:
if len(nicks) == 1 and msg.nick in nicks:
capability = 'voice'
else:
capability = 'op'
else:
nicks = [msg.nick]
capability = 'voice'
capability = ircdb.makeChannelCapability(channel, capability)
if ircdb.checkCapability(msg.prefix, capability):
def f(L):
return ircmsgs.voices(channel, L)
a = []
d = {}
for nick in nicks:
if not nick in d and not nick in irc.state.channels[channel].voices:
d[nick] = nick
a.append(nick)
self._sendMsgs(irc, a, f)
else:
irc.errorNoCapability(capability)
voice = wrap(voice, ['channel', ('haveOp', 'voice someone'),
any('nickInChannel')])
def devoice(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will remove voice from all
the nicks given. If no nicks are given, removes voice from the person
sending the message.
"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
if irc.nick in nicks:
irc.error('I cowardly refuse to devoice myself. If you really '
'want me devoiced, tell me to op you and then devoice '
'me yourself.', Raise=True)
if not nicks:
nicks = [msg.nick]
def f(L):
return ircmsgs.devoices(channel, L)
a = []
d = {}
for nick in nicks:
if not nick in d and nick in irc.state.channels[channel].voices:
d[nick] = nick
a.append(nick)
self._sendMsgs(irc, nicks, f)
devoice = wrap(devoice, ['channel','voice', ('haveOp', 'devoice someone'),
any('nickInChannel')])
def cycle(self, irc, msg, args, channel):
"""[<channel>]
If you have the #channel,op capability, this will cause the bot to
"cycle", or PART and then JOIN the channel. <channel> is only necessary
if the message isn't sent in the channel itself.
"""
self._delChan(irc,channel)
self._sendMsg(irc, ircmsgs.part(channel, msg.nick))
networkGroup = conf.supybot.networks.get(irc.network)
self._sendMsg(irc, networkGroup.channels.join(channel))
cycle = wrap(cycle, ['op'])
def fpart(self, irc, msg, args, channel, nick, text):
"""[<channel>] <nick> [<reason>]"""
if not text:
text = msg.nick
else:
text = '%s - %s' % (text,msg.nick)
irc.sendMsg(ircmsgs.IrcMsg('remove %s %s :%s' % (channel,nick,text)))
fpart = wrap(fpart, ['channel','op', ('haveOp'),'nickInChannel',additional('text')])
def kick(self, irc, msg, args, channel, nick, reason):
"""[<channel>] <nick> [<reason>]
Kicks <nick> from <channel> for <reason>. If <reason> isn't given,
uses the nick of the person making the command as the reason.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
if ircutils.strEqual(nick, irc.nick):
irc.error('I cowardly refuse to kick myself.', Raise=True)
if not reason:
reason = msg.nick
else:
reason = '%s - %s' % (reason,msg.nick)
kicklen = irc.state.supported.get('kicklen', sys.maxint)
if len(reason) > kicklen:
irc.error('The reason you gave is longer than the allowed '
'length for a KICK reason on this server.',
Raise=True)
irc.sendMsg(ircmsgs.kick(channel, nick, reason))
kick = wrap(kick, ['op', ('haveOp', 'kick someone'),
'nickInChannel', additional('text')])
def bans(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will ban all the
<nick>s you provide. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
if not nicks:
return
def f(L):
return ircmsgs.bans(channel, L)
a = []
d = {}
c = self._getChan(irc,channel)
now = time.time()
for nick in nicks:
m = getmask(irc,nick)
if not m:
continue
if not m in d and not m in irc.state.channels[channel].bans:
d[m] = m
id = self._addban(irc,channel,msg.prefix,'b',m,0)
self._addbanaffects(irc,channel,id,'b',m)
c.activeBans[str(m)] = (id,msg.prefix,'b',m,now,now)
a.append(m)
self._sendMsgs(irc, a, f)
bans = wrap(bans, ['op', ('haveOp', 'ban someone'),
any('nickInChannel')])
def quiets(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will quiet all the
<nick>s you provide. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
if not nicks or not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
def f(L):
return ircmsgs.quiets(channel, L)
a = []
d = {}
c = self._getChan(irc,channel)
now = time.time()
for nick in nicks:
m = getmask(irc,nick)
if not m:
continue
if not m in d and not m in irc.state.channels[channel].quiets:
d[m] = m
id = self._addban(irc,channel,msg.prefix,'q',m,0)
self._addbanaffects(irc,channel,id,'q',m)
c.activeQuiets[str(m)] = (id,msg.prefix,'q',m,now,now)
a.append(m)
self._sendMsgs(irc, a, f)
quiets = wrap(quiets, ['op', ('haveOp', 'quiet someone'),
any('nickInChannel')])
def unbans(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will unban all the
<nick>s you provide. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
if not nicks or not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
def f(L):
return ircmsgs.unbans(channel, L)
a = []
d = {}
for nick in nicks:
try:
hostmask = irc.state.nickToHostmask(nick)
except:
continue
mask = getmask(irc,nick)
if not mask:
continue
for ban in irc.state.channels[channel].bans:
if not ban in d:
if ircutils.hostmaskPatternEqual(ban,mask) or ircutils.hostmaskPatternEqual(ban,hostmask):
d[ban] = ban
a.append(ban)
if ban in c.activeBans:
(id,by,b,m,at,end) = c.activeBans[ban]
self._markendban(irc,channel,msg.prefix,id)
if len(a):
self._sendMsgs(irc, a, f)
unbans = wrap(unbans, ['op', ('haveOp', 'unban someone'),
any('nickInChannel')])
def unquiets(self, irc, msg, args, channel, nicks):
"""[<channel>] [<nick> ...]
If you have the #channel,op capability, this will unquiet all the
<nick>s you provide. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
if not nicks or not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
def f(L):
return ircmsgs.unquiets(channel, L)
a = []
d = {}
for nick in nicks:
try:
hostmask = irc.state.nickToHostmask(nick)
except:
continue
mask = getmask(irc,nick)
if not mask:
continue
for quiet in irc.state.channels[channel].quiets:
if not quiet in d:
if ircutils.hostmaskPatternEqual(quiet,mask) or ircutils.hostmaskPatternEqual(quiet,hostmask):
d[quiet] = quiet
if quiet in c.activeQuiets:
(id,by,q,m,at,end) = c.activeQuiets[quiet]
self._markendban(irc,channel,msg.prefix,id)
a.append(quiet)
if len(a):
self._sendMsgs(irc, a, f)
unquiets = wrap(unquiets, ['op', ('haveOp', 'unquiet someone'),
any('nickInChannel')])
def kban(self, irc, msg, args, channel, text):
"""[<channel>] <nick|hostmask> [<duration>s,m,h,d,w,M,Y 0 means forever] [<reason>]"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
now = time.time()
bannedNick = text[0].lstrip().rstrip()
mask = getmask(irc,bannedNick)
if not mask:
return
if mask in irc.state.channels[channel].bans:
return
reason = msg.nick
duration = 0
if len(text) > 1:
a = text[1]
if len(text[1]) > 0:
duration = getduration(text[1])
if duration < 0:
reason = '%s - %s' % (' '.join(text),reason)
duration = 0
else:
t = ' '.join(text)
t = t.replace(text[0]+' '+text[1],'').lstrip()
reason = '%s - %s' % (t,reason)
else:
duration = 0
t = ' '.join(text)
t = t.replace(text[0],'').lstrip()
id = self._addban(irc,channel,msg.prefix,'b',mask,duration)
self._addbanaffects(irc,channel,id,'b',mask)
if duration:
ban = (id,msg.prefix,'b',mask,now,now+duration)
name = self._scheduleun(irc,channel,'b',mask,now+duration)
c.activeBans[mask] = c.schedules[name] = ban
c.pendingBans[mask] = name
else:
c.activeBans[mask] = (id,msg.prefix,'b',mask,now,now)
if reason != msg.nick:
self._banmark(irc,id,msg.prefix,reason)
irc.sendMsg(ircmsgs.ban(channel,mask))
if bannedNick in irc.state.channels[channel].users:
irc.sendMsg(ircmsgs.IrcMsg('remove %s %s :%s' % (channel,bannedNick,reason)))
if reason == msg.nick and not msg.nick == irc.nick:
try:
user = ircdb.users.getUser(msg.prefix)
except KeyError:
user = None
if user:
irc.queueMsg(ircmsgs.privmsg(msg.nick,"About [#%s +b %s in %s] you can use !banmark %s or !banedit %s" % (id,mask,channel,id,id)))
kban = wrap(kban,
['op',
('haveOp'),
many('something')])
def forward(self, irc, msg, args, channel, text):
"""[<channel>] <nick|hostmask> [<duration>s,m,h,d,w,M,Y 0 means forever] <#channel>]"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
now = time.time()
bannedNick = text[0].lstrip().rstrip()
mask = getmask(irc,bannedNick)
if not mask:
return
if mask in irc.state.channels[channel].bans:
return
reason = msg.nick
duration = 0
if len(text) > 1:
a = text[1]
if len(text[1]) > 0:
duration = getduration(text[1])
if duration < 0:
reason = join(text)
duration = 0
else:
t = ' '.join(text)
t = t.replace(text[0]+' '+text[1],'').lstrip()
reason = t
else:
duration = 0
t = ' '.join(text)
t = t.replace(text[0],'').lstrip()
mask = '%s$%s' % (mask,reason)
id = self._addban(irc,channel,msg.prefix,'b',mask,duration)
self._addbanaffects(irc,channel,id,'b',mask)
if duration:
ban = (id,msg.prefix,'b',mask,now,now+duration)
name = self._scheduleun(irc,channel,'b',mask,now+duration)
c.activeBans[mask] = c.schedules[name] = ban
c.pendingBans[mask] = name
else:
c.activeBans[mask] = (id,msg.prefix,'b',mask,now,now)
if reason != msg.nick:
self._banmark(irc,id,msg.prefix,reason)
irc.sendMsg(ircmsgs.ban(channel,mask))
if bannedNick in irc.state.channels[channel].users:
irc.sendMsg(ircmsgs.IrcMsg('remove %s %s :%s' % (channel,bannedNick,msg.nick)))
try:
user = ircdb.users.getUser(msg.prefix)
except KeyError:
user = None
if user and not msg.nick == irc.nick:
irc.queueMsg(ircmsgs.privmsg(msg.nick,"About [#%s +b %s in %s] you can use !banmark %s or !banedit %s" % (id,mask,channel,id,id)))
forward = wrap(forward,
['op',
('haveOp'),
many('something')])
def quiet(self, irc, msg, args, channel, text):
"""[<channel>] <nick|hostmask> [<duration>s,m,h,d,w,M,Y 0 means forever] [<reason>]"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
now = time.time()
bannedNick = text[0].lstrip().rstrip()
mask = getmask(irc,bannedNick)
if not mask:
return
if mask in irc.state.channels[channel].quiets:
return
reason = msg.nick
modes = []
if bannedNick in irc.state.channels[channel].users:
if bannedNick in irc.state.channels[channel].voices:
modes.append(('-v',bannedNick))
if bannedNick in irc.state.channels[channel].ops:
modes.append(('-o',bannedNick))
self.log.info('%s : %s' % (bannedNick,bannedNick in irc.state.channels[channel].voices))
duration = 0
if len(text) > 1:
a = text[1]
if len(text[1]) > 0:
duration = getduration(text[1])
if duration < 0:
reason = '%s - %s' % (' '.join(text),reason)
duration = 0
else:
t = ' '.join(text)
t = t.replace(text[0]+' '+text[1],'').lstrip()
reason = '%s - %s' % (t,reason)
else:
duration = 0
t = ' '.join(text)
t = t.replace(text[0],'').lstrip()
id = self._addban(irc,channel,msg.prefix,'q',mask,duration)
self._addbanaffects(irc,channel,id,'q',mask)
if duration:
ban = (id,msg.prefix,'q',mask,now,now+duration)
name = self._scheduleun(irc,channel,'q',mask,now+duration)
c.activeQuiets[mask] = c.schedules[name] = ban
c.pendingQuiets[mask] = name
else:
c.activeQuiets[mask] = (id,msg.prefix,'q',mask,now,now)
if reason != msg.nick:
self._banmark(irc,id,msg.prefix,reason)
if len(modes) != 0:
def f(L):
return ircmsgs.modes(channel, L)
modes.append(('+q',mask))
self._sendMsgs(irc, modes, f)
else:
if irc.nick in irc.state.channels[channel].ops:
irc.sendMsg(ircmsgs.quiet(channel,mask))
else:
irc.sendMsg(ircmsgs.IrcMsg('CS QUIET %s %s' % (channel,mask)))
if reason != msg.nick and bannedNick in irc.state.channels[channel].users:
if duration > 0:
irc.queueMsg(ircmsgs.privmsg(bannedNick,'%s - %s' % (reason,utils.timeElapsed(duration))))
else:
irc.queueMsg(ircmsgs.privmsg(bannedNick,reason))
if reason == msg.nick and not msg.nick == irc.nick:
try:
user = ircdb.users.getUser(msg.prefix)
except KeyError:
user = None
if user:
irc.queueMsg(ircmsgs.privmsg(msg.nick,"About [#%s +q %s in %s] you can use !banmark %s or !banedit %s" % (id,mask,channel,id,id)))
quiet = wrap(quiet,
['op',
many('something')])
def _banmark (self,irc,id,nick,text):
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT id FROM bans WHERE id=%s""",int(id))
except:
return
if c.rowcount:
c = db.cursor()
try:
c.execute("""INSERT INTO comments VALUES (%s, %s, %s, %s)""",
(id,nick,time.time(),text))
db.commit()
except:
return
def unban(self, irc, msg, args, channel, text):
"""[<channel>] [<nick|hostmask|banid>]"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
if text in irc.state.channels[channel].users:
bans = []
try:
hostmask = irc.state.nickToHostmask(text)
except:
return
mask = getmask(irc,text)
if not mask:
return
for ban in irc.state.channels[channel].bans:
if ircutils.hostmaskPatternEqual(ban,mask) or ircutils.hostmaskPatternEqual(ban,hostmask):
bans.append(ban)
if ban in c.activeBans:
(id,by,k,mask,at,end) = c.activeBans[ban]
self._markendban(irc,channel,msg.prefix,id)
if len(bans):
def f(L):
return ircmsgs.unbans(channel, L)
self._sendMsgs(irc, bans, f)
elif ircutils.isUserHostmask(text):
bans = []
for ban in irc.state.channels[channel].bans:
if ircutils.hostmaskPatternEqual(ban,text):
bans.append(ban)
if ban in c.activeBans:
(id,by,k,mask,at,end) = c.activeBans[ban]
self._markendban(irc,channel,msg.prefix,id)
if len(bans):
def f(L):
return ircmsgs.unbans(channel, L)
self._sendMsgs(irc, bans, f)
elif text.isdigit():
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=%s""",(text))
except:
return
if c.rowcount:
bans = c.fetchall()
(channel,by,kind,mask,begin_at,end_at,removed_at,removed_by) = bans[0]
if kind == 'b':
if mask in irc.state.channels[channel].bans and not removed_at:
self._markendban(irc,channel,msg.prefix,text)
irc.queueMsg(ircmsgs.unban(channel,mask))
unban = wrap(unban, ['op',
('haveOp', 'unban someone'),
'text'])
def unquiet(self, irc, msg, args, channel, text):
"""[<channel>] [<nick|hostmask|banid>]"""
if not channel in irc.state.channels:
return
else:
if not irc.state.channels[channel].synchro:
return
c = self._getChan(irc,channel)
text = text.lstrip()
if text in irc.state.channels[channel].users:
bans = []
try:
hostmask = irc.state.nickToHostmask(text)
except:
return
mask = getmask(irc,text)
if not mask:
return
for ban in irc.state.channels[channel].quiets:
if ircutils.hostmaskPatternEqual(ban,mask) or ircutils.hostmaskPatternEqual(ban,hostmask):
bans.append(ban)
if ban in c.activeQuiets:
(id,by,k,mask,at,end) = c.activeQuiets[ban]
self._markendban(irc,channel,msg.prefix,id)
if len(bans) > 1:
def f(L):
return ircmsgs.unquiets(channel, L)
self._sendMsgs(irc, bans, f)
else:
if irc.nick in irc.state.channels[channel].ops:
irc.queueMsg(ircmsgs.unquiet(channel,mask))
else:
irc.queueMsg(ircmsgs.IrcMsg('CS UNQUIET %s %s' % (channel,mask)))
elif ircutils.isUserHostmask(text):
bans = []
for ban in irc.state.channels[channel].quiets:
if ircutils.hostmaskPatternEqual(ban,text):
bans.append(ban)
if ban in c.activeQuiets:
(id,by,k,mask,at,end) = c.activeQuiets[ban]
self._markendban(irc,channel,msg.prefix,id)
if len(bans):
def f(L):
return ircmsgs.unquiets(channel, L)
self._sendMsgs(irc, bans, f)
elif text.isdigit():
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=%s""",(text))
except:
return
if c.rowcount:
bans = c.fetchall()
(channel,by,kind,mask,begin_at,end_at,removed_at,removed_by) = bans[0]
if kind == 'q':
if mask in irc.state.channels[channel].quiets and not removed_at:
self._markendban(irc,channel,msg.prefix,text)
if irc.nick in irc.state.channels[channel].ops:
irc.queueMsg(ircmsgs.unquiet(channel,mask))
else:
irc.queueMsg(ircmsgs.IrcMsg('CS UNQUIET %s %s' % (channel,mask)))
unquiet = wrap(unquiet, ['op',
'text'])
def invite(self, irc, msg, args, channel, nick):
"""[<channel>] <nick>
If you have the #channel,op capability, this will invite <nick>
to join <channel>. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
nick = nick or msg.nick
self._sendMsg(irc, ircmsgs.invite(nick, channel))
self.invites[(irc.getRealIrc(), ircutils.toLower(nick))] = irc
invite = wrap(invite, ['op', ('haveOp', 'invite someone'),
additional('nick')])
def _check (self,irc,channel,ban):
a = []
L = []
masks = []
if not channel in irc.state.channels:
return None
for user in irc.state.channels[channel].users:
try:
hostmask = irc.state.nickToHostmask(user)
masks.append([hostmask,getmask(irc,user)])
except:
continue
try:
(n,i,h) = ircutils.splitHostmask(ban)
if h.find('$') != -1:
h = h.split('$')[0]
ban = '%s!%s@%s' % (n,i,h)
except:
self.log.error('error in _check')
for m in masks:
(h,mask) = m
if ircutils.hostmaskPatternEqual(ban,h) or ircutils.hostmaskPatternEqual(ban,mask):
L.append(h)
return L
def check (self,irc,msg,args,channel,text):
"""[<channel>] [<banmask>] returns list of affected users"""
L = self._check(irc,channel,text)
if L is None:
irc.error("i'm not in %s" % channel)
else:
irc.reply('%s matchs: %s' % (text,', '.join(L)))
check = wrap(check,['op','text'])
def restorechan (self,irc,msg,args,channel):
"""[<channel>] update channel config with supybot one"""
self._restorechan(irc,channel)
irc.replySuccess()
restorechan = wrap(restorechan, ['owner','channel'])
def baninfo (self,irc,msg,args,user,id):
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=%s""",(id))
except:
irc.error('database is locked, try again later')
return
L = []
if c.rowcount:
bans = c.fetchall()
(channel,by,kind,mask,begin_at,end_at,removed_at,removed_by) = bans[0]
on = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at)))
L.append('%s in %s, %s sets +%s %s' % (on,channel,by,kind,mask))
was = float(begin_at) == float(end_at)
if was:
was = 'forever'
else:
was = utils.timeElapsed(float(end_at) - float(begin_at))
if not removed_at:
if was == 'forever':
L.append('duration is %s' % was)
else:
L.append('duration is %s and will expire in %s' % (was,utils.timeElapsed(float(end_at) - time.time())))
else:
L.append('original duration was %s' % was)
L.append('removed after %s on %s by %s' % (utils.timeElapsed(float(removed_at)-float(begin_at)),time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))),removed_by))
c = db.cursor()
try:
c.execute("""SELECT oper, comment FROM comments WHERE ban_id=%s""",(id))
if c.rowcount:
comments = c.fetchall()
for comment in comments:
(n,t) = comment
L.append('comment by %s: %s' % (n,t))
else:
L.append('no comment can be found')
except:
L.append('error when trying to read comment')
c = db.cursor()
try:
c.execute("""SELECT full FROM nicks WHERE ban_id=%s""",(id))
if c.rowcount:
bans = c.fetchall()
for ban in bans:
L.append('affected %s' % ban[0])
L.append('See !banlog %s for log' % id)
except:
L.append('error when trying to read affected users')
for line in L:
irc.queueMsg(ircmsgs.privmsg(msg.nick,line))
baninfo = wrap(baninfo, ['user', 'nonNegativeInt'])
def bansearch (self,irc,msg,args,user,text):
"""[<hostmask|nick>] returns bans match"""
db = self._getbandb()
t = '*%s*' % text
c = db.cursor()
try:
c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB %s ORDER BY ban_id DESC""",(t))
except:
irc.reply('database locked, try again later')
return
L = []
a = {}
if c.rowcount:
bans = c.fetchall()
d = {}
for ban in bans:
(id,full) = ban
if not id in d:
d[id] = id
for ban in d:
c = db.cursor()
try:
c.execute("""SELECT id, mask, kind, channel FROM bans WHERE id=%s ORDER BY id DESC""",(int(ban)))
if c.rowcount:
bans = c.fetchall()
for ban in bans:
(id,mask,kind,channel) = ban
a[id] = ban
except:
irc.reply('database locked, try again later')
return
c = db.cursor()
try:
c.execute("""SELECT id, mask, kind, channel FROM bans WHERE mask GLOB %s ORDER BY id DESC""",(t))
if c.rowcount:
bans = c.fetchall()
for ban in bans:
(id,mask,kind,channel) = ban
a[id] = ban
except:
irc.reply('database locked, try again later')
return
if len(a):
ar = []
for ban in a:
(id,mask,kind,channel) = a[ban]
ar.append([int(id),mask,kind,channel])
ar.sort(reverse=True)
i = 0
while i < len(ar):
(id,mask,kind,channel) = ar[i]
L.append('[#%s +%s %s in %s]' % (id,kind,mask,channel))
i = i+1
irc.reply(', '.join(L))
else:
irc.reply('no ban found')
bansearch = wrap(bansearch, ['user','text'])
def banedit(self, irc, msg, args, user, id, text):
"""[<id>] [<duration>s,m,h,d,w,M,Y 0 means forever] change duration of an active ban/quiet"""
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT channel, oper, kind, mask, begin_at, end_at, removed_at FROM bans WHERE id=%s""",int(id))
except:
irc.error('database locked, try again later')
return
if c.rowcount:
bans = c.fetchall()
(channel,by,kind,mask,begin_at,end_at,removed_at) = bans[0]
if removed_at:
irc.error('this ban/quiet has been removed')
return
was = float(end_at) - float(begin_at)
if channel in irc.state.channels:
chan = self._getChan(irc,channel)
if not irc.state.channels[channel].synchro or not chan.synchro:
irc.error('please, try again later, channel is not synchronised yet.')
return
if kind == 'q':
if not mask in irc.state.channels[channel].quiets:
irc.reply('there is no +q %s in %s' % (mask,channel))
return
elif kind == 'b':
if not mask in irc.state.channels[channel].bans:
irc.reply('there is no +b %s in %s' % (mask,channel))
return
d = getduration(text)
t = time.time()
if d < 1:
irc.queueMsg(ircmsgs.IrcMsg('MODE %s -%s %s' % (channel,kind,mask)))
return
t = t+d
c = db.cursor()
ban = (id,by,kind,mask,begin_at,t)
try:
c.execute("""UPDATE bans SET end_at=%s WHERE id=%s""",(t,id))
except:
irc.error('database locked, try again later')
return
db.commit()
if kind == 'b':
if mask in chan.pendingBans:
name = chan.pendingBans[mask]
try:
schedule.removeEvent(name)
except:
self.log.info('cannot found schedule %s : %s %s' % (name,mask,channel))
else:
self.log.info('no schedule %s %s' % (mask,channel))
name = self._scheduleun(irc,channel,kind,mask,t)
chan.activeBans[mask] = chan.schedules[name] = ban
chan.pendingBans[mask] = name
elif kind == 'q':
if mask in chan.pendingQuiets:
name = chan.pendingQuiets[mask]
try:
schedule.removeEvent(name)
except:
self.log.error('cannot remove schedule %s : %s %s' % (name,mask,channel))
else:
self.log.info('no schedule %s %s' % (mask,channel))
name = self._scheduleun(irc,channel,kind,mask,t)
chan.activeQuiets[mask] = chan.schedules[name] = ban
chan.pendingQuiets[mask] = name
if was == 0:
was = 'forever'
else:
was = utils.timeElapsed(was)+' with %s remaining' % utils.timeElapsed(float(end_at) - time.time())
self._banmark(irc,id,msg.prefix,'duration updated : %s ( was %s )' % (text,was))
irc.reply('#%s duration updated for +%s %s in %s (was %s)' % (id,kind,mask,channel,was))
else:
irc.reply('there is no ban/quiet #%s' % id)
banedit = wrap(banedit, ['user', 'nonNegativeInt','text'])
def banmark(self,irc,msg,args,user,id,text):
"""[<id>] [<text>] comment a ban/quiet"""
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT id FROM bans WHERE id=%s""",int(id))
except:
irc.error('database locked, try again later')
return
if c.rowcount:
c = db.cursor()
try:
c.execute("""INSERT INTO comments VALUES (%s, %s, %s, %s)""",
(id,msg.prefix,time.time(),text))
db.commit()
except:
irc.reply('database locked, try again later')
return
irc.queueMsg(ircmsgs.privmsg(msg.nick,'done. see !baninfo %s or !banlog %s or !banaffects %s' % (id,id,id)))
else:
irc.reply('there is no ban/quiet with id #%s' % id)
banmark = wrap(banmark, ['user', 'nonNegativeInt','text'])
def banaffects(self, irc, msg, args, user, id):
"""[<id>] return list of users affected by a ban/quiet"""
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT channel, oper, kind, mask, begin_at FROM bans WHERE id=%s LIMIT 1""",(id))
except:
irc.error('database locked, try again later')
return
s = ''
if c.rowcount:
bans = c.fetchall()
(channel,by,kind,mask,begin_at) = bans[0]
s += 'On %s, by %s in %s +%s %s' % (time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))),by,channel,kind,mask)
else:
irc.reply('there is no ban/quiet with #%s' % id)
return
c = db.cursor()
L = []
try:
c.execute("""SELECT full FROM nicks WHERE ban_id=%s""",(id))
except:
irc.error('database locked, try again later')
return
if c.rowcount:
bans = c.fetchall()
for ban in bans:
L.append('%s' % ban[0])
s += ' affected %s user(s): %s' % (len(L),', '.join(L))
irc.reply(s)
else:
s += ' affected 0 user'
irc.reply(s)
banaffects = wrap(banaffects, ['user', 'nonNegativeInt'])
def banlog (self,irc,msg,args,user,id):
"""[<id>] return log of affected users by a ban"""
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT id FROM bans WHERE id=%s LIMIT 1""",(id))
except:
irc.error('database locked, try again later')
return
if not c.rowcount:
irc.reply('there is no ban/quiet with #%s' % id)
return
L = []
L.append('Logs of #%s:' % id)
c = db.cursor()
try:
c.execute("""SELECT full, log FROM nicks WHERE ban_id=%s""",(id))
except:
irc.error('database locked, try again later')
return
if c.rowcount:
users = c.fetchall()
for u in users:
(full,log) = u
L.append('for %s' % full)
if log != '':
for line in log.split('\n'):
if line:
L.append(line)
L.append('--')
for line in L:
irc.queueMsg(ircmsgs.privmsg(msg.nick,line))
else:
irc.reply('no log for this ban/quiet')
banlog = wrap(banlog, ['user', 'nonNegativeInt'])
def makeDb(self, filename):
if os.path.exists(filename):
return sqlite.connect(filename)
db = sqlite.connect(filename)
c = db.cursor()
c.execute("""CREATE TABLE datas (
id INTEGER PRIMARY KEY,
mask VARCHAR(500) NOT NULL,
time TIMESTAMP NOT NULL,
regexp TEXT NOT NULL,
action TEXT NOT NULL,
kind VARCHAR(4)
)""")
db.commit()
return db
def _restoreregexps(self,channel):
db = self.getDb(channel)
c = db.cursor()
c.execute("""SELECT id, regexp, action, kind FROM datas WHERE 1=1""")
L = []
for item in c.fetchall():
L.append([item[0],item[1],item[2],item[3],utils.str.perlReToPythonRe(item[1])])
self.log.info('%s regexps restored for %s' % (len(L),channel))
return L
def onjoin (self,irc,msg,args,channel,text):
"""[channel] </regexp/ @ action> triggered at each join"""
db = self.getDb(channel)
cu = db.cursor()
mask = irc.state.nickToHostmask(msg.nick)
a = text.split('@')
c = self._getChan(irc,channel)
if len(a) == 2:
regexp = a[0].rstrip()
action = a[1].lstrip()
try:
reg = utils.str.perlReToPythonRe(regexp)
except:
irc.reply('bad regular expression, see : http://www.cs.tut.fi/~jkorpela/perl/regexp.html')
return
cu.execute("""INSERT INTO datas VALUES (NULL, %s, %s, %s, %s, 'JOIN')""", mask, int(time.time()), regexp, action)
id = db.insert_id()
db.commit()
c.regexps.append([id,regexp,action,'JOIN',reg])
irc.reply('#%s done' % id)
else:
irc.reply('usage: /regexp/ @ command')
onjoin = wrap(onjoin,['channel','op','text'])
def onnick (self,irc,msg,args,channel,text):
"""[channel] </regexp/ @ action> trigger when user change nick ( you can use $nick, $channel, $mask, $text )"""
db = self.getDb(channel)
cu = db.cursor()
mask = irc.state.nickToHostmask(msg.nick)
a = text.split('@')
c = self._getChan(irc,channel)
if len(a) == 2:
regexp = a[0].rstrip()
action = a[1].lstrip()
try:
reg = utils.str.perlReToPythonRe(regexp)
except:
irc.reply('bad regular expression, see : http://www.cs.tut.fi/~jkorpela/perl/regexp.html')
return
cu.execute("""INSERT INTO datas VALUES (NULL, %s, %s, %s, %s, 'NICK')""", mask, int(time.time()), regexp, action)
id = db.insert_id()
db.commit()
c.regexps.append([id,regexp,action,'NICK',reg])
irc.reply('#%s done' % id)
else:
irc.reply('usage: /regexp/ @ command')
onnick = wrap(onnick, ['channel','op','text'])
def watch (self,irc,msg,args,channel,text):
"""[channel] <nick|/regexp/> forward message to logChannel"""
c = self._getChan(irc,channel)
if not c.logChannel in irc.state.channels:
irc.reply("There is no logChannel setted for %s or i'm not in" % channel)
return
regexp = ''
if text in irc.state.channels[channel].users:
reg = getmask(irc,text)
reg = reg.replace('~','')
reg = reg.replace('/','\/')
reg = reg.replace('.','\.')
reg = reg.replace('*','\*')
reg = reg.replace('[','\[')
reg = reg.replace(']','\]')
regexp = '/%s/' % reg
else:
regexp = text
self.log.info('trying to watch %s in %s' % (regexp,channel))
db = self.getDb(channel)
cu = db.cursor()
mask = irc.state.nickToHostmask(msg.nick)
action = ''
try:
reg = utils.str.perlReToPythonRe(regexp)
except:
irc.reply('Sorry, bad regexp, or cannot found user, take a look here : http://www.cs.tut.fi/~jkorpela/perl/regexp.html')
return
cu.execute("""INSERT INTO datas VALUES (NULL, %s, %s, %s, %s, 'TEXT')""", mask, int(time.time()), regexp, action)
id = db.insert_id()
action = 'channel private %s [$channel](#%s) <$nick> $text' % (c.logChannel,id)
cu.execute("""UPDATE datas SET action=%s WHERE id=%s""", (action,int(id)))
db.commit()
c.regexps.append([id,regexp,action,'TEXT',reg])
irc.reply('#%s done' % id)
watch = wrap(watch, ['channel','op','text'])
def unwatch (self,irc,msg,args,channel,text):
"""[channel] <nick|/regexp/> delete a forward to logChannel trigger"""
c = self._getChan(irc,channel)
if not c.logChannel in irc.state.channels:
irc.reply("i'm not in %s" % c.logChannel)
return
regexp = ''
if text in irc.state.channels[channel].users:
reg = getmask(irc,text)
reg = reg.replace('~','')
reg = reg.replace('/','\/')
reg = reg.replace('.','\.')
reg = reg.replace('*','\*')
reg = reg.replace('[','\[')
reg = reg.replace(']','\]')
regexp = '/%s/' % reg
else:
regexp = text
db = self.getDb(channel)
cu = db.cursor()
cu.execute("""SELECT id FROM datas WHERE regexp='%s'""" % regexp)
if cu.rowcount == 0:
irc.reply('sorry, no such regexp')
return
id = int(cu.fetchall()[0][0])
cu.execute("""DELETE FROM datas WHERE id=%s""" % id)
db.commit()
for item in c.regexps:
if item[0] == id:
index = c.regexps.index(item)
del c.regexps[index]
irc.reply('#%s deleted' % id)
return
irc.reply("can't found %s in regular expression database of %s" % (regexp,channel))
unwatch = wrap(unwatch, ['channel','op','text'])
def warn (self,irc,msg,args,channel,text):
"""[channel] </regexp/ @ number action> trigger at each message in channel ( you can use $nick, $channel, $mask, $text )"""
c = self._getChan(irc,channel)
db = self.getDb(channel)
cu = db.cursor()
mask = irc.state.nickToHostmask(msg.nick)
a = text.split('@')
if len(a) == 2:
regexp = a[0].rstrip()
action = a[1].lstrip()
try:
reg = utils.str.perlReToPythonRe(regexp)
except:
irc.reply('Sorry, bad regexp, take a look here : http://www.cs.tut.fi/~jkorpela/perl/regexp.html')
return
cu.execute("""INSERT INTO datas VALUES (NULL, %s, %s, %s, %s, 'WARN')""", mask, int(time.time()), regexp, action)
id = db.insert_id()
db.commit()
c.regexps.append([id,regexp,action,'WARN',reg])
irc.reply('#%s done' % id)
else:
irc.reply('usage: /regexp/ @ command')
warn = wrap(warn,['channel','op','text'])
def regadd (self,irc,msg,args,channel,text):
"""[channel] </regexp/ @ action> trigger at each message in channel ( you can use $nick, $channel, $mask, $text )"""
c = self._getChan(irc,channel)
db = self.getDb(channel)
cu = db.cursor()
mask = irc.state.nickToHostmask(msg.nick)
a = text.split('@')
if len(a) == 2:
regexp = a[0].rstrip()
action = a[1].lstrip()
try:
reg = utils.str.perlReToPythonRe(regexp)
except:
if msg.nick != irc.nick:
irc.reply('Sorry, bad regexp, take a look here : http://www.cs.tut.fi/~jkorpela/perl/regexp.html')
return
cu.execute("""INSERT INTO datas VALUES (NULL, %s, %s, %s, %s, 'TEXT')""", mask, int(time.time()), regexp, action)
id = db.insert_id()
db.commit()
c.regexps.append([id,regexp,action,'TEXT',reg])
if msg.nick != irc.nick:
irc.reply('#%s done' % id)
else:
if msg.nick != irc.nick:
irc.reply('usage: /regexp/ @ command')
regadd = wrap(regadd,['channel','op','text'])
def reglist (self,irc,msg,args,channel):
"""[channel] list regexp in database"""
c = self._getChan(irc,channel)
L = []
for item in c.regexps:
L.append('[#%s %s @ %s |%s]' % (item[0],item[1],item[2],item[3]))
if len(L) != 0:
irc.reply(', '.join(L))
else:
irc.reply('no regexp on %s' % channel)
reglist = wrap(reglist,['channel','op'])
def reginfo (self,irc,msg,args,channel,text):
"""[channel] <id> give information about a regexp"""
id = -1
if text.isdigit():
id = int(text)
db = self.getDb(channel)
c = db.cursor()
c.execute("""SELECT mask, regexp, action, time, kind FROM datas WHERE id LIKE %s""" %id)
if c.rowcount == 0:
irc.reply('no such id')
return
matchs = c.fetchall()
(mask,reg,action,at,kind) = matchs[0]
at = time.strftime(conf.supybot.reply.format.time(),time.localtime(int(at)))
irc.queueMsg(ircmsgs.privmsg(msg.nick,'%s: #%i %s @ %s by %s at %s / %s' % (msg.nick, id, reg, action, mask, at, kind)))
else:
irc.reply('%s is not an id' % text)
reginfo = wrap(reginfo,['channel','op','text'])
def regdel (self,irc,msg,args,channel,text):
"""[channel] <id|/regexp/> delete regexp by id or regexp"""
id = -1
c = self._getChan(irc,channel)
db = self.getDb(channel)
if text.isdigit():
id = int(text)
cu = db.cursor()
cu.execute("""SELECT id FROM datas WHERE id LIKE %s""" %id)
if cu.rowcount == 0:
irc.error('sorry, no such id')
return
else:
cu.execute("""DELETE FROM datas WHERE id LIKE %s""" % id)
db.commit()
for item in c.regexps:
if item[0] == id:
index = c.regexps.index(item)
del c.regexps[index]
irc.reply('#%s deleted' % id)
return
else:
cu = db.cursor()
cu.execute("""SELECT id FROM datas WHERE regexp='%s'""" % text)
if cu.rowcount == 0:
if msg.nick != irc.nick:
irc.reply('sorry, no such regexp')
return
id = int(cu.fetchall()[0][0])
cu.execute("""DELETE FROM datas WHERE id=%s""" % id)
db.commit()
for item in c.regexps:
if item[0] == id:
index = c.regexps.index(item)
del c.regexps[index]
if msg.nick != irc.nick:
irc.reply('#%s deleted' % id)
return
if msg.nick != irc.nick:
irc.reply('sorry, no such regexp')
regdel = wrap(regdel,['channel','op','text'])
def _restorechan (self,irc,channel):
self._delChan(irc,channel)
self._getChan(irc,channel)
def _getChan (self,irc,channel):
ch = channel
channel = channel.lower()
if not irc in self.ircs:
self.ircs[irc] = {}
if not channel in self.ircs[irc]:
c = self.ircs[irc][channel] = Chan ()
c.logChannel = self.registryValue('logChannel',channel=ch)
c.logSize = self.registryValue('logSize',channel=ch)
c.opChannel = self.registryValue('opChannel',channel=ch)
c.evadeBanCheck = self.registryValue('evadeBanCheck',channel=ch)
c.evadeKickMessage = self.registryValue('evadeKickMessage',channel=ch)
c.evadeBanDuration = self.registryValue('evadeBanDuration',channel=ch)
c.floodCheck = self.registryValue('floodCheck',channel=ch)
c.floodPermit = self.registryValue('floodPermit',channel=ch)
c.floodLife = self.registryValue('floodPermit',channel=ch)
c.floodQuietDuration = self.registryValue('floodQuietDuration',channel=ch)
if c.floodCheck:
c.floodQueue = SpamQueue (c.floodLife)
c.lowFloodCheck = self.registryValue('lowFloodCheck',channel=ch)
c.lowFloodPermit = self.registryValue('lowFloodPermit',channel=ch)
c.lowFloodLife = self.registryValue('lowFloodLife',channel=ch)
c.lowFloodQuietDuration = self.registryValue('lowFloodQuietDuration',channel=ch)
if c.lowFloodCheck:
c.lowFloodQueue = SpamQueue(c.lowFloodLife)
c.floodMessage = self.registryValue('floodMessage',channel=ch)
c.repeatCheck = self.registryValue('repeatCheck',channel=ch)
c.repeatPermit = self.registryValue('repeatPermit',channel=ch)
c.repeatLife = self.registryValue('repeatLife',channel=ch)
c.repeatMessage = self.registryValue('repeatMessage',channel=ch)
c.repeatQuietDuration = self.registryValue('repeatQuietDuration',channel=ch)
if c.repeatCheck:
c.repeatQueue = SpamQueue (c.repeatLife)
c.highlightCheck = self.registryValue('highlightCheck',channel=ch)
c.highlightPermit = self.registryValue('highlightPermit',channel=ch)
c.highlightMessage = self.registryValue('highlightMessage',channel=ch)
c.highlightQuietDuration = self.registryValue('highlightQuietDuration',channel=ch)
c.noticeCheck = self.registryValue('noticeCheck',channel=ch)
c.noticeLife = self.registryValue('noticeLife',channel=ch)
c.noticePermit = self.registryValue('noticePermit',channel=ch)
c.noticeMessage = self.registryValue('noticeMessage',channel=ch)
c.noticeQuietDuration = self.registryValue('noticeQuietDuration',channel=ch)
if c.noticeCheck:
c.noticeQueue = SpamQueue(c.noticeLife)
c.badUserLife = self.registryValue('badUserLife',channel=ch)
c.badUserQueue = SpamQueue(c.badUserLife)
c.badUserPermit = self.registryValue('badUserPermit',channel=ch)
c.badUserMessage = self.registryValue('badUserMessage',channel=ch)
c.badUserBanDuration = self.registryValue('badUserBanDuration',channel=ch)
c.massjoinCheck = self.registryValue('massjoinCheck',channel=ch)
c.massjoinLife = self.registryValue('massjoinLife',channel=ch)
if c.massjoinCheck:
c.massjoinQueue = SpamQueue(c.massjoinLife)
c.massjoinPermit = self.registryValue('massjoinPermit',channel=ch)
c.massjoinMode = self.registryValue('massjoinMode',channel=ch)
c.massjoinUnMode = self.registryValue('massjoinUnMode',channel=ch)
c.massjoinDuration = self.registryValue('massjoinDuration',channel=ch)
c.cycleCheck = self.registryValue('cycleCheck',channel=ch)
c.cycleLife = self.registryValue('cycleLife',channel=ch)
if c.cycleCheck:
c.cycleQueue = SpamQueue(c.cycleLife)
c.cyclePermit = self.registryValue('cyclePermit',channel=ch)
c.cycleBanDuration = self.registryValue('cycleBanDuration',channel=ch)
c.regexps = self._restoreregexps(ch)
c.commandCheck = self.registryValue('commandCheck',channel=ch)
c.commandPermit = self.registryValue('commandPermit',channel=ch)
c.commandLife = self.registryValue('commandLife',channel=ch)
if c.commandCheck:
c.commandQueue = SpamQueue(c.commandLife)
c.commandDisableDuration = self.registryValue('commandDisableDuration',channel=ch)
c.warnLife = self.registryValue('warnLife',channel=ch)
if not c.synchro and channel in irc.state.channels and irc.state.channels[channel].synchro:
self._syncChan(irc,channel)
else:
c = self.ircs[irc][channel]
if not c.synchro and channel in irc.state.channels and irc.state.channels[channel].synchro:
self._syncChan(irc,channel)
return c
def _delChan (self,irc,channel):
channel = channel.lower()
if not irc in self.ircs:
return
if not channel in self.ircs[irc]:
return
c = self.ircs[irc][channel]
if c.synchro:
if c.warnSchedule:
try:
schedule.removeEvent(c.warnSchedule)
except:
self.log.error('cannot remove %s' % c.warnSchedule)
if len(c.schedules):
for s in c.schedules:
try:
schedule.removeEvent(s)
except:
continue
del self.ircs[irc][channel]
self.log.info('%s removed' % channel)
def _syncChan (self,irc,channel):
c = self.ircs[irc][channel]
db = self._getbandb()
cu = db.cursor()
cu.execute("""SELECT id, oper, kind, mask, begin_at, end_at, removed_at FROM bans WHERE channel=%s ORDER BY id""",channel)
toForget = []
toRemove = []
if cu.rowcount:
bans = cu.fetchall()
t = time.time()
for fullban in bans:
(id,by,kind,mask,begin,end,removed_at) = fullban
ban = (id,by,kind,mask,begin,end)
if not removed_at:
if float(begin) != float(end):
active = float(end) > t
if active:
if kind == 'b':
if mask in irc.state.channels[channel].bans:
name = self._scheduleun(irc,channel,kind,mask,end)
c.activeBans[mask] = c.schedules[name] = ban
c.pendingBans[mask] = name
else:
toForget.append(ban)
elif kind == 'q':
if mask in irc.state.channels[channel].quiets:
name = self._scheduleun(irc,channel,kind,mask,end)
c.activeQuiets[mask] = c.schedules[name] = ban
c.pendingQuiets[mask] = name
else:
toForget.append(ban)
else:
if kind == 'b':
if mask in irc.state.channels[channel].bans:
c.activeBans[mask] = ban
toRemove.append(ban)
else:
toForget.append(ban)
elif kind == 'q':
if mask in irc.state.channels[channel].quiets:
c.activeQuiets[mask] = ban
toRemove.append(ban)
else:
toForget.append(ban)
else:
if kind == 'b':
if mask in irc.state.channels[channel].bans:
c.activeBans[mask] = ban
else:
toForget.append(ban)
elif kind == 'q':
if mask in irc.state.channels[channel].quiets:
c.activeQuiets[mask] = ban
else:
toForget.append(ban)
m = []
for ban in toRemove:
(id,by,kind,mask,begin,end) = ban
m.append(('-%s' % kind,mask))
def f(L):
return ircmsgs.modes(channel,L)
self._sendMsgs(irc,m,f)
for ban in toForget:
(id,by,kind,mask,begin,end) = ban
self._endban (irc,channel,'Unknow!~unknow@unknow',id)
now = time.time()
for b in irc.state.channels[channel].bans:
if not b in c.activeBans and b in irc.state.channels[channel].bansOwner:
id = self._addban(irc,channel,irc.state.channels[channel].bansOwner[b],'b',b,0)
c.activeBans[str(b)] = (id,irc.state.channels[channel].bansOwner[b],'b',b,now,now)
for q in irc.state.channels[channel].quiets:
if not q in c.activeQuiets and q in irc.state.channels[channel].quietsOwner:
id = self._addban(irc,channel,irc.state.channels[channel].quietsOwner[q],'q',q,0)
c.activeQuiets[str(q)] = (id,irc.state.channels[channel].quietsOwner[q],'q',q,now,now)
def clearWarn():
for u in c.nicks:
c.nicks[u].warns = 0
c.warnSchedule = schedule.addEvent(clearWarn,c.warnLife)
# if c.logChannel in irc.state.channels:
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] remove %s q/b' % (channel,len(toRemove))))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] forget %s q/b' % (channel,len(toForget))))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] schedule %s q/b' % (channel,len(c.schedules))))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] active %s q/b' % (channel,(len(c.activeQuiets)+len(c.activeBans)))))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] %s regulars expressions restored' % (channel,len(c.regexps))))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] flood %s, lowFlood %s, evadeBan %s, repeat %s, highlight %s, notice/ctcp %s, massjoin %s, cycle %s' % (channel,c.floodCheck,c.lowFloodCheck,c.evadeBanCheck,c.repeatCheck,c.highlightCheck,c.noticeCheck,c.massjoinCheck,c.cycleCheck)))
#irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] synchronised' % channel))
self.log.info('** %s synchronised' % channel)
c.synchro = True
def _scheduleun(self,irc,channel,kind,mask,end):
def un():
if channel in irc.state.channels and irc.state.channels[channel].synchro:
c = self._getChan(irc,channel)
if irc.nick in irc.state.channels[channel].ops:
if kind == 'b' and mask in irc.state.channels[channel].bans:
irc.queueMsg(ircmsgs.IrcMsg('MODE %s -b %s' % (channel,mask)))
elif kind == 'q' and mask in irc.state.channels[channel].quiets:
irc.queueMsg(ircmsgs.IrcMsg('MODE %s -q %s' % (channel,mask)))
else:
self.log.info('error, nothing to do for -%s in %s about %s' % (kind,channel,mask))
else:
if kind == 'b' and mask in irc.state.channels[channel].bans:
self.log.info('error, not opped cannot -%s in %s about %s' % (kind,channel,mask))
elif kind == 'q' and mask in irc.state.channels[channel].quiets:
irc.queueMsg(ircmsgs.IrcMsg('CS UNQUIET %s %s' % (channel,mask)))
return schedule.addEvent(un,float(end))
def die(self):
for irc in self.ircs:
for channel in self.ircs[irc]:
c = self._getChan(irc,channel)
if len(c.schedules):
for s in c.schedules:
try:
schedule.removeEvent(s)
except:
continue
self.ircs = {}
def _doLog(self,irc,channel,nick,message):
c = self._getChan(irc,channel)
try:
hostmask = nick
(n, u, h) = ircutils.splitHostmask(hostmask)
m = ircutils.joinHostmask('*',u,h)
except:
return
if not n in c.nicks:
oNick = Nick ()
oNick.nick = n
oNick.host = hostmask
oNick.mask = getmask(irc,n)
c.nicks[n] = oNick
else:
oNick = c.nicks[n]
if len(oNick.logs) > c.logSize:
oNick.logs.dequeue()
oNick.logs.enqueue('%s %s' % (time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime()),message))
def _getUser (self,irc,channel,nick):
c = self._getChan(irc,channel)
try:
hostmask = nick
(n, u, h) = ircutils.splitHostmask(hostmask)
m = ircutils.joinHostmask('*',u,h)
except:
return
if not n in c.nicks:
oNick = Nick ()
oNick.nick = n
oNick.host = hostmask
oNick.mask = getmask(irc,n)
c.nicks[n] = oNick
else:
oNick = c.nicks[n]
return oNick
def makeDb(self, filename):
if os.path.exists(filename):
return sqlite.connect(filename)
db = sqlite.connect(filename)
c = db.cursor()
c.execute("""CREATE TABLE datas (
id INTEGER PRIMARY KEY,
mask VARCHAR(1000) NOT NULL,
time TIMESTAMP NOT NULL,
regexp TEXT NOT NULL,
action TEXT NOT NULL,
kind VARCHAR(4) NOT NULL
)""")
db.commit()
return db
def _getbandb (self):
filename = self.registryValue('banDatabase')
if os.path.exists(filename):
return sqlite.connect(filename)
db = sqlite.connect(filename)
c = db.cursor()
c.execute("""CREATE TABLE bans (
id INTEGER PRIMARY KEY,
channel VARCHAR(100) NOT NULL,
oper VARCHAR(1000) NOT NULL,
kind VARCHAR(1) NOT NULL,
mask VARCHAR(1000) NOT NULL,
begin_at TIMESTAMP NOT NULL,
end_at TIMESTAMP NOT NULL,
removed_at TIMESTAMP,
removed_by VARCHAR(1000)
)""")
c.execute("""CREATE TABLE nicks (
ban_id INTEGER,
ban VARCHAR(1000) NOT NULL,
full VARCHAR(1000) NOT NULL,
log TEXT NOT NULL
)""")
c.execute("""CREATE TABLE comments (
ban_id INTEGER,
oper VARCHAR(1000) NOT NULL,
at TIMESTAMP NOT NULL,
comment TEXT NOT NULL
)""")
db.commit()
return db
def _addbanaffects (self,irc,channel,id,kind,ban):
chan = self._getChan(irc,channel)
db = self._getbandb()
L = self._check(irc,channel,ban)
if not L or len(L) == 0:
L = []
for n in chan.nicks:
if ircutils.hostmaskPatternEqual(ban,chan.nicks[n].host):
L.append(chan.nicks[n].host)
if L and len(L):
for u in L:
c = db.cursor()
n = u.split('!')[0]
log = ''
if n in chan.nicks:
count = 0
for line in chan.nicks[n].logs:
log += chan.nicks[n].logs[count]+'\n'
count += 1
try:
c.execute("""INSERT INTO nicks VALUES (%s, %s, %s, %s)""",(id,ban,u,log))
db.commit()
except:
self.log.info('error in _addbanaffects with %s %s %s %s' % (channel,id,kind,ban))
continue
def _addban (self,irc,channel,oper,kind,ban,duration):
db = self._getbandb()
channel = channel.lower()
c = db.cursor()
now = time.time()
if not duration < 1:
end = now+duration
else:
end = now
try:
c.execute("""INSERT INTO bans VALUES (NULL, %s, %s, %s, %s, %s, %s, NULL, NULL)""",
(channel,oper,kind,ban,now,end))
except:
return
db.commit()
id = int(db.insert_id())
return id
def _summaryBan(self,irc,channel,by,id,kind,mode,mask,begin,end,remove):
s = '[#%s %s%s %s by %s' % (id,mode,kind,mask,by.split('!')[0])
b = float(begin)
e = float(end)
db = self._getbandb()
c = db.cursor()
try:
c.execute("""SELECT full FROM nicks WHERE ban_id=%s""",(id))
except:
s += ']'
return s
L = []
if c.rowcount:
bans = c.fetchall()
for ban in bans:
L.append(ban)
if b != e and not remove:
s += ', for %s' % utils.timeElapsed(e-b)
if remove:
r = float(remove)
s += ', during %s' % utils.timeElapsed(r-b)
if len(L) == 0:
s += ', affected 0 user'
elif len(L) == 1:
s += ', affected %s' % L[0][0]
else:
s += ', affected %s users' % len(L)
else:
if len(L) == 0:
s += ', affects 0 user'
elif len(L) == 1:
s += ', affects %s' % L[0][0].split('!')[0]
else:
s += ', affects %s users' % len(L)
s += ']'
return s
def _markendban (self,irc,channel,oper,id):
db = self._getbandb()
channel = channel.lower()
c = db.cursor()
ch = self._getChan(irc,channel)
ch.removed[id] = oper
try:
c.execute("""UPDATE bans SET removed_by=%s WHERE id=%s""",(oper,id))
except:
return
db.commit()
def _endban (self,irc,channel,oper,id):
db = self._getbandb()
channel = channel.lower()
c = db.cursor()
try:
c.execute("""SELECT id, removed_by FROM bans WHERE id=%s""",id)
if c.rowcount:
bans = c.fetchall()
(id,removed_by) = bans[0]
if removed_by:
oper = removed_by
except:
self.log.info('error when trying to find removed_by for %s' % id)
try:
c = db.cursor()
c.execute("""UPDATE bans SET removed_at=%s, removed_by=%s WHERE id=%s""", (time.time(),oper,id))
except:
return oper
db.commit()
return oper
def _isFlood (self,irc,channel,match,text):
c = self._getChan(irc,channel)
key = [match]
lines = splitmessage(text,300)
if c.floodCheck:
if len(lines) > 1:
for line in lines:
c.floodQueue.enqueue(key)
else:
c.floodQueue.enqueue(key)
if c.floodQueue.len(key) > c.floodPermit:
c.floodQueue.reset(key)
c.badUserQueue.enqueue([match])
self.log.info('flood detected for %s in %s' % (match,channel))
return True
return False
def _isLowFlood (self,irc,channel,match,text):
c = self._getChan(irc,channel)
key = [match]
lines = splitmessage(text,300)
if c.lowFloodCheck:
if len(lines) > 1:
for line in lines:
c.lowFloodQueue.enqueue(key)
else:
c.lowFloodQueue.enqueue(key)
if c.lowFloodQueue.len(key) > c.lowFloodPermit:
c.lowFloodQueue.reset(key)
c.badUserQueue.enqueue([match])
self.log.info('low flood detected for %s in %s' % (match,channel))
return True
return False
def _isRepeat (self,irc,channel,match,text):
c = self._getChan(irc,channel)
key = ['%s%s' % (match,text)]
lines = splitmessage(text,300)
if c.repeatCheck:
if len(lines) > 1:
for line in lines:
c.repeatQueue.enqueue(key)
else:
c.repeatQueue.enqueue(key)
if c.repeatQueue.len(key) > c.repeatPermit or self.rerepeat.search(text):
c.repeatQueue.reset(key)
c.badUserQueue.enqueue([match])
self.log.info('repeat detected for %s in %s' % (match,channel))
return True
return False
def _isHighlight (self,irc,channel,match,text):
c = self._getChan(irc,channel)
if c.highlightCheck:
n = 0
t = text.replace(',','')
t = t.replace(';','')
a = t.split(' ')
for w in a:
if w in irc.state.channels[channel].users:
n +=1
if n > c.highlightPermit:
c.badUserQueue.enqueue([match])
self.log.info('highlight detected for %s in %s' % (match,channel))
return True
return False
def _isCtcp (self,irc,channel,match,text):
c = self._getChan(irc,channel)
if c.noticeCheck:
key = [match]
s = ''
c.noticeQueue.enqueue(key)
if c.noticeQueue.len(key) > c.noticePermit:
c.noticeQueue.reset(key)
c.badUserQueue.enqueue(key)
self.log.info('ctcp detected for %s in %s' % (match,channel))
return True
return False
def doJoin(self, irc, msg):
try:
hostmask = irc.state.nickToHostmask(msg.nick)
mask = getmask(irc,msg.nick)
bot = irc.state.nickToHostmask(irc.nick)
except:
return
if not mask:
return
gateway = isgatewayweb (hostmask)
ip = False
try:
ip = ircdb.isip(mask.split('@')[1])
except:
return
if ip:
hexa = '*!*%s@*' % iptohexa(mask.split('@')[1])
now = time.time()
channels = msg.args[0].split(',')
for channel in channels:
c = self._getChan(irc,channel)
self._doLog(irc,channel,msg.prefix,'*** %s has joined' % msg.nick)
chan = ircdb.channels.getChannel(channel)
banned = False
if chan.bans:
for ban in chan.bans:
if ircutils.hostmaskPatternEqual(ban,hostmask) or ircutils.hostmaskPatternEqual(ban,mask):
s = 'kban %s %s %t' % (channel,ban,chan.bans[ban])
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
banned = True
break
if c.synchro:
massjoin = False
if c.massjoinCheck and not c.netsplit:
k = [channel]
c.massjoinQueue.enqueue(k)
if c.massjoinQueue.len(k) > c.massjoinPermit:
c.massjoinQueue.reset(k)
massjoin = True
s = 'mode %s %s' % (channel,c.massjoinMode)
if not 'r' in irc.state.channels[channel].modes:
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
def ur():
s = 'mode %s %s' % (channel,c.massjoinUnMode)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
schedule.addEvent(ur,time.time()+c.massjoinDuration)
if c.evadeBanCheck and not massjoin and not banned:
for ban in irc.state.channels[channel].bans:
if ip and hexa:
if ircutils.hostmaskPatternEqual(ban,hexa):
s = 'kban %s %s %s %s %s' % (channel,msg.nick,c.evadeBanDuration,c.evadeKickMessage,ban)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
break
elif gateway:
try:
isBanIsIp = ircdb.isip(ban.split('@')[1])
except:
continue
if isBanIsIp:
banHexa = '*!*%s@*' % iptohexa(ban.split('@')[1])
if ircutils.hostmaskPatternEqual(mask,banHexa):
s = 'kban %s %s %s %s %s' % (channel,msg.nick,c.evadeBanDuration,c.evadeKickMessage,ban)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
break
if c.logChannel in irc.state.channels:
for quiet in irc.state.channels[channel].quiets:
if mask == quiet:
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s quieted by %s joins %s' % (msg.prefix,quiet,channel)))
elif ip and hexa:
if ircutils.hostmaskPatternEqual(quiet,hexa):
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s evade quiet %s in %s' % (msg.prefix,quiet,channel)))
break
elif gateway:
try:
isBanIsIp = ircdb.isip(quiet.split('@')[1])
except:
continue
if isBanIsIp:
banHexa = '*!*%s@*' % iptohexa(quiet.split('@')[1])
if ircutils.hostmaskPatternEqual(mask,banHexa):
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s evade quiet %s in %s' % (msg.prefix,quiet,channel)))
break
else:
if ircutils.hostmaskPatternEqual(quiet,hostmask):
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s quieted by %s joins %s' % (msg.prefix,quiet,channel)))
if not massjoin:
for item in c.regexps:
if item[3] == 'JOIN':
message = item[2]
if item[4].search('%s' % hostmask):
message = message.replace('$nick','"%s"' % msg.nick)
message = message.replace('$hostmask',msg.prefix)
message = message.replace('$channel',channel)
message = message.replace('$mask',mask)
if message.find('$randomNick') != -1:
a = []
for user in irc.state.channels[channel].users:
a.append(user)
message = message.replace('$randomNick','"%s"' % a[int(len(a)*random.random())])
message = message.lstrip()
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),message.split(' '))
except:
self.log.info('Error with #%s in %s' % (item[0],channel))
def doKick(self, irc, msg):
if len(msg.args) == 3:
(channel,target,reason) = msg.args
else:
(channel,target) = msg.args
reason = ''
try:
hostmask = irc.state.nickToHostmask(target)
except:
return
self._doLog(irc,channel,hostmask,
'*** %s was kicked by %s (%s)' % (hostmask,msg.nick,reason))
c = self._getChan(irc,channel)
if c.logChannel in irc.state.channels:
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] %s kicked by %s (%s)' % (channel,hostmask,msg.nick,reason)))
if target == irc.nick:
if self.registryValue('alwaysRejoin', channel):
networkGroup = conf.supybot.networks.get(irc.network)
irc.sendMsg(networkGroup.channels.join(channel))
def doPart(self, irc, msg):
try:
mask = getmask(irc,msg.nick)
bot = irc.state.nickToHostmask(irc.nick)
except:
return
if not mask:
return
reason = ''
if len(msg.args) == 2:
(tmp,reason) = msg.args
channels = msg.args[0].split(',')
key = [mask]
for channel in channels:
c = self._getChan(irc,channel)
self._doLog(irc,channel,msg.prefix,'*** %s has left [%s]' % (msg.nick,reason))
if c.cycleCheck:
c.cycleQueue.enqueue(key)
if c.cycleQueue.len(key) > c.cyclePermit:
c.cycleQueue.reset(key)
s = 'kban %s %s %s join/part flood' % (channel,mask,c.cycleBanDuration)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
if irc.nick == msg.nick:
self._delChan(irc,channel)
if c.logChannel in irc.state.channels and reason.find('requested by') != -1:
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] part %s %s' % (channel,msg.nick,reason)))
def doQuit(self, irc, msg):
try:
hostmask = irc.state.nickToHostmask(msg.nick)
mask = getmask(irc,msg.nick)
bot = irc.state.nickToHostmask(irc.nick)
except:
return
if not mask:
return
reason = ''
if len(msg.args) == 1:
reason = msg.args[0].lstrip().rstrip()
isSplit = reason == '*.net *.split'
isCloak = reason == 'Changing host'
isFlood = reason == 'Excess Flood'
key = [mask]
for channel in irc.state.channels:
c = self._getChan(irc,channel)
if isSplit and not c.netsplit:
def us():
c.netsplit = False
schedule.addEvent(us,900)
c.netsplit = True
if msg.nick in c.nicks:
self._doLog(irc,channel,msg.prefix,'*** %s has quit [%s]' % (msg.nick,reason))
if c.cycleCheck and not isSplit and not isCloak:
if not isSplit and not isCloak:
c.cycleQueue.enqueue(key)
if c.cycleQueue.len(key) > c.cyclePermit:
c.cycleQueue.reset(key)
s = 'kban %s %s %s join/part flood' % (channel,mask,c.cycleBanDuration)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
if isFlood:
c.cycleQueue.enqueue(key)
if c.cycleQueue.len(key) > c.cyclePermit:
c.cycleQueue.reset(key)
s = 'kban %s %s %s join/part flood' % (channel,mask,c.cycleBanDuration)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error %s' % s)
def doPrivmsg(self, irc, msg):
if msg.nick == irc.nick:
return
try:
hostmask = irc.state.nickToHostmask(msg.nick)
mask = getmask(irc,msg.nick)
bot = irc.state.nickToHostmask(irc.nick)
except:
return
if not mask:
return
isCtcp = ircmsgs.isCtcp(msg)
(recipients, text) = msg.args
isAction = False
if ircmsgs.isAction(msg):
isAction = True
text = ircmsgs.unAction(msg)
for channel in recipients.split(','):
if irc.isChannel(channel):
if isAction:
self._doLog(irc,channel,msg.prefix,'* %s %s' % (msg.nick,text))
else:
self._doLog(irc,channel,msg.prefix,'<%s> %s' % (msg.nick, text))
c = self._getChan(irc,channel)
underAttack = False
if c.synchro:
s = None
isClone = False
a = self._check(irc,channel,mask)
check = ircdb.ignores.checkIgnored(mask)
isrepeat = False
if self._isFlood(irc,channel,hostmask,text):
if check or mask in irc.state.channels[channel].quiets:
c.badUserQueue.enqueue([mask])
else:
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.floodQuietDuration,c.floodMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(hostmask, time.time()+c.floodQuietDuration)
check = True
if len(a) > 1:
n = 0
for user in a:
if c.floodQueue.len([user]) > c.floodPermit:
n = n+1
if n > 1:
isClone = True
if self._isLowFlood(irc,channel,hostmask,text):
if check or mask in irc.state.channels[channel].quiets:
c.badUserQueue.enqueue([mask])
else:
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.lowFloodQuietDuration,c.floodMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(hostmask, time.time()+c.lowFloodQuietDuration)
check = True
if len(a) > 1:
n = 0
for user in a:
if c.lowFloodQueue.len([user]) > c.lowFloodQueue:
n = n+1
if n > 1:
isClone = True
if self._isRepeat(irc,channel,hostmask,text):
if check or mask in irc.state.channels[channel].quiets:
c.badUserQueue.enqueue([mask])
else:
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.repeatQuietDuration,c.repeatMessage)
isrepeat = True
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(hostmask, time.time()+c.repeatQuietDuration)
check = True
if len(a) > 1:
n = 0
for user in a:
if c.repeatQueue.len(['%s%s' % (user,text)]) > c.repeatPermit:
n = n+1
if n > 1:
isClone = True
if self._isHighlight(irc,channel,hostmask,text):
if check or mask in irc.state.channels[channel].quiets:
c.badUserQueue.enqueue([mask])
else:
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.highlightQuietDuration,c.highlightMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(hostmask, time.time()+c.highlightQuietDuration)
check = True
elif self._isHighlight(irc,channel,mask,text) and len(a) > 1 and not ircdb.ignores.checkIgnored(hostmask):
isClone = True
if isCtcp and not isAction:
if self._isCtcp(irc,channel,hostmask,text):
if check or mask in irc.state.channels[channel].quiets:
c.badUserQueue.enqueue([mask])
else:
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.noticeQuietDuration,c.noticeMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(hostmask, time.time()+c.noticeQuietDuration)
check = True
if len(a) > 1:
n = 0
for user in a:
if c.noticeQueue.len([user]) > c.noticePermit:
n = n+1
if n > 1:
isClone = True
key = [mask]
if c.badUserQueue.len(key) > c.badUserPermit:
c.badUserQueue.reset(key)
s = 'kban %s %s %s %s' % (channel,msg.nick,c.badUserBanDuration,c.badUserMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(mask, time.time()+c.badUserBanDuration)
check = True
if isrepeat:
reg = text.replace('~','')
reg = reg.replace('/','\/')
reg = reg.replace('.','\.')
reg = reg.replace('*','\*')
reg = reg.replace('[','\[')
reg = reg.replace(']','\]')
reg = 'regadd %s /%s/i @ kban $channel $nick %s %s' % (channel,reg,c.badUserBanDuration,c.badUserMessage)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),reg.split(' '))
def liftreg():
ureg = 'regdel %s /%s/i' % (channel,text)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),ureg.split(' '))
except:
self.log.info('Error with %s' % ureg)
schedule.addEvent(liftreg,time.time()+c.badUserBanDuration)
except:
self.log.info('Error with %s' % reg)
elif isClone:
s = 'kban %s %s %s %s' % (channel,msg.nick,c.badUserBanDuration,c.badUserMessage)
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(mask, time.time()+c.badUserBanDuration)
check = True
if s:
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error with %s' % s)
if s.startswith('kban'):
c.attacks.enqueue([channel])
if c.attacks.len([channel]) > 3 and not 'r' in irc.state.channels[channel].modes:
irc.sendMsg(ircmsgs.IrcMsg('MODE %s +r-z+q $~a' % channel))
underAttack = True
c.attacks.reset([channel])
if c.opChannel in irc.state.channels:
s = 'ops %s emergency modes for 15 minutes (+r-z+q $~a)' % channel
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error with %s' % s)
def ua():
irc.sendMsg(ircmsgs.IrcMsg('MODE %s -r+z-q $~a' % channel))
schedule.addEvent(ua,time.time()+900)
if isClone:
for user in a:
if user != hostmask:
fp = 'fpart %s %s %s' % (channel,user.split('!')[0],c.badUserMessage)
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),fp.split(' '))
except:
self.log.info('Error with %s' % fp)
if c.opChannel in irc.state.channels and isClone and not underAttack and len(a) > 1:
s = 'ops %s %s clones attacks: %s' % (channel,len(a),', '.join(a))
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error with %s' % s)
if c.logChannel in irc.state.channels and not isClone and not underAttack and not ircdb.ignores.checkIgnored(hostmask):
s = None
m = None
if isAction:
s = '* %s %s' % (msg.nick,text)
else:
s = '<%s> %s' % (msg.nick,text)
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:
m = '+m'
else:
if 'z' in irc.state.channels[channel].modes:
for ban in irc.state.channels[channel].bans:
if ircutils.hostmaskPatternEqual(ban,msg.prefix):
m = '+b'
break
if not m:
for quiet in irc.state.channels[channel].quiets:
if ircutils.hostmaskPatternEqual(quiet,msg.prefix):
m = '+q'
break
if m:
s = '[%s](%s) %s' % (channel,m,s)
irc.queueMsg(ircmsgs.privmsg(c.logChannel,s))
raw = '%s %s %s' % (msg.prefix,mask,text)
warned = False
if msg.addressed and c.commandCheck:
key = msg.addressed.split(' ')[0]
c.commandQueue.enqueue([key])
if c.commandQueue.len([key]) > c.commandPermit:
s = 'channel disable %s' % key
c.commandQueue.reset([key])
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
s = 'channel enable %s' % key
def ui():
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
except:
self.log.info('Error with %s in %s' % (s,channel))
schedule.addEvent(ui,time.time()+c.commandDisableDuration)
except:
self.log.info('Error with %s in %s' % (s,channel))
if not check and not isClone and not underAttack and not ircdb.ignores.checkIgnored(hostmask):
for item in c.regexps:
if item[3] == 'TEXT':
message = item[2].lstrip().rstrip()
if item[4].search(raw):
message = message.replace('$channel',channel)
message = message.replace('$nick','"%s"' % msg.nick)
message = message.replace('$hostmask',msg.prefix)
message = message.replace('$mask',mask)
message = message.replace('$text',text)
if message.find('$randomNick') != -1:
a = []
for user in irc.state.channels[channel].users:
a.append(user)
message = message.replace('$randomNick','"%s"' % a[int(len(a)*random.random())])
message = message.lstrip()
a = message.split(' ')
a[0] = a[0].lstrip()
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),a)
except:
self.log.info('Error with #%s in %s' % (item[0],channel))
elif item[3] == 'WARN' and not warned:
cu = self._getUser(irc,channel,msg.prefix)
if cu:
if item[4].search(raw):
message = item[2]
a = message.split(' ')
if a[0].lstrip().rstrip().isdigit():
i = int(a[0].lstrip().rstrip())
n = cu.warns+1
if n == i:
cu.warns = cu.warns+1
warned = True
a.pop(0)
message = ' '.join(a).lstrip().rstrip()
message = message.replace('$channel',channel)
message = message.replace('$nick','"%s"' % msg.nick)
message = message.replace('$hostmask',msg.prefix)
message = message.replace('$mask',mask)
message = message.replace('$text',text)
if message.find('$randomNick') != -1:
a = []
for user in irc.state.channels[channel].users:
a.append(user)
message = message.replace('$randomNick','"%s"' % a[int(len(a)*random.random())])
message = message.lstrip()
a = message.split(' ')
a[0] = a[0].lstrip().rstrip()
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),a)
except:
self.log.info('Error with #%s in %s' % (item[0],channel))
def doNotice(self, irc, msg):
if ircmsgs.isCtcp(msg):
self.log.info('CTCP received from %s' % msg.prefix)
(recipients, text) = msg.args
if msg.nick == irc.nick:
return
try:
hostmask = irc.state.nickToHostmask(msg.nick)
mask = getmask(irc,msg.nick)
bot = irc.state.nickToHostmask(irc.nick)
except:
return
if not mask:
return
for channel in recipients.split(','):
if irc.isChannel(channel):
self._doLog(irc,channel,msg.prefix,'-%s- %s' % (msg.nick,text))
c = self._getChan(irc,channel)
if c.synchro:
if c.noticeCheck:
key = [mask]
s = ''
c.noticeQueue.enqueue(key)
if c.noticeQueue.len(key) > c.noticePermit:
c.noticeQueue.reset(key)
s = 'quiet %s %s %s %s' % (channel,msg.nick,c.noticeQuietDuration,c.noticeMessage)
c.badUserQueue.enqueue(key)
if c.badUserQueue.len(key) > c.badUserPermit:
c.badUserQueue.reset(key)
s = 'kban %s %s %s %s' % (channel,msg.nick,c.badUserBanDuration,c.badUserMessage)
else:
if not ircdb.checkCapability(msg.prefix, 'owner'):
ircdb.ignores.add(mask, time.time()+c.noticeQuietDuration)
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
if s.startswith('kban'):
a = self._check(irc,channel,mask)
if a and len(a) > 1:
for u in a:
s = 'kick %s %s %s' % (channel,u.split('!')[0],c.badUserMessage)
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),s.split(' '))
if c.logChannel in irc.state.channels:
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'[%s] -%s- %s' % (channel,msg.prefix,text)))
def doNick(self, irc, msg):
oldNick = msg.nick
newNick = msg.args[0]
(n, u, h) = ircutils.splitHostmask(msg.prefix)
prefix = ircutils.joinHostmask(newNick,u,h)
for (channel, ch) in irc.state.channels.iteritems():
if newNick in ch.users:
c = self._getChan(irc,channel)
if oldNick in c.nicks:
c.nicks[newNick] = c.nicks[oldNick]
c.nicks[newNick].host = msg.prefix
del c.nicks[oldNick]
self._doLog(irc,channel,prefix,
'*** %s is now known as %s' % (oldNick,newNick))
if c.synchro:
for item in c.regexps:
if item[3] == 'NICK':
message = item[2]
if item[4].search(prefix):
message = message.replace('$nick','"%s"' % newNick)
message = message.replace('$hostmask',msg.prefix)
message = message.replace('$channel',channel)
message = message.replace('$mask',mask)
if message.find('$randomNick') != -1:
a = []
for user in irc.state.channels[channel].users:
a.append(user)
message = message.replace('$randomNick','"%s"' % a[int(len(a)*random.random())])
message = message.lstrip()
try:
self.Proxy(irc,ircmsgs.IrcMsg(prefix=irc.prefix,msg=msg),message.split(' '))
except:
self.log.info('Error with #%s in %s' % (item[0],channel))
def doMode(self, irc, msg):
channel = msg.args[0]
if irc.isChannel(channel) and msg.args[1:]:
self._doLog(irc,channel,msg.prefix,
'*** %s sets mode: %s %s' % (msg.nick,msg.args[1],' '.join(msg.args[2:])))
try:
mask = irc.state.nickToHostmask(msg.nick)
except:
return
c = self._getChan(irc,channel)
now = time.time()
if channel in irc.state.channels and msg.args[1:]:
if irc.state.channels[channel].synchro:
modes = ircutils.separateModes(msg.args[1:])
a = []
for mode in modes:
(kind,value) = mode
id = None
if '+' in kind:
if 'b' in kind:
if not value in c.activeBans:
id = self._addban(irc,channel,msg.prefix,'b',value,0)
self._addbanaffects(irc,channel,id,'b',value)
c.activeBans[str(value)] = (id,msg.prefix,'b',value,now,now)
if msg.nick != irc.nick and msg.nick != 'ChanServ':
try:
user = ircdb.users.getUser(msg.prefix)
except KeyError:
user = None
if user:
irc.queueMsg(ircmsgs.privmsg(msg.nick,"About [#%s +b %s in %s] you can use !banmark %s or !banedit %s" % (id,value,channel,id,id)))
a.append(self._summaryBan(irc,channel,msg.prefix,id,kind[1],kind[0],value,now,now,None))
else:
(id,by,k,mask,begin,end) = c.activeBans[str(value)]
a.append(self._summaryBan(irc,channel,by,id,kind[1],kind[0],mask,begin,end,None))
chan = ircdb.channels.getChannel(channel)
chan.addIgnore(value, 0)
ircdb.channels.setChannel(channel, chan)
elif 'q' in kind:
if not value in c.activeQuiets:
id = self._addban(irc,channel,msg.prefix,'q',value,0)
self._addbanaffects(irc,channel,id,'q',value)
c.activeQuiets[str(value)] = (id,msg.prefix,'q',value,now,now)
if msg.nick != irc.nick and msg.nick != 'ChanServ':
try:
user = ircdb.users.getUser(msg.prefix)
except KeyError:
user = None
if user:
irc.queueMsg(ircmsgs.privmsg(msg.nick,"About [#%s +q %s in %s] you can use !banmark %s or !banedit %s" % (id,value,channel,id,id)))
a.append(self._summaryBan(irc,channel,msg.prefix,id,kind[1],kind[0],value,now,now,None))
else:
(id,by,k,mask,begin,end) = c.activeQuiets[str(value)]
a.append(self._summaryBan(irc,channel,by,id,kind[1],kind[0],mask,begin,end,None))
chan = ircdb.channels.getChannel(channel)
chan.addIgnore(value, 0)
ircdb.channels.setChannel(channel, chan)
else:
if value:
a.append('%s %s' % (kind,value))
else:
a.append('%s' % kind)
else:
if 'b' in kind:
if value in c.activeBans:
(id,by,k,mask,begin,end) = c.activeBans[value]
self._endban(irc,channel,msg.prefix,id)
per = msg.prefix
if id in c.removed:
per = c.removed[id]
s = self._summaryBan(irc,channel,per,id,kind[1],kind[0],mask,begin,end,now)
if per != msg.nick:
a.append(s)
else:
s = s.replace('by %s' % msg.nick,'')
a.append(s)
if mask in c.pendingBans:
name = c.pendingBans[mask]
if name in c.schedules:
try:
schedule.removeEvent(name)
del c.schedules[name]
except:
del c.schedules[name]
del c.pendingBans[mask]
del c.activeBans[mask]
else:
a.append('%s %s' % (kind,value))
chan = ircdb.channels.getChannel(channel)
try:
chan.removeIgnore(value)
ircdb.channels.setChannel(channel, chan)
except KeyError:
self.log.info('error when removing %s of %s ignore list' % (value,channel))
elif 'q' in kind:
if value in c.activeQuiets:
(id,by,k,mask,begin,end) = c.activeQuiets[value]
self._endban(irc,channel,msg.prefix,id)
per = msg.prefix
if id in c.removed:
per = c.removed[id]
s = self._summaryBan(irc,channel,per,id,kind[1],kind[0],mask,begin,end,now)
if per != msg.nick:
a.append(s)
else:
s = s.replace('by %s' % msg.nick,'')
a.append(s)
if mask in c.pendingQuiets:
name = c.pendingQuiets[mask]
if name in c.schedules:
try:
schedule.removeEvent(name)
del c.schedules[name]
except:
del c.schedules[name]
del c.pendingQuiets[mask]
del c.activeQuiets[mask]
else:
a.append('%s %s' % (kind,value))
chan = ircdb.channels.getChannel(channel)
try:
chan.removeIgnore(value)
ircdb.channels.setChannel(channel, chan)
except KeyError:
self.log.info('error when removing %s of %s ignore list' % (value,channel))
else:
if value:
a.append('%s %s' % (kind,value))
else:
a.append('%s' % kind)
if c.logChannel in irc.state.channels:
if len(' '.join(a)) > 380:
i = 0
while i < len(a):
r = [a[i],a[int(i+1)]]
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s sets mode in %s : %s' % (msg.nick,channel,' '.join(r))))
i = i+2
else:
irc.queueMsg(ircmsgs.privmsg(c.logChannel,'%s sets mode in %s : %s' % (msg.nick,channel,' '.join(a))))
def do341(self, irc, msg):
(_, nick, channel) = msg.args
nick = ircutils.toLower(nick)
replyIrc = self.invites.pop((irc, nick), None)
if replyIrc is not None:
self.log.info('Inviting %s to %s by command of %s.',
nick, channel, replyIrc.msg.prefix)
replyIrc.replySuccess()
else:
self.log.info('Inviting %s to %s.', nick, channel)
def do443(self, irc, msg):
(_, nick, channel, _) = msg.args
nick = ircutils.toLower(nick)
replyIrc = self.invites.pop((irc, nick), None)
if replyIrc is not None:
replyIrc.error(format('%s is already in %s.', nick, channel))
def do401(self, irc, msg):
nick = msg.args[1]
nick = ircutils.toLower(nick)
replyIrc = self.invites.pop((irc, nick), None)
if replyIrc is not None:
replyIrc.error(format('There is no %s on this network.', nick))
def do504(self, irc, msg):
nick = msg.args[1]
nick = ircutils.toLower(nick)
replyIrc = self.invites.pop((irc, nick), None)
if replyirc is not None:
replyIrc.error(format('There is no %s on this server.', nick))
class lobotomy(callbacks.Commands):
def add(self, irc, msg, args, channel):
"""[<channel>]
If you have the #channel,op capability, this will "lobotomize" the
bot, making it silent and unanswering to all requests made in the
channel. <channel> is only necessary if the message isn't sent in
the channel itself.
"""
c = ircdb.channels.getChannel(channel)
c.lobotomized = True
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
add = wrap(add, ['op'])
def remove(self, irc, msg, args, channel):
"""[<channel>]
If you have the #channel,op capability, this will unlobotomize the
bot, making it respond to requests made in the channel again.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
c = ircdb.channels.getChannel(channel)
c.lobotomized = False
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
remove = wrap(remove, ['op'])
def list(self, irc, msg, args):
"""takes no arguments
Returns the channels in which this bot is lobotomized.
"""
L = []
for (channel, c) in ircdb.channels.iteritems():
if c.lobotomized:
chancap = ircdb.makeChannelCapability(channel, 'op')
if ircdb.checkCapability(msg.prefix, 'admin') or \
ircdb.checkCapability(msg.prefix, chancap) or \
(channel in irc.state.channels and \
msg.nick in irc.state.channels[channel].users):
L.append(channel)
if L:
L.sort()
s = format('I\'m currently lobotomized in %L.', L)
irc.reply(s)
else:
irc.reply('I\'m not currently lobotomized in any channels '
'that you\'re in.')
list = wrap(list)
class ban(callbacks.Commands):
def add(self, irc, msg, args, channel, banmask, expires):
"""[<channel>] <nick|hostmask> [<expires>]
If you have the #channel,op capability, this will effect a
persistent ban from interacting with the bot on the given
<hostmask> (or the current hostmask associated with <nick>. Other
plugins may enforce this ban by actually banning users with
matching hostmasks when they join. <expires> is an optional
argument specifying when (in "seconds from now") the ban should
expire; if none is given, the ban will never automatically expire.
<channel> is only necessary if the message isn't sent in the
channel itself.
"""
c = ircdb.channels.getChannel(channel)
c.addBan(banmask, expires)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
add = wrap(add, ['op', 'banmask', additional('expiry', 0)])
def remove(self, irc, msg, args, channel, banmask):
"""[<channel>] <hostmask>
If you have the #channel,op capability, this will remove the
persistent ban on <hostmask>. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
c = ircdb.channels.getChannel(channel)
try:
c.removeBan(banmask)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
except KeyError:
irc.error('There are no persistent bans for that hostmask.')
remove = wrap(remove, ['op', 'hostmask'])
def list(self, irc, msg, args, channel):
"""[<channel>]
If you have the #channel,op capability, this will show you the
current persistent bans on #channel.
"""
c = ircdb.channels.getChannel(channel)
if c.bans:
bans = []
for ban in c.bans:
if c.bans[ban]:
bans.append(format('%q (expires %t)',
ban, c.bans[ban]))
else:
bans.append(format('%q (never expires)',
ban, c.bans[ban]))
irc.reply(format('%L', bans))
else:
irc.reply(format('There are no persistent bans on %s.',
channel))
list = wrap(list, ['op'])
class ignore(callbacks.Commands):
def add(self, irc, msg, args, channel, banmask, expires):
"""[<channel>] <nick|hostmask> [<expires>]
If you have the #channel,op capability, this will set a persistent
ignore on <hostmask> or the hostmask currently
associated with <nick>. <expires> is an optional argument
specifying when (in "seconds from now") the ignore will expire; if
it isn't given, the ignore will never automatically expire.
<channel> is only necessary if the message isn't sent in the
channel itself.
"""
c = ircdb.channels.getChannel(channel)
c.addIgnore(banmask, expires)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
add = wrap(add, ['op', 'banmask', additional('expiry', 0)])
def remove(self, irc, msg, args, channel, banmask):
"""[<channel>] <hostmask>
If you have the #channel,op capability, this will remove the
persistent ignore on <hostmask> in the channel. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
c = ircdb.channels.getChannel(channel)
try:
c.removeIgnore(banmask)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
except KeyError:
irc.error('There are no ignores for that hostmask.')
remove = wrap(remove, ['op', 'hostmask'])
def list(self, irc, msg, args, channel):
"""[<channel>]
Lists the hostmasks that the bot is ignoring on the given channel.
<channel> is only necessary if the message isn't sent in the
channel itself.
"""
# XXX Add the expirations.
c = ircdb.channels.getChannel(channel)
if len(c.ignores) == 0:
s = format('I\'m not currently ignoring any hostmasks in %q',
channel)
irc.reply(s)
else:
L = sorted(c.ignores)
irc.reply(utils.str.commaAndify(map(repr, L)))
list = wrap(list, ['op'])
class capability(callbacks.Commands):
def add(self, irc, msg, args, channel, user, capabilities):
"""[<channel>] <nick|username> <capability> [<capability> ...]
If you have the #channel,op capability, this will give the user
<name> (or the user to whom <nick> maps)
the capability <capability> in the channel. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
for c in capabilities.split():
c = ircdb.makeChannelCapability(channel, c)
user.addCapability(c)
ircdb.users.setUser(user)
irc.replySuccess()
add = wrap(add, ['op', 'otherUser', 'capability'])
def remove(self, irc, msg, args, channel, user, capabilities):
"""[<channel>] <name|hostmask> <capability> [<capability> ...]
If you have the #channel,op capability, this will take from the
user currently identified as <name> (or the user to whom <hostmask>
maps) the capability <capability> in the channel. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
fail = []
for c in capabilities.split():
cap = ircdb.makeChannelCapability(channel, c)
try:
user.removeCapability(cap)
except KeyError:
fail.append(c)
ircdb.users.setUser(user)
if fail:
s = 'capability'
if len(fail) > 1:
s = utils.str.pluralize(s)
irc.error(format('That user didn\'t have the %L %s.', fail, s),
Raise=True)
irc.replySuccess()
remove = wrap(remove, ['op', 'otherUser', 'capability'])
# XXX This needs to be fix0red to be like Owner.defaultcapability. Or
# something else. This is a horrible interface.
def setdefault(self, irc, msg, args, channel, v):
"""[<channel>] {True|False}
If you have the #channel,op capability, this will set the default
response to non-power-related (that is, not {op, halfop, voice}
capabilities to be the value you give. <channel> is only necessary
if the message isn't sent in the channel itself.
"""
c = ircdb.channels.getChannel(channel)
if v:
c.setDefaultCapability(True)
else:
c.setDefaultCapability(False)
ircdb.channels.setChannel(channel, c)
irc.replySuccess()
setdefault = wrap(setdefault, ['op', 'boolean'])
def set(self, irc, msg, args, channel, capabilities):
"""[<channel>] <capability> [<capability> ...]
If you have the #channel,op capability, this will add the channel
capability <capability> for all users in the channel. <channel> is
only necessary if the message isn't sent in the channel itself.
"""
chan = ircdb.channels.getChannel(channel)
for c in capabilities:
chan.addCapability(c)
ircdb.channels.setChannel(channel, chan)
irc.replySuccess()
set = wrap(set, ['op', many('capability')])
def unset(self, irc, msg, args, channel, capabilities):
"""[<channel>] <capability> [<capability> ...]
If you have the #channel,op capability, this will unset the channel
capability <capability> so each user's specific capability or the
channel default capability will take precedence. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
chan = ircdb.channels.getChannel(channel)
fail = []
for c in capabilities:
try:
chan.removeCapability(c)
except KeyError:
fail.append(c)
ircdb.channels.setChannel(channel, chan)
if fail:
s = 'capability'
if len(fail) > 1:
s = utils.str.pluralize(s)
irc.error(format('I do not know about the %L %s.', fail, s),
Raise=True)
irc.replySuccess()
unset = wrap(unset, ['op', many('capability')])
def list(self, irc, msg, args, channel):
"""[<channel>]
Returns the capabilities present on the <channel>. <channel> is
only necessary if the message isn't sent in the channel itself.
"""
c = ircdb.channels.getChannel(channel)
L = sorted(c.capabilities)
irc.reply(' '.join(L))
list = wrap(list, ['channel'])
def disable(self, irc, msg, args, channel, plugin, command):
"""[<channel>] [<plugin>] [<command>]
If you have the #channel,op capability, this will disable the <command>
in <channel>. If <plugin> is provided, <command> will be disabled only
for that plugin. If only <plugin> is provided, all commands in the
given plugin will be disabled. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
chan = ircdb.channels.getChannel(channel)
failMsg = ''
if plugin:
s = '-%s' % plugin.name()
if command:
if plugin.isCommand(command):
s = '-%s.%s' % (plugin.name(), command)
else:
failMsg = format('The %s plugin does not have a command '
'called %s.', plugin.name(), command)
elif command:
# findCallbackForCommand
if filter(None, irc.findCallbacksForArgs([command])):
s = '-%s' % command
else:
failMsg = format('No plugin or command named %s could be '
'found.', command)
else:
raise callbacks.ArgumentError
if failMsg:
irc.error(failMsg)
else:
chan.addCapability(s)
ircdb.channels.setChannel(channel, chan)
irc.replySuccess()
disable = wrap(disable, ['op',
optional(('plugin', False)),
additional('commandName')])
def enable(self, irc, msg, args, channel, plugin, command):
"""[<channel>] [<plugin>] [<command>]
If you have the #channel,op capability, this will enable the <command>
in <channel> if it has been disabled. If <plugin> is provided,
<command> will be enabled only for that plugin. If only <plugin> is
provided, all commands in the given plugin will be enabled. <channel>
is only necessary if the message isn't sent in the channel itself.
"""
chan = ircdb.channels.getChannel(channel)
failMsg = ''
if plugin:
s = '-%s' % plugin.name()
if command:
if plugin.isCommand(command):
s = '-%s.%s' % (plugin.name(), command)
else:
failMsg = format('The %s plugin does not have a command '
'called %s.', plugin.name(), command)
elif command:
# findCallbackForCommand
if filter(None, irc.findCallbacksForArgs([command])):
s = '-%s' % command
else:
failMsg = format('No plugin or command named %s could be '
'found.', command)
else:
raise callbacks.ArgumentError
if failMsg:
irc.error(failMsg)
else:
fail = []
try:
chan.removeCapability(s)
except KeyError:
fail.append(s)
ircdb.channels.setChannel(channel, chan)
if fail:
irc.error(format('%s was not disabled.', s[1:]))
else:
irc.replySuccess()
enable = wrap(enable, ['op',
optional(('plugin', False)),
additional('commandName')])
def nicks(self, irc, msg, args, channel):
"""[<channel>]
Returns the nicks in <channel>. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
# Make sure we don't elicit information about private channels to
# people or channels that shouldn't know
if 's' in irc.state.channels[channel].modes and \
msg.args[0] != channel and \
(ircutils.isChannel(msg.args[0]) or \
msg.nick not in irc.state.channels[channel].users):
irc.error('You don\'t have access to that information.')
L = list(irc.state.channels[channel].users)
utils.sortBy(str.lower, L)
irc.reply(utils.str.commaAndify(L))
nicks = wrap(nicks, ['inChannel'])
def alertOps(self, irc, channel, s, frm=None):
"""Internal message for notifying all the #channel,ops in a channel of
a given situation."""
capability = ircdb.makeChannelCapability(channel, 'op')
s = format('Alert to all %s ops: %s', channel, s)
if frm is not None:
s += format(' (from %s)', frm)
for nick in irc.state.channels[channel].users:
hostmask = irc.state.nickToHostmask(nick)
if ircdb.checkCapability(hostmask, capability):
irc.reply(s, to=nick, private=True)
def alert(self, irc, msg, args, channel, text):
"""[<channel>] <text>
Sends <text> to all the users in <channel> who have the <channel>,op
capability.
"""
self.alertOps(irc, channel, text, frm=msg.nick)
alert = wrap(alert, ['op', 'text'])
Class = Channel
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
| Posted by niko on Tue 2 Mar 2010 20:43 | 234 views
- Syntax: None
- Expires: never
- Report
- IMG
- Download
-
|
