def doPrivmsg(self, irc, msg): assert self is irc.callbacks[0], \ 'Owner isn\'t first callback: %r' % irc.callbacks if ircmsgs.isCtcp(msg): return s = callbacks.addressed(irc.nick, msg) if s: ignored = ircdb.checkIgnored(msg.prefix) if ignored: self.log.info('Ignoring command from %s.', msg.prefix) return maximum = conf.supybot.abuse.flood.command.maximum() self.commands.enqueue(msg) if conf.supybot.abuse.flood.command() \ and self.commands.len(msg) > maximum \ and not ircdb.checkCapability(msg.prefix, 'owner') \ and not ircdb.checkCapability(msg.prefix, 'admin'): punishment = conf.supybot.abuse.flood.command.punishment() banmask = ircutils.banmask(msg.prefix) self.log.info('Ignoring %s for %s seconds due to an apparent ' 'command flood.', banmask, punishment) ircdb.ignores.add(banmask, time.time() + punishment) irc.reply('You\'ve given me %s commands within the last ' 'minute; I\'m now ignoring you for %s. Try #fedora-botgames.' % (maximum, utils.timeElapsed(punishment, seconds=False))) return try: tokens = callbacks.tokenize(s, channel=msg.args[0]) self.Proxy(irc, msg, tokens) except SyntaxError, e: irc.queueMsg(callbacks.error(msg, str(e)))
def getUser(self, **kw): """ will return a user object tagged with a hostmask for use or None """ if 'protocol' not in kw: raise KeyError, 'Need a protocol name' else: user = None if 'username' not in kw: raise KeyError, 'Need a username' try: user = ircdb.users.getUser(kw['username']) except KeyError: return False cap = self.registryValue('capability') pcap = self.registryValue('%s.capability' % kw['protocol']) if cap: if not ircdb.checkCapability(kw['username'], cap): return False if pcap: if not ircdb.checkCapability(kw['username'], pcap): return False if 'password' in kw: if not user.checkPassword(kw['password']): return False elif 'blob' in kw: if not self.checkKey(kw['username'], kw['blob']): return False else: return False user.gwhm = self.buildHostmask(kw['username'], kw['protocol'], kw['peer']) user.addAuth(user.gwhm) return user
def bombsenabled(self, irc, msg, args, channel, value): """[value] Sets the value of the allowBombs config value for the channel. Restricted to users with channel timebombadmin capability.""" statusDescription = "are currently" if value: # tmp = ircdb.channels.getChannel(channel).defaultAllow - problems with multithreading? # ircdb.channels.getChannel(channel).defaultAllow = False hasCap = ircdb.checkCapability(msg.prefix, "timebombadmin") if (channel == "#powder" or channel == "#powder-dev") and not ircdb.checkCapability(msg.prefix, "admin"): irc.error("You need the admin capability to do that") return # ircdb.channels.getChannel(channel).defaultAllow = tmp if hasCap: oldValue = self.registryValue("allowBombs", channel) try: conf.supybot.plugins.Timebomb.allowBombs.get(channel).set(value) except registry.InvalidRegistryValue: irc.error("Value must be either True or False (or On or Off)") return if self.registryValue("allowBombs", channel) == oldValue: statusDescription = "were already" else: statusDescription = "have now been" else: irc.error("You need the timebombadmin capability to do that") return if self.registryValue("allowBombs", channel): irc.reply("Timebombs {} enabled in{}".format(statusDescription, channel)) else: irc.reply("Timebombs {} disabled in{}".format(statusDescription, channel))
def floodPunish(self, irc, msg, floodType, dummy = False): channel = msg.args[0] if (not irc.nick in irc.state.channels[channel].ops) and\ (not irc.nick in irc.state.channels[channel].halfops): self.log.warning("%s flooded in %s, but not opped.",\ msg.nick, channel) return if msg.nick in self.immunities: self.log.debug("Not punnishing %s, they are immune.", msg.nick) return if msg.nick in irc.state.channels[channel].ops or\ msg.nick in irc.state.channels[channel].halfops or\ msg.nick in irc.state.channels[channel].voices: self.log.debug("%s flooded in %s. But"\ + " I will not punish them because they have"\ + " special access.", msg.nick, channel) return if ircdb.checkCapability(msg.prefix, 'trusted') or\ ircdb.checkCapability(msg.prefix, 'admin') or\ ircdb.checkCapability(msg.prefix, channel + ',op'): self.log.debug("%s flooded in %s. But"\ + " I will not punish them because they are"\ + " trusted.", msg.nick, channel) return if msg.host in self.offenses and self.offenses[msg.host] > 2: hostmask = irc.state.nickToHostmask(msg.nick) banmaskstyle = conf.supybot.protocols.irc.banmask banmask = banmaskstyle.makeBanmask(hostmask) if not dummy: irc.queueMsg(ircmsgs.ban(channel, banmask)) self.log.warning("Banned %s (%s) from %s for repeated"\ + " flooding.", banmask, msg.nick, channel) reason = floodType + " flood detected." if floodType == "Paste": reason += " Use a pastebin like pastebin.ubuntu.com or gist.github.com." if not dummy: irc.queueMsg(ircmsgs.kick(channel, msg.nick, reason)) self.log.warning("Kicked %s from %s for %s flooding.",\ msg.nick, channel, floodType) # Don't schedule the same nick twice if not (msg.host in self.offenses): schedule.addEvent(self.clearOffenses, time.time()+300, args=[msg.host]) self.offenses[msg.host] = 0 # Incremented below self.offenses[msg.host] += 1 self.immunities[msg.nick] = True schedule.addEvent(self.unImmunify, time.time()+3, args=[msg.nick])
def _kban(self, irc, msg, args, bannedNick, reason): # Check that they're not trying to make us kickban ourself. channel = msg.args[0] if not irc.isNick(bannedNick[0]): self.log.warning('%q tried to kban a non nick: %q', msg.prefix, bannedNick) raise callbacks.ArgumentError elif bannedNick == irc.nick: self.log.warning('%q tried to make me kban myself.', msg.prefix) irc.error('I cowardly refuse to kickban myself.') return if not reason: reason = msg.nick try: bannedHostmask = irc.state.nickToHostmask(bannedNick) except KeyError: irc.error(format('I haven\'t seen %s.', bannedNick), Raise=True) capability = ircdb.makeChannelCapability(channel, 'op') banmaskstyle = conf.supybot.protocols.irc.banmask banmask = banmaskstyle.makeBanmask(bannedHostmask, ["host", "user"]) # Check (again) that they're not trying to make us kickban ourself. if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(bannedHostmask, irc.prefix): self.log.warning('%q tried to make me kban myself.',msg.prefix) irc.error('I cowardly refuse to ban myself.') return else: self.log.warning('Using exact hostmask since banmask would ' 'ban myself.') banmask = bannedHostmask # Now, let's actually get to it. Check to make sure they have # #channel,op and the bannee doesn't have #channel,op; or that the # bannee and the banner are both the same person. def doBan(): if irc.state.channels[channel].isOp(bannedNick): irc.queueMsg(ircmsgs.deop(channel, bannedNick)) irc.queueMsg(ircmsgs.ban(channel, banmask)) irc.queueMsg(ircmsgs.kick(channel, bannedNick, reason)) def f(): if channel in irc.state.channels and \ banmask in irc.state.channels[channel].bans: irc.queueMsg(ircmsgs.unban(channel, banmask)) schedule.addEvent(f, 3600) if bannedNick == msg.nick: doBan() elif ircdb.checkCapability(msg.prefix, capability): if ircdb.checkCapability(bannedHostmask, capability): self.log.warning('%s tried to ban %q, but both have %s', msg.prefix, bannedHostmask, capability) irc.error(format('%s has %s too, you can\'t ban him/her/it.', bannedNick, capability)) else: doBan() else: self.log.warning('%q attempted kban without %s', msg.prefix, capability) irc.errorNoCapability(capability) exact,nick,user,host
def votes(self, irc, msg, args, channel, pid): """[channel] <id> Retrieves the vote count for a poll. """ if channel and msg.args[0] in irc.state.channels: if msg.args[0] != channel: if ircdb.checkCapability(msg.prefix, 'admin') or ircdb.checkCapability(msg.prefix, 'owner'): irc.error("Not Implemented") else: irc.errorInvalid('argument', channel) elif msg.args[0] == channel: irc.error("Not Implemented")
def do(type): cap = ircdb.makeChannelCapability(channel, type) cap_auto = ircdb.makeChannelCapability(channel, "auto" + type) try: apply_mode = ircdb.checkCapability( msg.prefix, cap, ignoreOwner=not self.registryValue("owner"), ignoreChannelOp=True, ignoreDefaultAllow=True, ) except KeyError: apply_mode = False if self.registryValue("alternativeCapabilities", channel): try: override = ircdb.checkCapability( msg.prefix, cap_auto, ignoreOwner=not self.registryValue("owner"), ignoreChannelOp=True, ignoreDefaultAllow=True, ) except KeyError: override = False else: override = False if apply_mode or override: if override or self.registryValue(type, channel): self.log.info("Scheduling auto-%s of %s in %s.", type, msg.prefix, channel) def dismiss(): """Determines whether or not a mode has already been applied.""" l = getattr(irc.state.channels[channel], type + "s") return msg.nick in l msgmaker = getattr(ircmsgs, type) schedule_msg(msgmaker(channel, msg.nick), dismiss) raise Continue # Even if fallthrough, let's only do one. elif not fallthrough: self.log.debug( "%s has %s, but supybot.plugins.AutoMode.%s" " is not enabled in %s, refusing to fall " "through.", msg.prefix, cap, type, channel, ) raise Continue
def checkAndAct (self,irc,prefix,chan,kind,items,text,msg): protected = ircdb.makeChannelCapability(chan.name, 'protected') if ircdb.checkCapability(prefix, protected): return for pattern in list(items.keys()): item = chan.kinds[kind][pattern] if item.enable == '1': for match in re.finditer(item.re, text): if match: act = item.action account = '' gecos = '' if prefix.split('!')[0] in chan.nicks: (prefix,account,gecos) = chan.nicks[prefix.split('!')[0]] act = act.replace('$nick',prefix.split('!')[0]) act = act.replace('$hostmask',prefix) act = act.replace('$account',account) act = act.replace('$username',gecos) act = act.replace('$id',str(item.uid)) act = act.replace('$channel',chan.name) act = act.replace('$*',text) if act.find(' :') != -1: a = text.split(' :') if len(a) > 1: act = act.replace('$text',text.split(' :')[1]) for (i, j) in enumerate(match.groups()): act = re.sub(r'\$' + str(i+1), match.group(i+1), act) self.act(irc,msg,chan.name,act,item.owner) break
def add(self, channel, user, id, option): db = self._getDb(channel) cursor = db.cursor() # Only the poll starter or an admin can add options cursor.execute("""SELECT started_by FROM polls WHERE id=%s""", id) if cursor.rowcount == 0: raise dbi.NoRecordError if not ((user.id == cursor.fetchone()[0]) or (ircdb.checkCapability(user.id, 'admin'))): raise PollError, \ 'That poll isn\'t yours and you aren\'t an admin.' # and NOBODY can add options once a poll has votes cursor.execute("""SELECT COUNT(user_id) FROM votes WHERE poll_id=%s""", id) if int(cursor.fetchone()[0]) != 0: raise PollError, 'Cannot add options to a poll with votes.' # Get the next highest id cursor.execute("""SELECT MAX(id)+1 FROM options WHERE poll_id=%s""", id) option_id = cursor.fetchone()[0] or 1 cursor.execute("""INSERT INTO options VALUES (%s, %s, %s)""", option_id, id, option) db.commit()
def remove(self, irc, msg, args, user, hostmask, password): """[<name>] [<hostmask>] [<password>] Removes the hostmask <hostmask> from the record of the user specified by <name>. If the hostmask given is 'all' then all hostmasks will be removed. The <password> may only be required if the user is not recognized by their hostmask. This message must be sent to the bot privately (not on a channel) since it may contain a password. If <hostmask> is not given, it defaults to your current hostmask. If <name> is not given, it defaults to your currently identified name. """ if not hostmask: hostmask = msg.prefix if not user.checkPassword(password) and \ not user.checkHostmask(msg.prefix): if not ircdb.checkCapability(msg.prefix, 'owner'): irc.error(conf.supybot.replies.incorrectAuthentication()) return try: s = '' if hostmask == 'all': user.hostmasks.clear() s = _('All hostmasks removed.') else: user.removeHostmask(hostmask) except KeyError: irc.error(_('There was no such hostmask.')) return ircdb.users.setUser(user) irc.replySuccess(s)
def _checkManageCapabilities(self, irc, msg, channel): """Check if the user has any of the required capabilities to manage the channel topic. The list of required capabilities is in requireManageCapability channel config. Also allow if the user is a chanop. Since they can change the topic manually anyway. """ c = irc.state.channels[channel] if msg.nick in c.ops or msg.nick in c.halfops or 't' not in c.modes: return True capabilities = self.registryValue('requireManageCapability', channel) if capabilities: for capability in re.split(r'\s*;\s*', capabilities): if capability.startswith('channel,'): capability = ircdb.makeChannelCapability( channel, capability[8:]) if capability and ircdb.checkCapability(msg.prefix, capability): return capabilities = self.registryValue('requireManageCapability', channel) irc.errorNoCapability(capabilities, Raise=True) else: return
def tell(self, irc, msg, args, target, text): """<nick> <text> Tells the <nick> whatever <text> is. Use nested commands to your benefit here. """ if target.lower() == 'me': target = msg.nick if ircutils.isChannel(target): irc.error('Dude, just give the command. No need for the tell.') return if not ircutils.isNick(target): irc.errorInvalid('nick', target) if ircutils.nickEqual(target, irc.nick): irc.error('You just told me, why should I tell myself?',Raise=True) if target not in irc.state.nicksToHostmasks and \ not ircdb.checkCapability(msg.prefix, 'owner'): # We'll let owners do this. s = 'I haven\'t seen %s, I\'ll let you do the telling.' % target irc.error(s, Raise=True) if irc.action: irc.action = False text = '* %s %s' % (irc.nick, text) s = '%s wants me to tell you: %s' % (msg.nick, text) irc.reply(s, to=target, private=True)
def nicks(self, irc, msg, args, channel, optlist): """[<channel>] [--count] Returns the nicks in <channel>. <channel> is only necessary if the message isn't sent in the channel itself. Returns only the number of nicks if --count option is provided. """ # Make sure we don't elicit information about private channels to # people or channels that shouldn't know capability = ircdb.makeChannelCapability(channel, 'op') hostmask = irc.state.nickToHostmask(msg.nick) if 's' in irc.state.channels[channel].modes and \ msg.args[0] != channel and \ not ircdb.checkCapability(hostmask, capability) 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.'), Raise=True) L = list(irc.state.channels[channel].users) keys = [option for (option, arg) in optlist] if 'count' not in keys: utils.sortBy(str.lower, L) private = self.registryValue("nicksInPrivate", channel) irc.reply(utils.str.commaAndify(L), private=private) else: irc.reply(str(len(L)))
def _preCheck(self, irc, msg, target, action): if self.registryValue('requireRegistration', target): try: foo = ircdb.users.getUser(msg.prefix) except KeyError: irc.errorNotRegistered(Raise=True) capability = self.registryValue('requireCapability', target) if capability: if not ircdb.checkCapability(msg.prefix, capability): irc.errorNoCapability(capability, Raise=True) if irc.isChannel(target): if self.registryValue('requirePresenceInChannel', target) and \ msg.nick not in irc.state.channels[target].users: irc.error(format(_('You must be in %s to %q in there.'), target, action), Raise=True) c = ircdb.channels.getChannel(target) if c.lobotomized: irc.error(format(_('I\'m lobotomized in %s.'), target), Raise=True) if not c._checkCapability(self.name()): irc.error(_('That channel has set its capabilities so as to ' 'disallow the use of this plugin.'), Raise=True) elif action == 'say' and not self.registryValue('allowPrivateTarget'): irc.error(format(_('%q cannot be used to send private messages.'), action), Raise=True)
def add(self, irc, msg, args, user, capability): """<name|hostmask> <capability> Gives the user specified by <name> (or the user to whom <hostmask> currently maps) the specified capability <capability> """ # Ok, the concepts that are important with capabilities: # ### 1) No user should be able to elevate their privilege to owner. ### 2) Admin users are *not* superior to #channel.ops, and don't ### have God-like powers over channels. ### 3) We assume that Admin users are two things: non-malicious and ### and greedy for power. So they'll try to elevate their ### privilege to owner, but they won't try to crash the bot for ### no reason. # Thus, the owner capability can't be given in the bot. Admin # users can only give out capabilities they have themselves (which # will depend on supybot.capabilities and its child default) but # generally means they can't mess with channel capabilities. if ircutils.strEqual(capability, 'owner'): irc.error(_('The "owner" capability can\'t be added in the ' 'bot. Use the supybot-adduser program (or edit the ' 'users.conf file yourself) to add an owner ' 'capability.')) return if ircdb.isAntiCapability(capability) or \ ircdb.checkCapability(msg.prefix, capability): user.addCapability(capability) ircdb.users.setUser(user) irc.replySuccess() else: irc.error(_('You can\'t add capabilities you don\'t have.'))
def delquote(self, irc, msg, args, optlist, qid): """[--channel <#channel>] <id> Delete the quote number 'id', only by the creator of the quote in the first 5 minutes or by an admin. If --channel is supplied the quote is fetched from that channel database.""" channel = msg.args[0] for (option, arg) in optlist: if option == 'channel': if not ircutils.isChannel(arg): irc.error(format(_('%s is not a valid channel.'), arg), Raise=True) channel = arg q = self.db.getQuoteById(channel, qid) if q is not None: if ircdb.checkCapability(msg.prefix, 'admin'): self.db.delQuoteById(channel, qid) irc.replySuccess() elif (time.time() - 300) <= q[3]: if q[2].lower() == msg.nick.lower(): self.db.delQuoteById(channel, qid) irc.replySuccess() else: irc.error(format(_("This quote only can be deleted by %s " "or an admin."), q[2])) else: irc.error(format(_("Too late, it has already passed 5 minutes." " Ask an admin."), qid, channel)) else: irc.error(format(_("No such quote %s in %s's database."), qid, channel))
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 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 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.devoices(channel, L) self._sendMsgs(irc, nicks, f) else: irc.errorNoCapability(capability)
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 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) self._sendMsgs(irc, nicks, f) else: irc.errorNoCapability(capability)
def _lock(self, irc, msg, channel, user, key, locking=True): #self.log.debug('in _lock') #self.log.debug('id: %s', id) id = user.id info = self.db.getFactinfo(channel, key) if not info: irc.error(format(_('No such factoid: %q'), key)) return (created_by, a, a, a, a, a, a, locked_by, a) = info # Don't perform redundant operations if locking and locked_by is not None: irc.error(format(_('Factoid %q is already locked.'), key)) return if not locking and locked_by is None: irc.error(format(_('Factoid %q is not locked.'), key)) return # Can only lock/unlock own factoids unless you're an admin #self.log.debug('admin?: %s', ircdb.checkCapability(id, 'admin')) #self.log.debug('created_by: %s', created_by) if not (ircdb.checkCapability(id, 'admin') or created_by == id): if locking: s = 'lock' else: s = 'unlock' irc.error(format(_('Cannot %s someone else\'s factoid unless you ' 'are an admin.'), s)) return # Okay, we're done, ready to lock/unlock if locking: self.db.lock(channel, key, id) else: self.db.unlock(channel, key) irc.replySuccess()
def boosterset(self, irc, msg, args, text): """<booster> Sets a new booster""" channell = msg.args[0] network = irc.network nick = msg.nick global targett global boosterr #if nick in self.registryValue('blacklist').split(' '): #irc.error("You've been blacklisted!", Raise=True if msg.args[0] in self.registryValue('channels').split(' '): global targett else: irc.error('Not a valid channel! Please contact Vlad', Raise=True) if nick in irc.state.channels[channell].ops: capability='ops' if not ircdb.checkCapability(msg.prefix, capability): irc.errorNoCapability(capability, Raise=True) target_line = linecache.getline(channell+network, 1).rstrip("\n") booster_line = linecache.getline(channell+network, 2).rstrip("\n") reason_line = linecache.getline(channell+network, 3).rstrip("\n") boosterrx = text oldfile = open(channell+network, 'w') oldfile.write(target_line+"\n") oldfile.write(boosterrx+"\n") oldfile.flush() linecache.clearcache() irc.reply('Done.')
def tell(self, irc, msg, args, target, text): """<nick> <text> Tells the <nick> whatever <text> is. Use nested commands to your benefit here. """ if irc.nested: irc.error("This command cannot be nested.", Raise=True) if target.lower() == "me": target = msg.nick if ircutils.isChannel(target): irc.error("Dude, just give the command. No need for the tell.") return if not ircutils.isNick(target): irc.errorInvalid("nick", target) if ircutils.nickEqual(target, irc.nick): irc.error("You just told me, why should I tell myself?", Raise=True) if target not in irc.state.nicksToHostmasks and not ircdb.checkCapability(msg.prefix, "owner"): # We'll let owners do this. s = "I haven't seen %s, I'll let you do the telling." % target irc.error(s, Raise=True) if irc.action: irc.action = False text = "* %s %s" % (irc.nick, text) s = "%s wants me to tell you: %s" % (msg.nick, text) irc.replySuccess() irc.reply(s, to=target, private=True)
def checkChangeAllowed(self, irc, msg, channel, user, record): if user.id == record.by: return True cap = ircdb.makeChannelCapability(channel, 'op') if ircdb.checkCapability(msg.prefix, cap): return True irc.errorNoCapability(cap)
def newpoll(self, irc, msg, args, channel, interval, answers, question): """<number of minutes for announce interval> <"answer,answer,..."> question Creates a new poll with the given question and answers. <channel> is only necessary if the message isn't sent in the channel itself.""" capability = ircdb.makeChannelCapability(channel, 'op') if not ircdb.checkCapability(msg.prefix, capability): irc.error('Need ops') return db = self.getDb(channel) cursor = db.cursor() self._execute_query(cursor, 'INSERT INTO polls VALUES (?,?,?,?,?)', None, datetime.datetime.now(), 1, None, question) pollid = cursor.lastrowid # used to add choices into db. each choice represented by character, starting at capital A (code 65) def genAnswers(): for i, answer in enumerate(answers, start=65): yield pollid, chr(i), answer cursor.executemany('INSERT INTO choices VALUES (?,?,?)', genAnswers()) db.commit() irc.reply('Started new poll #%s' % pollid) # function called by schedule event. can not have args def runPoll(): self._runPoll(irc, channel, pollid) # start schedule. will announce poll/choices to channel at interval schedule.addPeriodicEvent(runPoll, interval*60, name='%s_poll_%s' % (channel, pollid)) self.poll_schedules.append('%s_poll_%s' % (channel, pollid))
def checkChannelCapability(irc, msg, args, state, cap): if not state.channel: getChannel(irc, msg, args, state) cap = ircdb.canonicalCapability(cap) cap = ircdb.makeChannelCapability(state.channel, cap) if not ircdb.checkCapability(msg.prefix, cap): state.errorNoCapability(cap, Raise=True)
def inFilter(self, irc, msg): self.filtering = True # We need to check for bad words here rather than in doPrivmsg because # messages don't get to doPrivmsg if the user is ignored. if msg.command == 'PRIVMSG': channel = msg.args[0] self.updateRegexp(channel) s = ircutils.stripFormatting(msg.args[1]) if ircutils.isChannel(channel) and self.registryValue('kick', channel): if self.words and self.regexp.search(s): c = irc.state.channels[channel] cap = ircdb.makeChannelCapability(channel, 'op') if c.isHalfopPlus(irc.nick): if c.isHalfopPlus(msg.nick) or \ ircdb.checkCapability(msg.prefix, cap): self.log.debug("Not kicking %s from %s, because " "they are halfop+ or can't be " "kicked.", msg.nick, channel) else: message = self.registryValue('kick.message', channel) irc.queueMsg(ircmsgs.kick(channel, msg.nick, message)) else: self.log.warning('Should kick %s from %s, but not opped.', msg.nick, channel) return msg
def doPrivmsg(self, irc, msg): (recipients, text) = msg.args for channel in recipients.split(','): if irc.isChannel(channel): noLogPrefix = self.registryValue('noLogPrefix', channel) cap = ircdb.makeChannelCapability(channel, 'logChannelMessages') try: logChannelMessages = ircdb.checkCapability(msg.prefix, cap, ignoreOwner=True) except KeyError: logChannelMessages = True nick = msg.nick or irc.nick if msg.tagged('LogsToDB__relayed'): (nick, text) = text.split(' ', 1) nick = nick[1:-1] msg.args = (recipients, text) if (noLogPrefix and text.startswith(noLogPrefix)) or \ not logChannelMessages: text = '-= THIS MESSAGE NOT LOGGED =-' if ircmsgs.isAction(msg): self.doLog(irc, channel, '* %s %s\n', nick, ircmsgs.unAction(msg)) else: self.doLog(irc, channel, '<%s> %s\n', nick, text) message = msg.args[1] self.logViewerDB.add_message(msg.nick, msg.prefix, message, channel) self.logViewerFile.write_message(msg.nick, message)
def invalidCommand(self, irc, msg, tokens): assert not msg.repliedTo, 'repliedTo msg in Misc.invalidCommand.' assert self is irc.callbacks[-1], 'Misc isn\'t last callback.' self.log.debug('Misc.invalidCommand called (tokens %s)', tokens) channel = msg.args[0] # Only bother with the invaildCommand flood handling if it's actually # enabled if conf.supybot.abuse.flood.command.invalid(): # First, we check for invalidCommand floods. This is rightfully done # here since this will be the last invalidCommand called, and thus it # will only be called if this is *truly* an invalid command. maximum = conf.supybot.abuse.flood.command.invalid.maximum() banmasker = conf.supybot.protocols.irc.banmask.makeBanmask self.invalidCommands.enqueue(msg) if self.invalidCommands.len(msg) > maximum and \ not ircdb.checkCapability(msg.prefix, 'owner'): penalty = conf.supybot.abuse.flood.command.invalid.punishment() banmask = banmasker(msg.prefix) self.log.info('Ignoring %s for %s seconds due to an apparent ' 'invalid command flood.', banmask, penalty) if tokens and tokens[0] == 'Error:': self.log.warning('Apparent error loop with another Supybot ' 'observed. Consider ignoring this bot ' 'permanently.') ircdb.ignores.add(banmask, time.time() + penalty) if conf.supybot.abuse.flood.command.invalid.notify(): irc.reply('You\'ve given me %s invalid commands within ' 'the last minute; I\'m now ignoring you for %s.' % (maximum, utils.timeElapsed(penalty, seconds=False))) return # Now, for normal handling. if conf.get(conf.supybot.reply.whenNotCommand, channel): if len(tokens) >= 2: cb = irc.getCallback(tokens[0]) if cb: plugin = cb.name() irc.error(format('The %q plugin is loaded, but there is ' 'no command named %q in it. Try "list ' '%s" to see the commands in the %q ' 'plugin.', plugin, tokens[1], plugin, plugin)) else: irc.errorInvalid('command', tokens[0], repr=False) else: command = tokens and tokens[0] or '' irc.errorInvalid('command', command, repr=False) else: if tokens: # echo [] will get us an empty token set, but there's no need # to log this in that case anyway, it being a nested command. self.log.info('Not replying to %s, not a command.', tokens[0]) if irc.nested: bracketConfig = conf.supybot.commands.nested.brackets brackets = conf.get(bracketConfig, channel) if brackets: (left, right) = brackets irc.reply(left + ' '.join(tokens) + right) else: pass # Let's just do nothing, I can't think of better.
def list(self, irc, msg, args, name): """[<name>] Returns the hostmasks of the user specified by <name>; if <name> isn't specified, returns the hostmasks of the user calling the command. """ def getHostmasks(user): hostmasks = list(map(repr, user.hostmasks)) if hostmasks: hostmasks.sort() return format('%L', hostmasks) else: return format(_('%s has no registered hostmasks.'), user.name) try: user = ircdb.users.getUser(msg.prefix) if name: if name != user.name and \ not ircdb.checkCapability(msg.prefix, 'owner'): irc.error(_('You may only retrieve your own ' 'hostmasks.'), Raise=True) else: try: user = ircdb.users.getUser(name) irc.reply(getHostmasks(user)) except KeyError: irc.errorNoUser() else: irc.reply(getHostmasks(user)) except KeyError: irc.errorNotRegistered()
def _slot(self, lastItem): irc = lastItem.irc msg = lastItem.msg channel = lastItem.channel prefix = lastItem.prefix nick = prefix.split('!')[0] kind = lastItem.kind try: ircdb.users.getUser(msg.prefix) # May raise KeyError capability = self.registryValue('exempt') if capability: if ircdb.checkCapability(msg.prefix, capability): return except KeyError: pass punishment = self.registryValue('%s.punishment' % kind, channel) reason = _('%s flood detected') % kind if punishment == 'kick': msg = ircmsgs.kick(channel, nick, reason) irc.queueMsg(msg) elif punishment == 'ban': msg = ircmsgs.ban(channel, prefix) irc.queueMsg(msg) elif punishment == 'kban': msg = ircmsgs.kick(channel, nick, reason) irc.queueMsg(msg) msg = ircmsgs.ban(channel, prefix) irc.queueMsg(msg) elif punishment.startswith('mode'): msg = ircmsgs.mode(channel, punishment[len('mode'):]) irc.queueMsg(msg) elif punishment.startswith('command '): tokens = callbacks.tokenize(punishment[len('command '):]) self.Proxy(irc, msg, tokens)
def _setValue(self, irc, msg, group, value): capability = getCapability(group._name) if ircdb.checkCapability(msg.prefix, capability): # I think callCommand catches exceptions here. Should it? group.set(value) irc.replySuccess() else: irc.errorNoCapability(capability)
def remove(self, irc, msg, args, channel, hostmask): """<channel> <hostmask>""" if not ircdb.checkCapability(msg.prefix, 'admin'): irc.errorNoCapability('admin', Raise=True) channel = channel.lower() def predicate(entry): return entry.channel == channel and entry.hostmask == hostmask entry = next(self.db.select(predicate), None) if not entry: irc.reply('No greeting with that hostmask found') return self.db.remove(entry.id) irc.replySuccess()
def _voice(self, irc, msg, args, channel, nicks, fn): 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 fn(channel, L) self._sendMsgs(irc, nicks, f) else: irc.errorNoCapability(capability)
def _checkManageCapabilities(self, irc, msg, channel): """Check if the user has any of the required capabilities to manage the regexp database.""" capabilities = self.registryValue('requireManageCapability') if capabilities: for capability in re.split(r'\s*;\s*', capabilities): if capability.startswith('channel,'): capability = capability[8:] if channel != 'global': capability = ircdb.makeChannelCapability( channel, capability) if capability and ircdb.checkCapability( msg.prefix, capability): #print "has capability:", capability return True return False else: return True
def cutwire(self, irc, msg, args, channel, cutWire): """[<channel>] <color> Will cut the given wire if you've been bombed. """ channel = ircutils.toLower(channel) try: if not self.bombs[channel].active or self.bombs[channel].rethrown: return if not ircutils.nickEqual(self.bombs[channel].victim, msg.nick) and not ircdb.checkCapability( msg.prefix, "admin"): irc.reply("You can't cut the wire on someone else's bomb!") return self.bombs[channel].cutwire(irc, cutWire) except KeyError: pass irc.noReply()
def _getValue(self, irc, msg, group, addChannel=False): value = str(group) or ' ' if addChannel and irc.isChannel(msg.args[0]) and not irc.nested: s = str(group.get(msg.args[0])) value = _('Global: %s; %s: %s') % (value, msg.args[0], s) if hasattr(group, 'value'): if not group._private: return (value, None) else: capability = getCapability(group._name) if ircdb.checkCapability(msg.prefix, capability): return (value, True) else: irc.errorNoCapability(capability, Raise=True) else: irc.error(_('That registry variable has no value. Use the list ' 'command in this plugin to see what variables are ' 'available in this group.'))
def cutwire(self, irc, msg, args, channel, cutWire): """<colored wire> Will cut the given wire if you've been timebombed.""" channel = ircutils.toLower(channel) try: if not self.bombs[channel].active: return if not ircutils.nickEqual(self.bombs[channel].victim, msg.nick) and not ircdb.checkCapability( msg.prefix, 'admin'): irc.reply('You can\'t cut the wire on someone else\'s bomb!') return self.bombs[channel].cutwire(irc, cutWire) except KeyError: pass irc.noReply()
def _slot(self, lastItem): irc = lastItem.irc msg = lastItem.msg channel = lastItem.channel prefix = lastItem.prefix nick = prefix.split('!')[0] kind = lastItem.kind if not ircutils.isChannel(channel): return if not self.registryValue('enable', channel): return try: ircdb.users.getUser(msg.prefix) # May raise KeyError capability = self.registryValue('exempt') if capability: if ircdb.checkCapability(msg.prefix, ','.join([channel, capability])): return except KeyError: pass punishment = self.registryValue('%s.punishment' % kind, channel) reason = _('%s flood detected') % kind if punishment == 'kick': msg = ircmsgs.kick(channel, nick, reason) irc.queueMsg(msg) elif punishment == 'ban': msg = ircmsgs.ban(channel, prefix) irc.queueMsg(msg) elif punishment == 'kban': msg = ircmsgs.ban(channel, prefix) irc.queueMsg(msg) msg = ircmsgs.kick(channel, nick, reason) irc.queueMsg(msg) elif punishment.startswith('mode'): msg = ircmsgs.mode(channel, punishment[len('mode'):]) irc.queueMsg(msg) elif punishment.startswith('umode'): msg = ircmsgs.mode(channel, (punishment[len('umode'):], nick)) irc.queueMsg(msg) elif punishment.startswith('command '): tokens = callbacks.tokenize(punishment[len('command '):]) self.Proxy(irc, msg, tokens)
def vote(self, irc, msg, args, action): """<something> Votes for something. It doesn't actually perform any actions directly, but could be an interesting way to get user feedback.""" action = ircutils.stripFormatting(action.lower()).strip() override = self.registryValue("allowAdminOverride") and \ ircdb.checkCapability(msg.prefix, 'admin') if not action: # It must be just whitespace or formatting codes irc.error("You must specify a proper action!", Raise=True) try: votedhosts = map(self._lazyhostmask, self.votedb[action][1:]) if self._lazyhostmask(msg.prefix) in votedhosts and not override: irc.error("You have already voted to %r." % action, Raise=True) except KeyError: self.votedb[action] = [0] self.votedb[action][0] += 1 irc.reply("%s voted to %s" % (msg.nick, self._formatAction(action))) self.votedb[action].append(msg.prefix)
def vacuum(self, irc, msg, args, channel): """[<channel>|global] Vacuums the database for <channel>. See SQLite vacuum doc here: http://www.sqlite.org/lang_vacuum.html <channel> is only necessary if the message isn't sent in the channel itself. First check if user has the required capability specified in plugin config requireVacuumCapability. """ capability = self.registryValue('requireVacuumCapability') if capability: if not ircdb.checkCapability(msg.prefix, capability): irc.errorNoCapability(capability, Raise=True) db = self.getDb(channel) cursor = db.cursor() cursor.execute("""VACUUM""") db.commit() irc.replySuccess()
def list(self, irc, msg, args, optlist, cb): """[--private] [<plugin>] Lists the commands available in the given plugin. If no plugin is given, lists the public plugins available. If --private is given, lists the private plugins. """ private = False for (option, argument) in optlist: if option == 'private': private = True if not self.registryValue('listPrivatePlugins') and \ not ircdb.checkCapability(msg.prefix, 'owner'): irc.errorNoCapability('owner') if not cb: def isPublic(cb): name = cb.name() return conf.supybot.plugins.get(name).public() names = [cb.name() for cb in irc.callbacks if (private and not isPublic(cb)) or (not private and isPublic(cb))] names.sort() if names: irc.reply(format('%L', names)) else: if private: irc.reply('There are no private plugins.') else: irc.reply('There are no public plugins.') else: commands = cb.listCommands() if commands: commands.sort() irc.reply(format('%L', commands)) else: irc.reply(format('That plugin exists, but has no commands. ' 'This probably means that it has some ' 'configuration variables that can be ' 'changed in order to modify its behavior. ' 'Try "config list supybot.plugins.%s" to see ' 'what configuration variables it has.', cb.name()))
def inFilter(self, irc, msg): if msg.command == 'PRIVMSG' and self.active: channel = msg.args[0] s = ircutils.stripFormatting(msg.args[1]) if ircutils.isChannel(channel) and self.registryValue( 'kick', channel): if confusables.is_mixed_script(s): c = irc.state.channels[channel] cap = ircdb.makeChannelCapability(channel, 'op') u = msg.nick t = time.time() self.thresh[u].append(t) self.thresh[u] = [ item for item in self.thresh[u] if item > (t - self.registryValue('slw')) ] self.log.warning( "Detected mixed <%s>, threshold" "for user %s is now %s", s, u, len(self.thresh[u])) if len(self.thresh[u]) > self.registryValue('rep'): self.log.warning( "Threshold reached, trying to kick %s", u) if c.isHalfopPlus(irc.nick): if c.isHalfopPlus(u) or \ ircdb.checkCapability(msg.prefix, cap): self.log.warning( "Not kicking %s from %s, because " "they are halfop+ or can't be " "kicked.", u, channel) else: message = self.registryValue( 'kick.message', channel) irc.queueMsg(ircmsgs.kick(channel, u, message)) self.log.warning("Kicked %s from %s", u, channel) else: self.log.warning( 'Should kick %s from %s, but not opped.', u, channel) return msg
def _preCheck(self, irc, msg, channel): if self.registryValue('requireRegistration'): try: _ = ircdb.users.getUser(msg.prefix) except KeyError: irc.errorNotRegistered(Raise=True) capability = self.registryValue('requireCapability') if capability: if not ircdb.checkCapability(msg.prefix, capability): irc.errorNoCapability(capability, Raise=True) if self.registryValue('requirePresenceInChannel', channel) and \ msg.nick not in irc.state.channels[channel].users: irc.error(format('You must be in %s to %q in there.', channel, 'say'), Raise=True) c = ircdb.channels.getChannel(channel) if c.lobotomized: irc.error(format('I\'m lobotomized in %s.', channel), Raise=True) if not c._checkCapability(self.name()): irc.error('That channel has set its capabilities so as to ' 'disallow the use of this plugin.', Raise=True)
def start(self, irc, msg, args): """takes no arguments A command to start the node checker.""" # don't forget to redefine the event wrapper if ircdb.checkCapability(msg.prefix, "owner"): def checkForPosts(): self.checkReddit(irc) try: schedule.addPeriodicEvent( checkForPosts, self.registryValue('checkinterval') * 60, 'redditCheck', False) except AssertionError: irc.reply('The reddit checker was already running!') else: irc.reply('Reddit checker started!') else: irc.reply("F**k off you unauthorized piece of shit")
def doPrivmsg(self, irc, msg): (recipients, text) = msg.args for channel in recipients.split(','): if irc.isChannel(channel): noLogPrefix = self.registryValue('noLogPrefix', channel, irc.network) cap = ircdb.makeChannelCapability(channel, 'logChannelMessages') try: logChannelMessages = ircdb.checkCapability( msg.prefix, cap, ignoreOwner=True) except KeyError: logChannelMessages = True nick = msg.nick or irc.nick rewriteRelayed = self.registryValue('rewriteRelayed', channel, irc.network) if msg.tagged('ChannelLogger__relayed'): wasRelayed = True elif 'label' in msg.server_tags: label = msg.server_tags['label'] if label in self._emitted_relayed_msgs: del self._emitted_relayed_msgs[label] wasRelayed = True else: wasRelayed = False else: wasRelayed = False if rewriteRelayed and wasRelayed: (nick, text) = text.split(' ', 1) nick = nick[1:-1] msg.args = (recipients, text) if (noLogPrefix and text.startswith(noLogPrefix)) or \ not logChannelMessages: text = '-= THIS MESSAGE NOT LOGGED =-' if ircmsgs.isAction(msg): self.doLog(irc, channel, '* %s %s\n', nick, ircmsgs.unAction(msg)) else: self.doLog(irc, channel, '<%s> %s\n', nick, text)
def doPrivmsg(self, irc, msg): assert self is irc.callbacks[0], \ 'Owner isn\'t first callback: %r' % irc.callbacks if ircmsgs.isCtcp(msg): return s = callbacks.addressed(irc, msg) if s: ignored = ircdb.checkIgnored(msg.prefix) if ignored: self.log.info('Ignoring command from %s.', msg.prefix) return maximum = conf.supybot.abuse.flood.command.maximum() self.commands.enqueue(msg) if conf.supybot.abuse.flood.command() \ and self.commands.len(msg) > maximum \ and not ircdb.checkCapability(msg.prefix, 'trusted'): punishment = conf.supybot.abuse.flood.command.punishment() banmask = conf.supybot.protocols.irc.banmask \ .makeBanmask(msg.prefix) self.log.info( 'Ignoring %s for %s seconds due to an apparent ' 'command flood.', banmask, punishment) ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.notify(): irc.reply('You\'ve given me %s commands within the last ' '%i seconds; I\'m now ignoring you for %s.' % (maximum, conf.supybot.abuse.flood.interval(), utils.timeElapsed(punishment, seconds=False))) return try: tokens = callbacks.tokenize(s, channel=msg.channel, network=irc.network) self.Proxy(irc, msg, tokens) except SyntaxError as e: if conf.supybot.reply.error.detailed(): irc.error(str(e)) else: irc.replyError(msg=msg) self.log.info('Syntax error: %s', e)
def report(self, irc, msg, args): """ Report the current question as invalid and skip to the next one. Only use this command if a question is unanswerable (e.g. audio/video clues) """ channel = msg.channel if (self.registryValue("requireOps", channel) and msg.nick not in irc.state.channels[channel].ops and not ircdb.checkCapability(msg.prefix, "admin")): return if channel in self.games: if self.games[channel].active: r = requests.post( "{0}/api/invalid".format(self.jserviceUrl), data={"id": self.games[channel].id}, ) if r.status_code == 200: self.games[channel].reply( "Question successfully reported.") else: self.games[channel].reply("Error. Question not reported.") self.games[channel].end()
def _getValue(self, irc, msg, group, network=None, channel=None, addGlobal=False): global_group = group global_value = str(group) or ' ' group = group.getSpecific( network=network.network, channel=channel, check=False) value = str(group) or ' ' if addGlobal and not irc.nested: if global_group._channelValue and channel: # TODO: also show the network value when relevant value = _( 'Global: %(global_value)s; ' '%(channel_name)s @ %(network_name)s: %(channel_value)s') % { 'global_value': global_value, 'channel_name': msg.channel, 'network_name': irc.network, 'channel_value': value, } elif global_group._networkValue and network: value = _( 'Global: %(global_value)s; ' '%(network_name)s: %(network_value)s') % { 'global_value': global_value, 'network_name': irc.network, 'network_value': value, } if hasattr(global_group, 'value'): if not global_group._private: return (value, None) else: capability = getCapability(irc, group._name) if ircdb.checkCapability(msg.prefix, capability): return (value, True) else: irc.errorNoCapability(capability, Raise=True) else: irc.error(_('That registry variable has no value. Use the list ' 'command in this plugin to see what variables are ' 'available in this group.'), Raise=True)
def defuse(self, irc, msg, args, channel): """Takes no arguments Defuses the active bomb (channel ops only)""" channel = ircutils.toLower(channel) try: if self.bombs[channel].active: if ircutils.nickEqual( self.bombs[channel].victim, msg.nick) and not ( ircutils.nickEqual(self.bombs[channel].victim, self.bombs[channel].sender) or ircdb.checkCapability(msg.prefix, 'admin')): irc.reply( 'You can\'t defuse a bomb that\'s in your own pants, you\'ll just have to cut a wire and hope for the best.' ) return self.bombs[channel].defuse() irc.reply('Bomb defused') else: irc.error('There is no active bomb') except KeyError: pass irc.error('There is no active bomb')
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 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) self._sendMsgs(irc, nicks, f) else: irc.errorNoCapability(capability)
def defuse(self, irc, msg, args, channel): """[<channel>] Defuses the active bomb (channel ops only). """ channel = ircutils.toLower(channel) try: if self.bombs[channel].active: if ircutils.nickEqual( self.bombs[channel].victim, msg.nick) and not ( ircutils.nickEqual(self.bombs[channel].victim, self.bombs[channel].sender) or ircdb.checkCapability(msg.prefix, "admin")): irc.reply( "You can't defuse a bomb that's in your own pants, you'll just" " have to cut a wire and hope for the best.") return self.bombs[channel].defuse() irc.reply("Bomb defused.") else: irc.error("There is no active bomb.") except KeyError: pass irc.error("There is no active bomb")
def _checkManageCapabilities(self, irc, msg, channel): """Check if the user has any of the required capabilities to manage the channel topic. The list of required capabilities is in requireManageCapability channel config. Also allow if the user is a chanop. Since he can change the topic manually anyway. """ c = irc.state.channels[channel] if msg.nick in c.ops or msg.nick in c.halfops or 't' not in c.modes: return True capabilities = self.registryValue('requireManageCapability') if capabilities: for capability in re.split(r'\s*;\s*', capabilities): if capability.startswith('channel,'): capability = ircdb.makeChannelCapability(channel, capability[8:]) if capability and ircdb.checkCapability(msg.prefix, capability): return True return False else: return True
def newpoll(self, irc, msg, args, channel, interval, answers, question): """<number of minutes for announce interval> <"answer,answer,..."> question Creates a new poll with the given question and answers. <channel> is only necessary if the message isn't sent in the channel itself.""" capability = ircdb.makeChannelCapability(channel, 'op') if not ircdb.checkCapability(msg.prefix, capability): irc.error('Need ops') return db = self.getDb(channel) cursor = db.cursor() self._execute_query(cursor, 'INSERT INTO polls VALUES (?,?,?,?,?)', None, datetime.datetime.now(), 1, None, question) pollid = cursor.lastrowid # used to add choices into db. each choice represented by character, starting at capital A (code 65) def genAnswers(): for i, answer in enumerate(answers, start=65): yield pollid, chr(i), answer cursor.executemany('INSERT INTO choices VALUES (?,?,?)', genAnswers()) db.commit() irc.reply('Started new poll #%s' % pollid) # function called by schedule event. can not have args def runPoll(): self._runPoll(irc, channel, pollid) # start schedule. will announce poll/choices to channel at interval schedule.addPeriodicEvent(runPoll, interval * 60, name='%s_poll_%s' % (channel, pollid)) self.poll_schedules.append('%s_poll_%s' % (channel, pollid))
def doPrivmsg(self, irc, msg): (recipients, text) = msg.args for channel in recipients.split(','): if irc.isChannel(channel): noLogPrefix = self.registryValue('noLogPrefix', channel) cap = ircdb.makeChannelCapability(channel, 'logChannelMessages') try: logChannelMessages = ircdb.checkCapability(msg.prefix, cap, ignoreOwner=True) except KeyError: logChannelMessages = True nick = msg.nick or irc.nick if msg.tagged('ChannelLogger__relayed'): (nick, text) = text.split(' ', 1) nick = nick[1:-1] msg.args = (recipients, text) if (noLogPrefix and text.startswith(noLogPrefix)) or \ not logChannelMessages: text = '-= THIS MESSAGE NOT LOGGED =-' if ircmsgs.isAction(msg): self.doLog(irc, channel, '* %s %s\n', nick, ircmsgs.unAction(msg)) else: self.doLog(irc, channel, '<%s> %s\n', nick, text)
def delquote(self, irc, msg, args, optlist, qid): """[--channel <#channel>] <id> Delete the quote number 'id', only by the creator of the quote in the first 5 minutes or by an admin. If --channel is supplied the quote is fetched from that channel database.""" channel = msg.args[0] for (option, arg) in optlist: if option == 'channel': if not ircutils.isChannel(arg): irc.error(format(_('%s is not a valid channel.'), arg), Raise=True) channel = arg q = self.db.getQuoteById(channel, qid) if q is not None: if ircdb.checkCapability(msg.prefix, 'admin'): self.db.delQuoteById(channel, qid) irc.replySuccess() elif (time.time() - 300) <= q[3]: if q[2].lower() == msg.nick.lower(): self.db.delQuoteById(channel, qid) irc.replySuccess() else: irc.error( format( _("This quote only can be deleted by %s " "or an admin."), q[2])) else: irc.error( format( _("Too late, it has already passed 5 minutes." " Ask an admin."), qid, channel)) else: irc.error( format(_("No such quote %s in %s's database."), qid, channel))
def _tell(self, irc, msg, args, target, text, notice): if irc.nested: irc.error('This command cannot be nested.', Raise=True) if target.lower() == 'me': target = msg.nick if irc.isChannel(target): irc.error(_('Hey, just give the command. No need for the tell.')) return if not ircutils.isNick(target): irc.errorInvalid('nick', target) if ircutils.nickEqual(target, irc.nick): irc.error(_('You just told me, why should I tell myself?'), Raise=True) if target not in irc.state.nicksToHostmasks and \ not ircdb.checkCapability(msg.prefix, 'owner'): # We'll let owners do this. s = _('I haven\'t seen %s, I\'ll let you do the telling.') % target irc.error(s, Raise=True) if irc.action: irc.action = False text = '* %s %s' % (irc.nick, text) s = _('%s wants me to tell you: %s') % (msg.nick, text) irc.replySuccess() irc.reply(s, to=target, private=True, notice=notice)
def do(type): cap = ircdb.makeChannelCapability(channel, type) try: if ircdb.checkCapability(msg.prefix, cap, ignoreOwner=not self.registryValue('owner')): if self.registryValue(type, channel): self.log.info('Scheduling auto-%s of %s in %s.', type, msg.prefix, channel) def dismiss(): """Determines whether or not a mode has already been applied.""" l = getattr(irc.state.channels[channel], type+'s') return (msg.nick in l) msgmaker = getattr(ircmsgs, type) schedule_msg(msgmaker(channel, msg.nick), dismiss) raise Continue # Even if fallthrough, let's only do one. elif not fallthrough: self.log.debug('%s has %s, but supybot.plugins.AutoMode.%s' ' is not enabled in %s, refusing to fall ' 'through.', msg.prefix, cap, type, channel) raise Continue except KeyError: pass
def checkCapabilityButIgnoreOwner(irc, msg, args, state, cap): cap = ircdb.canonicalCapability(cap) if not ircdb.checkCapability(msg.prefix, cap, ignoreOwner=True): state.errorNoCapability(cap, Raise=True)
def callCommand(self, name, irc, msg, *args, **kwargs): if ircdb.checkCapability(msg.prefix, 'admin'): self.__parent.callCommand(name, irc, msg, *args, **kwargs) else: irc.errorNoCapability('admin')
def invalidCommand(self, irc, msg, tokens): assert not msg.repliedTo, 'repliedTo msg in Misc.invalidCommand.' assert self is irc.callbacks[-1], 'Misc isn\'t last callback.' assert msg.command in ('PRIVMSG', 'NOTICE') self.log.debug('Misc.invalidCommand called (tokens %s)', tokens) # First, we check for invalidCommand floods. This is rightfully done # here since this will be the last invalidCommand called, and thus it # will only be called if this is *truly* an invalid command. maximum = conf.supybot.abuse.flood.command.invalid.maximum() self.invalidCommands.enqueue(msg) if self.invalidCommands.len(msg) > maximum and \ conf.supybot.abuse.flood.command.invalid() and \ not ircdb.checkCapability(msg.prefix, 'trusted'): punishment = conf.supybot.abuse.flood.command.invalid.punishment() banmask = '*!%s@%s' % (msg.user, msg.host) self.log.info( 'Ignoring %s for %s seconds due to an apparent ' 'invalid command flood.', banmask, punishment) if tokens and tokens[0] == 'Error:': self.log.warning('Apparent error loop with another Supybot ' 'observed. Consider ignoring this bot ' 'permanently.') ircdb.ignores.add(banmask, time.time() + punishment) if conf.supybot.abuse.flood.command.invalid.notify(): irc.reply( _('You\'ve given me %s invalid commands within the last ' '%i seconds; I\'m now ignoring you for %s.') % (maximum, conf.supybot.abuse.flood.interval(), utils.timeElapsed(punishment, seconds=False))) return # Now, for normal handling. channel = msg.channel # Only bother with the invaildCommand flood handling if it's actually # enabled if conf.supybot.abuse.flood.command.invalid(): # First, we check for invalidCommand floods. This is rightfully done # here since this will be the last invalidCommand called, and thus it # will only be called if this is *truly* an invalid command. maximum = conf.supybot.abuse.flood.command.invalid.maximum() banmasker = conf.supybot.protocols.irc.banmask.makeBanmask if self.invalidCommands.len(msg) > maximum and \ not ircdb.checkCapability(msg.prefix, 'trusted') and \ msg.prefix != irc.prefix and \ ircutils.isUserHostmask(msg.prefix): penalty = conf.supybot.abuse.flood.command.invalid.punishment() banmask = banmasker(msg.prefix, channel=channel, network=irc.network) self.log.info( 'Ignoring %s for %s seconds due to an apparent ' 'invalid command flood.', banmask, penalty) if tokens and tokens[0] == 'Error:': self.log.warning( 'Apparent error loop with another Supybot ' 'observed. Consider ignoring this bot ' 'permanently.') ircdb.ignores.add(banmask, time.time() + penalty) if conf.supybot.abuse.flood.command.invalid.notify(): irc.reply( 'You\'ve given me %s invalid commands within ' 'the last minute; I\'m now ignoring you for %s.' % (maximum, utils.timeElapsed(penalty, seconds=False))) return # Now, for normal handling. if conf.supybot.reply.whenNotCommand.getSpecific(irc.network, channel)(): if len(tokens) >= 2: cb = irc.getCallback(tokens[0]) if cb: plugin = cb.name() irc.error( format( _('The %q plugin is loaded, but there is ' 'no command named %q in it. Try "list ' '%s" to see the commands in the %q ' 'plugin.'), plugin, tokens[1], plugin, plugin)) else: irc.errorInvalid(_('command'), tokens[0], repr=False) else: command = tokens and tokens[0] or '' irc.errorInvalid(_('command'), command, repr=False) else: if tokens: # echo [] will get us an empty token set, but there's no need # to log this in that case anyway, it being a nested command. self.log.info('Not replying to %s in %s, not a command.', tokens[0], channel if channel != irc.nick else _('private')) if irc.nested: bracketConfig = conf.supybot.commands.nested.brackets brackets = bracketConfig.getSpecific(irc.network, channel)() if brackets: (left, right) = brackets irc.reply(left + ' '.join(tokens) + right) else: pass # Let's just do nothing, I can't think of better.
def _ban(self, irc, msg, args, channel, optlist, target, expiry, reason, kick): # Check that they're not trying to make us kickban ourself. if irc.isNick(target): bannedNick = target try: bannedHostmask = irc.state.nickToHostmask(target) banmaskstyle = conf.supybot.protocols.irc.banmask banmask = banmaskstyle.makeBanmask(bannedHostmask, [o[0] for o in optlist]) except KeyError: if not conf.supybot.protocols.irc.strictRfc() and \ target.startswith('$'): # Select the last part, or the whole target: bannedNick = target.split(':')[-1] banmask = bannedHostmask = target else: irc.error(format(_('I haven\'t seen %s.'), bannedNick), Raise=True) else: bannedNick = ircutils.nickFromHostmask(target) banmask = bannedHostmask = target if not irc.isNick(bannedNick): self.log.warning('%q tried to kban a non nick: %q', msg.prefix, bannedNick) raise callbacks.ArgumentError elif bannedNick == irc.nick: self.log.warning('%q tried to make me kban myself.', msg.prefix) irc.error(_('I cowardly refuse to kickban myself.')) return if not reason: reason = msg.nick capability = ircdb.makeChannelCapability(channel, 'op') # Check (again) that they're not trying to make us kickban ourself. if ircutils.hostmaskPatternEqual(banmask, irc.prefix): if ircutils.hostmaskPatternEqual(bannedHostmask, irc.prefix): self.log.warning('%q tried to make me kban myself.',msg.prefix) irc.error(_('I cowardly refuse to ban myself.')) return else: self.log.warning('Using exact hostmask since banmask would ' 'ban myself.') banmask = bannedHostmask # Now, let's actually get to it. Check to make sure they have # #channel,op and the bannee doesn't have #channel,op; or that the # bannee and the banner are both the same person. def doBan(): if irc.state.channels[channel].isOp(bannedNick): irc.queueMsg(ircmsgs.deop(channel, bannedNick)) irc.queueMsg(ircmsgs.ban(channel, banmask)) if kick: irc.queueMsg(ircmsgs.kick(channel, bannedNick, reason)) if expiry > 0: def f(): if channel in irc.state.channels and \ banmask in irc.state.channels[channel].bans: irc.queueMsg(ircmsgs.unban(channel, banmask)) schedule.addEvent(f, expiry) if bannedNick == msg.nick: doBan() elif ircdb.checkCapability(msg.prefix, capability): if ircdb.checkCapability(bannedHostmask, capability) and \ not ircdb.checkCapability(msg.prefix, 'owner'): self.log.warning('%s tried to ban %q, but both have %s', msg.prefix, bannedHostmask, capability) irc.error(format(_('%s has %s too, you can\'t ban ' 'them.'), bannedNick, capability)) else: doBan() else: self.log.warning('%q attempted kban without %s', msg.prefix, capability) irc.errorNoCapability(capability)