def nicks(self, irc, msg, args, channel): """[<channel>] Returns the nicks of the people in the linked channels. <channel> is only necessary if the message isn't sent on the channel itself.""" for relay in self.relays: if relay.sourceChannel == channel and \ relay.sourceNetwork == irc.network: if not world.getIrc(relay.targetNetwork): irc.reply(_('Not connected to network %s.') % relay.targetNetwork) else: users = [] ops = [] halfops = [] voices = [] normals = [] numUsers = 0 target = relay.targetChannel channels = world.getIrc(relay.targetNetwork).state.channels found = False for key, channel_ in channels.items(): if re.match(relay.targetChannel, key): found = True break if not found: continue for s in channel_.users: s = s.strip() if not s: continue numUsers += 1 if s in channel_.ops: users.append('@%s' % s) elif s in channel_.halfops: users.append('%%%s' % s) elif s in channel_.voices: users.append('+%s' % s) else: users.append(s) #utils.sortBy(ircutils.toLower, ops) #utils.sortBy(ircutils.toLower, halfops) #utils.sortBy(ircutils.toLower, voices) #utils.sortBy(ircutils.toLower, normals) users.sort() msg.tag('relayedMsg') s = _('%d users in %s on %s: %s') % (numUsers, relay.targetChannel, relay.targetNetwork, utils.str.commaAndify(users)) irc.reply(s) irc.noReply()
def nicks(self, irc, msg, args, channel): """[<channel>] Returns the nicks of the people in the linked channels. <channel> is only necessary if the message isn't sent on the channel itself.""" for relay in self.relays: if relay.sourceChannel == channel and \ relay.sourceNetwork.lower() == irc.network.lower(): if not world.getIrc(relay.targetNetwork): irc.reply( _('Not connected to network %s.') % relay.targetNetwork) else: users = [] ops = [] halfops = [] voices = [] normals = [] numUsers = 0 target = relay.targetChannel channels = world.getIrc(relay.targetNetwork).state.channels found = False for key, channel_ in channels.items(): if re.match(relay.targetChannel, key): found = True break if not found: continue for s in channel_.users: s = s.strip() if not s: continue numUsers += 1 if s in channel_.ops: users.append('@%s' % s) elif s in channel_.halfops: users.append('%%%s' % s) elif s in channel_.voices: users.append('+%s' % s) else: users.append(s) #utils.sortBy(ircutils.toLower, ops) #utils.sortBy(ircutils.toLower, halfops) #utils.sortBy(ircutils.toLower, voices) #utils.sortBy(ircutils.toLower, normals) users.sort() msg.tag('relayedMsg') s = _('%d users in %s on %s: %s') % ( numUsers, relay.targetChannel, relay.targetNetwork, utils.str.commaAndify(users)) irc.reply(s) irc.noReply()
def send(s): targetIRC = world.getIrc(relay.targetNetwork) if not targetIRC: self.log.info('LinkRelay: Not connected to network %s.' % relay.targetNetwork) elif targetIRC.zombie: self.log.info('LinkRelay: IRC %s appears to be a zombie'% relay.targetNetwork) elif irc.isChannel(relay.targetChannel) and \ relay.targetChannel not in targetIRC.state.channels: self.log.info('LinkRelay: I\'m not in in %s on %s' % (relay.targetChannel, relay.targetNetwork)) else: if isPrivmsg or \ self.registryValue('nonPrivmsgs', channel) == 'privmsg': f = ircmsgs.privmsg elif self.registryValue('nonPrivmsgs', channel) == 'notice': f = ircmsgs.notice else: return allowedLength = conf.get(conf.supybot.reply.mores.length, relay.targetChannel) or 470 cont = _('(continuation)') remainingLength = allowedLength - len(cont) - 1 head = s[0:allowedLength] tail = [cont + ' ' + s[i:i+remainingLength] for i in range(allowedLength, len(s), remainingLength)] for s in [head] + tail: msg = f(relay.targetChannel, s) msg.tag('relayedMsg') targetIRC.sendMsg(msg)
def send(s): targetIRC = world.getIrc(relay.targetNetwork) if not targetIRC: self.log.info('LinkRelay: Not connected to network %s.' % relay.targetNetwork) elif targetIRC.zombie: self.log.info('LinkRelay: IRC %s appears to be a zombie' % relay.targetNetwork) elif irc.isChannel(relay.targetChannel) and \ relay.targetChannel not in targetIRC.state.channels: self.log.info('LinkRelay: I\'m not in in %s on %s' % (relay.targetChannel, relay.targetNetwork)) else: if isPrivmsg or \ self.registryValue('nonPrivmsgs', channel) == 'privmsg': f = ircmsgs.privmsg elif self.registryValue('nonPrivmsgs', channel) == 'notice': f = ircmsgs.notice else: return allowedLength = conf.get(conf.supybot.reply.mores.length, relay.targetChannel) or 470 cont = _('(continuation)') remainingLength = allowedLength - len(cont) - 1 head = s[0:allowedLength] tail = [ cont + ' ' + s[i:i + remainingLength] for i in range(allowedLength, len(s), remainingLength) ] for s in [head] + tail: msg = f(relay.targetChannel, s) msg.tag('relayedMsg') targetIRC.sendMsg(msg)
def _getIrc(self, network): irc = world.getIrc(network) if irc: return irc else: raise callbacks.Error('I\'m not currently connected to %s.' % network)
def doPost(self, handler, path, form): headers = dict(self.headers) network = None try: information = path.split('/')[1:] network = information[0] except IndexError: self._send_error(handler, _("""Error: You need to provide the network name in the URL.""")) return irc = world.getIrc(network) if irc is None: self._send_error(handler, (_('Error: Unknown network %r') % network)) return # Handle payload payload = None try: payload = json.JSONDecoder().decode(form.decode('utf-8')) except Exception as e: self.log.info(e) self._send_error(handler, _('Error: Invalid JSON data sent.')) return try: self.gitlab.handle_payload(headers, payload, irc) except Exception as e: self.log.info(e) self._send_error(handler, _('Error: Invalid data sent.')) return # Return OK self._send_ok(handler)
def list(self, irc, msg, args): """takes no arguments Returns all the defined relay links""" if not self.relays: irc.reply(_('This is no relay enabled. Use "linkrelay add" to ' 'add one.')) return replies = [] for relay in self.relays: if world.getIrc(relay.targetNetwork): hasIRC = 'Link healthy!' else: hasIRC = '\x03%sNot connected to network.\017' % \ self.registryValue('colors.info', msg.args[0]) s ='\x02%s\x02 on \x02%s\x02 ==> \x02%s\x02 on \x02%s\x02. %s' if not self.registryValue('color', msg.args[0]): s = s.replace('\x02', '') replies.append(s % (relay.sourceChannel, relay.sourceNetwork, relay.targetChannel, relay.targetNetwork, hasIRC)) irc.replies(replies)
def _getIrc(self, network): irc = world.getIrc(network) if irc: return irc else: raise callbacks.Error, \ 'I\'m not currently connected to %s.' % network
def onPayload(self, headers, payload): if 'reply_env' not in ircmsgs.IrcMsg.__slots__: log.error("Got event payload from GitHub, but your version " "of Supybot is not compatible with reply " "environments, so, the GitHub plugin can't " "announce it.") if 'full_name' in payload['repository']: repo = payload['repository']['full_name'] elif 'name' in payload['repository']['owner']: repo = '%s/%s' % (payload['repository']['owner']['name'], payload['repository']['name']) else: repo = '%s/%s' % (payload['repository']['owner']['login'], payload['repository']['name']) event = headers['X-GitHub-Event'] announces = self._load() repoAnnounces = [] for (dbRepo, network, channel) in announces: if dbRepo == repo: repoAnnounces.append((network, channel)) if len(repoAnnounces) == 0: log.info('Commit for repo %s not announced anywhere' % repo) return for (network, channel) in repoAnnounces: # Compatability with DBs without a network if network == '': for irc in world.ircs: if channel in irc.state.channels: break else: irc = world.getIrc(network) if not irc: log.warning('Received GitHub payload with announcing ' 'enabled in %s on unloaded network %s.', channel, network) return if channel not in irc.state.channels: log.info(('Cannot announce event for repo ' '%s in %s on %s because I\'m not in %s.') % (repo, channel, irc.network, channel)) if event == 'push': commits = payload['commits'] if len(commits) == 0: log.warning('GitHub push hook called without any commit.') else: hidden = None last_commit = commits[-1] if last_commit['message'].startswith('Merge ') and \ len(commits) > 5: hidden = len(commits) + 1 commits = [last_commit] payload2 = dict(payload) for commit in commits: payload2['__commit'] = commit self._createPrivmsg(irc, channel, payload2, 'push', hidden) else: self._createPrivmsg(irc, channel, payload, event)
def f(): # If the network isn't available, pick any other one irc = world.getIrc(network) or world.ircs[0] tokens = callbacks.tokenize(command, channel=msg.channel, network=irc.network) if remove: del self.events[str(f.eventId)] self.Proxy(irc, msg, tokens)
def _forwardRequest(self, irc, msg, code): freeNode = world.getIrc("FreeNode") if not freeNode: irc.reply("not connected to geordi yet") return irc.noReply() self._replyIrc = irc self._replyMsg = msg freeNode.queueMsg(ircmsgs.privmsg("geordi", code))
def _inchannel(self, irc, channel): """ Returns whether or not the bot is in <channel> on network <irc>, which can be either the name of the network or its IrcObj. """ # If <irc> is a string and not an IrcObj, get the IrcObj that # matches it, returning False if it wasn't found. if isinstance(irc, str): irc = world.getIrc(irc) if not irc: return False for inchannel in irc.state.channels.keys(): if channel.lower() == inchannel.lower(): return True return False
def _garbagecollect(self, force=False): """ Removes stale activity data. """ if time.time() - self.lastgc < (60 * self.gcinterval) and not force: return for channel, times in self.times.items(): irc = world.getIrc(channel[0]) if not irc or time.time() - times[-1] >= 3600 * 6: try: del self.times[channel] except KeyError: pass self._writeactivityfile() self.lastgc = time.time()
def handle_message(self, path, data): try: network, channel = urllib.parse.unquote(path[1:]).split('.', 1) except: raise ValueError("Must pass network.channel as argument") irc = world.getIrc(network) if not irc: raise ValueError("Unknown IRC network %s" % network) if not ircutils.isChannel(channel): raise ValueError("Wrong IRC channel name %s" % channel) if channel not in irc.state.channels: raise ValueError("Bot not joined to channel %s" % channel) irc.queueMsg(ircmsgs.privmsg(channel, self.format_msg(data)))
def _sendmsg(self, irc, text, channel=None): """ Send <text> to <channel> on network <irc>. If <irc> is a list of sources, iterate through it and send <text> to each channel on network. """ if not isinstance(irc, list): if not self._inchannel(irc, channel): return False msg = ircmsgs.privmsg(channel, text, irc.prefix) msg.tag('relayed', True) irc.sendMsg(msg) return True else: successful = [] for source in irc: target = world.getIrc(source[0]) if target and self._sendmsg(target, text, source[1]): successful.append(source) return successful
def _readRelays(self): relays = [] path = dbPath() if not os.path.isfile(path): return relays with open(path) as fd: for (i, line) in enumerate(fd): try: (network, channel, skype_chat_id) = line.split() except ValueError: self.log.error("Skipping invalid line %i in %s: %s", i, path, line) irc = world.getIrc(network) if irc is None: self.log.warning("Unknown network %s", network) elif not irc.isChannel(channel): self.log.warning("Invalid channel %s", channel) relays.append(Relay(network, channel, skype_chat_id)) return relays
def onPayload(self, headers, payload): if 'reply_env' not in ircmsgs.IrcMsg.__slots__: log.error("Got event payload from GitHub, but your version " "of Supybot is not compatible with reply " "environments, so, the GitHub plugin can't " "announce it.") if 'full_name' in payload['repository']: repo = payload['repository']['full_name'] elif 'name' in payload['repository']['owner']: repo = '%s/%s' % (payload['repository']['owner']['name'], payload['repository']['name']) else: repo = '%s/%s' % (payload['repository']['owner']['login'], payload['repository']['name']) if 'X-GitHub-Event' in headers: event = headers['X-GitHub-Event'] else: # WTF? event = headers['x-github-event'] announces = self._load() repoAnnounces = [] for (dbRepo, network, channel) in announces: if fnmatch.fnmatch(repo, dbRepo): repoAnnounces.append((network, channel)) if len(repoAnnounces) == 0: log.info('Commit for repo %s not announced anywhere' % repo) return for (network, channel) in repoAnnounces: # Compatability with DBs without a network if network == '': for irc in world.ircs: if channel in irc.state.channels: break else: irc = world.getIrc(network) if not irc: log.warning('Received GitHub payload with announcing ' 'enabled in %s on unloaded network %s.', channel, network) return if channel not in irc.state.channels: log.info(('Cannot announce event for repo ' '%s in %s on %s because I\'m not in %s.') % (repo, channel, irc.network, channel)) if event == 'push': commits = payload['commits'] hidden = None if len(commits) == 0: log.warning('GitHub push hook called without any commit.') else: last_commit = commits[-1] max_comm = self.plugin.registryValue( 'max_announce_commits', channel) if len(commits) > max_comm + 1: # Limit to the specified number of commits, # but if there's only one more, show it hidden = len(commits) - max_comm commits = commits[:max_comm] payload2 = dict(payload) self._createPrivmsg(irc, channel, payload2, 'before.push') for commit in commits: payload2['__commit'] = commit self._createPrivmsg(irc, channel, payload2, 'push') if hidden: payload2['__hidden_commits'] = hidden self._createPrivmsg(irc, channel, payload2, 'push.hidden') elif event == 'gollum': pages = payload['pages'] if len(pages) == 0: log.warning('GitHub gollum hook called without any page.') else: payload2 = dict(payload) for page in pages: payload2['__page'] = page self._createPrivmsg(irc, channel, payload2, 'gollum') else: self._createPrivmsg(irc, channel, payload, event)
def send_msg(self, channel, text): irc = world.getIrc(self.registryValue('adminNet')) irc.queueMsg(ircmsgs.privmsg(channel, text)) log.info("Sent Message: \"{}\" to '{}'".format(text, channel))
def _send(self, network, nick, text): irc = world.getIrc(network) if irc: irc.queueMsg(ircmsgs.privmsg(nick, text))
def f(): # If the network isn't available, pick any other one irc = world.getIrc(network) or world.ircs[0] replyIrc = callbacks.ReplyIrcProxy(irc, msg) replyIrc.reply(_('Reminder: %s') % text, msg=msg, prefixNick=True) del self.events[str(f.eventId)]
def onPayload(self, headers, payload): if 'reply_env' not in ircmsgs.IrcMsg.__slots__: log.error("Got event payload from GitHub, but your version " "of Supybot is not compatible with reply " "environments, so, the GitHub plugin can't " "announce it.") if 'full_name' in payload['repository']: repo = payload['repository']['full_name'] elif 'name' in payload['repository']['owner']: repo = '%s/%s' % (payload['repository']['owner']['name'], payload['repository']['name']) else: repo = '%s/%s' % (payload['repository']['owner']['login'], payload['repository']['name']) if 'X-GitHub-Event' in headers: event = headers['X-GitHub-Event'] else: # WTF? event = headers['x-github-event'] announces = self._load() repoAnnounces = [] for (dbRepo, network, channel) in announces: if fnmatch.fnmatch(repo, dbRepo): repoAnnounces.append((network, channel)) if len(repoAnnounces) == 0: log.info('Commit for repo %s not announced anywhere' % repo) return for (network, channel) in repoAnnounces: # Compatability with DBs without a network if network == '': for irc in world.ircs: if channel in irc.state.channels: break else: irc = world.getIrc(network) if not irc: log.warning('Received GitHub payload with announcing ' 'enabled in %s on unloaded network %s.', channel, network) return if channel not in irc.state.channels: log.info(('Cannot announce event for repo ' '%s in %s on %s because I\'m not in %s.') % (repo, channel, irc.network, channel)) if event == 'push': commits = payload['commits'] if len(commits) == 0: log.warning('GitHub push hook called without any commit.') else: hidden = None last_commit = commits[-1] if last_commit['message'].startswith('Merge ') and \ len(commits) > 5: hidden = len(commits) + 1 commits = [last_commit] payload2 = dict(payload) for commit in commits: # trim the commit hash down to 7 char long if len(commit['id']) > 7: commit['id'] = commit['id'][:-33] payload2['__commit'] = commit self._createPrivmsg(irc, channel, payload2, 'push', hidden) elif event == 'commit_comment': # Here we get the commit name commit_url = 'https://api.github.com/repos/%s/git/commits/%s' % (payload['repository']['full_name'], payload['comment']['commit_id']) response = urllib.urlopen(commit_url); data = json.loads(response.read()) commit_name = data['message'] payload['comment']['commit_name'] = commit_name self._createPrivmsg(irc, channel, payload, event) else: self._createPrivmsg(irc, channel, payload, event)
def _announce_build_status(self, status): msg = '\x02[Nightly builds]\x02 %s' % status for (server, channel) in (('freenode', '#limnoria'),): world.getIrc(server).sendMsg(ircmsgs.privmsg(channel, msg))
def relay(self, irc, msg, channel=None): channel = (channel or msg.args[0]).lower() self.log.debug("RelayNext (%s): got channel %s", irc.network, channel) if not channel in irc.state.channels: return # Check for ignored events first. Checking for "'.' not in msg.nick" is for skipping # ignore checks from servers. ignoredevents = map(str.upper, self.registryValue('events.userIgnored', channel)) if msg.command in ignoredevents and msg.nick != irc.nick and '.' not in msg.nick and\ ircdb.checkIgnored(msg.prefix, channel): self.log.debug("RelayNext (%s): ignoring message from %s", irc.network, msg.prefix) return # Get the source channel source = "%s@%s" % (channel, irc.network) source = source.lower() out_s = self._format(irc, msg, channel) if out_s: for relay in self.db.values(): self.log.debug("RelayNext (%s): check if %s in %s", irc.network, source, relay) if source in relay: # If our channel is in a relay self.log.debug("RelayNext: found %s to be in relay %s", source, relay) # Remove ourselves from the target channels so we don't get duplicated messages targets = list(relay) targets.remove(source) self.log.debug("RelayNext: found targets %s for relay %s", targets, relay) if self.registryValue("antiflood.enable", channel): # Flood prevention timeout - how long commands of a certain type # should cease being relayed after flood prevention triggers timeout = self.registryValue("antiflood.timeout", channel) # If <maximum> messages of the same kind on one channel is # received in <seconds> seconds, flood prevention timeout is # triggered. maximum = self.registryValue("antiflood.maximum", channel) seconds = self.registryValue("antiflood.seconds", channel) # Store the message in a counter, with the keys taking the # form of (source channel@network, command name). If the counter # doesn't already exist, create one here. try: self.msgcounters[(source, msg.command)].enqueue(msg.prefix) except KeyError: self.msgcounters[(source, msg.command)] = TimeoutQueue(seconds) # Two different limits: one for messages and one for all others if msg.command == "PRIVMSG": maximum = self.registryValue("antiflood.maximum", channel) else: maximum = self.registryValue("antiflood.maximum.nonPrivmsgs", channel) if len(self.msgcounters[(source, msg.command)]) > maximum: # Amount of messages in the counter surpassed our limit, # announce the flood and block relaying messages of the # same type for X seconds self.log.debug("RelayNext (%s): message from %s blocked by " "flood protection.", irc.network, channel) if self.floodTriggered.get((source, msg.command)): # However, only send the announcement once. return c = msg.command e = format("Flood detected on %s (%s %ss/%s seconds), " "not relaying %ss for %s seconds!", channel, maximum, c, seconds, c, timeout) out_s = self._format(irc, msg, channel, announcement=e) self.floodTriggered[(source, msg.command)] = True self.log.info("RelayNext (%s): %s", irc.network, e) else: self.floodTriggered[(source, msg.command)] = False for cn in targets: # Iterate over all the relay targets for this message: # each target is stored internally as a #channel@netname # string. target, net = cn.split("@") otherIrc = world.getIrc(net) if otherIrc is None: self.log.debug("RelayNext: message to network %r" " dropped, we are not connected " "there!", net) return target_chanobj = otherIrc.state.channels.get(target) if (not target_chanobj) or otherIrc.nick not in target_chanobj.users: # We're not in the target relay channel! self.log.debug("RelayNext: message to %s@%s " "dropped, we are not in that " "channel!", target, net) else: out_msg = ircmsgs.privmsg(target, out_s) # Tag the message as relayed so we (and other relayers) don't # try to relay it again. out_msg.tag('relayedMsg') otherIrc.queueMsg(out_msg)
def nicks(self, irc, msg, args, channel, optlist): """[<channel>] [--count] Returns the nicks of the people in the linked channels. <channel> is only necessary if the message isn't sent in the channel itself. If --count is specified, only the amount of users in the relay is given.""" opts = dict(optlist) if irc.nested and 'count' not in keys: irc.error('This command cannot be nested.', Raise=True) try: c = irc.state.channels[channel] except KeyError: irc.error("Unknown channel '%s'." % channel, Raise=True) if msg.nick not in c.users: self.log.warning('RelayNext: %s on %s attempted to view ' 'nicks of %s without being in it.', msg.nick, irc.network, channel) irc.error(("You are not in '%s'." % channel), Raise=True) source = "%s@%s" % (channel, irc.network) source = source.lower() channel_count = 0 user_count = 0 all_users = [] # First, enumerate all the relays that the calling channel is it. Use a # set to prevent duplicates since one channel can be part of many relays. all_relays = set() for relay in self.db.values(): if source in relay: for channelpair in relay: # Each channel pair is a "#chan@net" string in the DB. all_relays.add(channelpair) for channelpair in all_relays: channel_count += 1 channel, net = channelpair.split("@", 1) try: c = world.getIrc(net).state.channels[channel] except (KeyError, AttributeError): # Unknown network or network disconnected. continue user_count += len(c.users) users = [] # Sort users before listing them, but do so case-insensitively. for s in sorted(c.users, key=ircutils.toLower): s = s.strip() if s in c.ops: users.append('@%s' % s) elif s in c.halfops: users.append('%%%s' % s) elif s in c.voices: users.append('+%s' % s) else: users.append(s) all_users += c.users s = format('%s users in %s on %s: %L', len(c.users), channel, net, users) # In outputting the user list, we need to make sure that the message fits, # and if not, wrap it into multiple messages. # XXX: This is ugly, but https://github.com/ProgVal/Limnoria/issues/1080 # means we have to chop off the (XX more messages) part too. allowed_length = 466 - len(irc.prefix) - len(irc.nick) - len(msg.nick) - \ len(_('(XX more messages)')) replies = textwrap.wrap(s, allowed_length) if 'count' not in opts: # Only bother doing this if we're not using --count. irc.reply(replies[0], private=True, notice=True) for s in replies[1:]: irc.reply("... %s" % s, private=True, notice=True) if 'count' in opts: # --count was specified; just reply with the amount of users. irc.reply(user_count) elif channel_count: irc.reply("Total users across %d channels: %d. Unique nicks: %d" % (channel_count, user_count, len(set(all_users))), private=True) else: irc.error("No relays for '%s' exist." % channel)
def _queueRelayedMsg(self, relay, s): msg = ircmsgs.privmsg(relay.channel, s) msg.tag("relayedMsg") world.getIrc(relay.network).queueMsg(msg)
def onPayload(self, headers, payload): if 'reply_env' not in ircmsgs.IrcMsg.__slots__: log.error("Got event payload from GitHub, but your version " "of Supybot is not compatible with reply " "environments, so, the GitHub plugin can't " "announce it.") if 'full_name' in payload['repository']: repo = payload['repository']['full_name'] elif 'name' in payload['repository']['owner']: repo = '%s/%s' % (payload['repository']['owner']['name'], payload['repository']['name']) else: repo = '%s/%s' % (payload['repository']['owner']['login'], payload['repository']['name']) if 'X-GitHub-Event' in headers: event = headers['X-GitHub-Event'] else: # WTF? event = headers['x-github-event'] announces = self._load() repoAnnounces = [] for (dbRepo, network, channel) in announces: if fnmatch.fnmatch(repo, dbRepo): repoAnnounces.append((network, channel)) if len(repoAnnounces) == 0: log.info('Commit for repo %s not announced anywhere' % repo) return for (network, channel) in repoAnnounces: # Compatability with DBs without a network if network == '': for irc in world.ircs: if channel in irc.state.channels: break else: irc = world.getIrc(network) if not irc: log.warning( 'Received GitHub payload with announcing ' 'enabled in %s on unloaded network %s.', channel, network) return if channel not in irc.state.channels: log.info(('Cannot announce event for repo ' '%s in %s on %s because I\'m not in %s.') % (repo, channel, irc.network, channel)) if event == 'push': commits = payload['commits'] if len(commits) == 0: log.warning( 'GitHub push hook called without any commit.') else: hidden = None last_commit = commits[-1] if last_commit['message'].startswith('Merge ') and \ len(commits) > 5: hidden = len(commits) + 1 commits = [last_commit] payload2 = dict(payload) self._createPrivmsg(irc, channel, payload2, 'before.push', None) for commit in commits: payload2['__commit'] = commit self._createPrivmsg(irc, channel, payload2, 'push', hidden) elif event == 'gollum': pages = payload['pages'] if len(pages) == 0: log.warning( 'GitHub gollum hook called without any page.') else: payload2 = dict(payload) for page in pages: payload2['__page'] = page self._createPrivmsg(irc, channel, payload2, 'gollum', None) else: self._createPrivmsg(irc, channel, payload, event)
def nicks(self, irc, msg, args, channel, optlist): """[<channel>] [--count] Returns the nicks of the people in the linked channels. <channel> is only necessary if the message isn't sent in the channel itself. If --count is specified, only the amount of users in the relay is given.""" opts = dict(optlist) if irc.nested and 'count' not in keys: irc.error('This command cannot be nested.', Raise=True) try: c = irc.state.channels[channel] except KeyError: irc.error("Unknown channel '%s'." % channel, Raise=True) if msg.nick not in c.users: self.log.warning( 'RelayNext: %s on %s attempted to view ' 'nicks of %s without being in it.', msg.nick, irc.network, channel) irc.error(("You are not in '%s'." % channel), Raise=True) source = "%s@%s" % (channel, irc.network) source = source.lower() channel_count = 0 user_count = 0 all_users = [] # First, enumerate all the relays that the calling channel is it. Use a # set to prevent duplicates since one channel can be part of many relays. all_relays = set() for relay in self.db.values(): if source in relay: for channelpair in relay: # Each channel pair is a "#chan@net" string in the DB. all_relays.add(channelpair) for channelpair in all_relays: channel_count += 1 channel, net = channelpair.split("@", 1) try: c = world.getIrc(net).state.channels[channel] except (KeyError, AttributeError): # Unknown network or network disconnected. continue user_count += len(c.users) users = [] # Sort users before listing them, but do so case-insensitively. for s in sorted(c.users, key=ircutils.toLower): s = s.strip() if s in c.ops: users.append('@%s' % s) elif s in c.halfops: users.append('%%%s' % s) elif s in c.voices: users.append('+%s' % s) else: users.append(s) all_users += c.users s = format('%s users in %s on %s: %L', len(c.users), channel, net, users) # In outputting the user list, we need to make sure that the message fits, # and if not, wrap it into multiple messages. # XXX: This is ugly, but https://github.com/ProgVal/Limnoria/issues/1080 # means we have to chop off the (XX more messages) part too. allowed_length = 466 - len(irc.prefix) - len(irc.nick) - len(msg.nick) - \ len(_('(XX more messages)')) replies = textwrap.wrap(s, allowed_length) if 'count' not in opts: # Only bother doing this if we're not using --count. irc.reply(replies[0], private=True, notice=True) for s in replies[1:]: irc.reply("... %s" % s, private=True, notice=True) if 'count' in opts: # --count was specified; just reply with the amount of users. irc.reply(user_count) elif channel_count: irc.reply("Total users across %d channels: %d. Unique nicks: %d" % (channel_count, user_count, len(set(all_users))), private=True) else: irc.error("No relays for '%s' exist." % channel)
def relay(self, irc, msg, channel=None): channel = (channel or msg.args[0]).lower() self.log.debug("RelayNext (%s): got channel %s", irc.network, channel) if not channel in irc.state.channels: return # Check for ignored events first. Checking for "'.' not in msg.nick" is for skipping # ignore checks from servers. ignoredevents = map(str.upper, self.registryValue('events.userIgnored', channel)) if msg.command in ignoredevents and msg.nick != irc.nick and '.' not in msg.nick and\ ircdb.checkIgnored(msg.prefix, channel): self.log.debug("RelayNext (%s): ignoring message from %s", irc.network, msg.prefix) return # Get the source channel source = "%s@%s" % (channel, irc.network) source = source.lower() out_s = self._format(irc, msg, channel) if out_s: for relay in self.db.values(): self.log.debug("RelayNext (%s): check if %s in %s", irc.network, source, relay) if source in relay: # If our channel is in a relay self.log.debug("RelayNext: found %s to be in relay %s", source, relay) # Remove ourselves from the target channels so we don't get duplicated messages targets = list(relay) targets.remove(source) self.log.debug("RelayNext: found targets %s for relay %s", targets, relay) if self.registryValue("antiflood.enable", channel): # Flood prevention timeout - how long commands of a certain type # should cease being relayed after flood prevention triggers timeout = self.registryValue("antiflood.timeout", channel) # If <maximum> messages of the same kind on one channel is # received in <seconds> seconds, flood prevention timeout is # triggered. maximum = self.registryValue("antiflood.maximum", channel) seconds = self.registryValue("antiflood.seconds", channel) # Store the message in a counter, with the keys taking the # form of (source channel@network, command name). If the counter # doesn't already exist, create one here. try: self.msgcounters[(source, msg.command)].enqueue(msg.prefix) except KeyError: self.msgcounters[( source, msg.command)] = TimeoutQueue(seconds) # Two different limits: one for messages and one for all others if msg.command == "PRIVMSG": maximum = self.registryValue( "antiflood.maximum", channel) else: maximum = self.registryValue( "antiflood.maximum.nonPrivmsgs", channel) if len(self.msgcounters[(source, msg.command)]) > maximum: # Amount of messages in the counter surpassed our limit, # announce the flood and block relaying messages of the # same type for X seconds self.log.debug( "RelayNext (%s): message from %s blocked by " "flood protection.", irc.network, channel) if self.floodTriggered.get((source, msg.command)): # However, only send the announcement once. return c = msg.command e = format( "Flood detected on %s (%s %ss/%s seconds), " "not relaying %ss for %s seconds!", channel, maximum, c, seconds, c, timeout) out_s = self._format(irc, msg, channel, announcement=e) self.floodTriggered[(source, msg.command)] = True self.log.info("RelayNext (%s): %s", irc.network, e) else: self.floodTriggered[(source, msg.command)] = False for cn in targets: # Iterate over all the relay targets for this message: # each target is stored internally as a #channel@netname # string. target, net = cn.split("@") otherIrc = world.getIrc(net) if otherIrc is None: self.log.debug( "RelayNext: message to network %r" " dropped, we are not connected " "there!", net) return target_chanobj = otherIrc.state.channels.get(target) if (not target_chanobj ) or otherIrc.nick not in target_chanobj.users: # We're not in the target relay channel! self.log.debug( "RelayNext: message to %s@%s " "dropped, we are not in that " "channel!", target, net) else: out_msg = ircmsgs.privmsg(target, out_s) # Tag the message as relayed so we (and other relayers) don't # try to relay it again. out_msg.tag('relayedMsg') otherIrc.queueMsg(out_msg)