class Seen: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'seen')) manager.registerCommand('seen', 'seen', 'seen', '(?P<nickname>[^ ]+)', self.check) manager.registerMessage('seen', self.record) def check(self, source, nickname): if not nickname in self.config: self.client.reply(source, 'I have never seen %s' % nickname) if isinstance(self.config[nickname], dict): self.client.reply( source, '%s was last seen %s in %s' % (nickname, timesince( datetime.datetime.utcfromtimestamp( self.config[nickname]['time'])), self.config[nickname]['channel'])) else: self.client.reply( source, '%s was last seen %s' % (nickname, timesince( datetime.datetime.utcfromtimestamp( self.config[nickname])))) def record(self, message): self.config[message.prefix['nickname']] = { 'channel': message.target, 'time': time() } self.config.save()
class Seen: help = "https://github.com/DASPRiD/DASBiT/wiki/Seen-Plugin" def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, "seen")) manager.registerCommand("seen", "seen", "seen", "(?P<nickname>[^ ]+)", self.check) manager.registerMessage("seen", self.record) def check(self, source, nickname): if not nickname in self.config: self.client.reply(source, "I have never seen %s" % nickname) if isinstance(self.config[nickname], dict): self.client.reply( source, "%s was last seen %s in %s" % ( nickname, timesince(datetime.datetime.utcfromtimestamp(self.config[nickname]["time"])), self.config[nickname]["channel"], ), ) else: self.client.reply( source, "%s was last seen %s" % (nickname, timesince(datetime.datetime.utcfromtimestamp(self.config[nickname]))), ) def record(self, message): self.config[message.prefix["nickname"]] = {"channel": message.target, "time": time()} self.config.save()
class Remind: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'remind')) manager.registerCommand('remind', 'remind', 'remind', '(?P<nickname>[^ ]+) (in (?P<channel>[^ ]+) )?(?P<message>(?:to|about|that) .+)', self.remind) manager.registerMessage('remind', self.checkReminder) def remind(self, source, nickname, channel, message): if not nickname in self.config: self.config[nickname] = [] self.config[nickname].append({ 'message': message, 'channel': None if channel == None else channel.lower(), 'from': source.prefix['nickname'], 'time': time() }) self.config.save() self.client.reply(source, 'Reminder stored', 'notice') def checkReminder(self, message): if not message.prefix['nickname'] in self.config: return nickname = message.prefix['nickname'] unsent = [] for reminder in self.config[nickname]: # Fallbacks for old reminders if not 'channel' in reminder: channel = None else: channel = reminder['channel'] if not 'time' in reminder: date = datetime.datetime.utcnow() else: date = datetime.datetime.utcfromtimestamp(reminder['time']) if channel != None and channel != message.target.lower(): unsent.append(reminder) continue self.client.reply( message, '%s, %s wants me to remind you %s (written %s)' % ( nickname, reminder['from'], reminder['message'], timesince(date) ) ) if len(unsent) > 0: self.config[nickname] = unsent else: del self.config[nickname] self.config.save()
class Factoid: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'factoid')) manager.registerCommand('factoid', 'add', 'factoid-add', '(?:(?P<channel>#[^ ]+) )?(?P<key>.+?) => (?P<value>.+?)', self.add) manager.registerCommand('factoid', 'remove', 'factoid-remove', '(?:(?P<channel>#[^ ]+) )?(?P<key>.+?)', self.remove) manager.registerCommand('factoid', 'tell', 'tell', '(?P<nickname>[^ ]+) about (?P<key>.+)', self.tell) def tell(self, source, nickname, key): channel = source.target.lower() key = key.lower() if channel in self.config and \ key in self.config[channel]: value = self.config[channel][key] elif 'global' in self.config and \ key in self.config['global']: value = self.config['global'][key] else: self.client.reply(source, 'Factoid "%s" not found' % key, 'notice') return self.client.reply(source, '%s, %s' % (nickname, value)) def add(self, source, key, value, channel = None): if channel is None: group = 'global' else: group = channel.lower() key = key.lower() if not group in self.config: self.config[group] = {} self.config[group][key] = value self.config.save() self.client.reply(source, 'Factoid "%s" has been added to %s' % (key, group), 'notice') def remove(self, source, key, channel = None): if channel is None: group = 'global' else: group = channel.lower() key = key.lower() if not group in self.config or \ not key in self.config[group]: self.client.reply(source, 'Factoid "%s" not found in %s' % (key, group), 'notice') return del self.config[group][key] self.config.save() self.client.reply(source, 'Factoid "%s" has been removed from %s' % (key, group), 'notice')
class Manual: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'manual')) manager.registerCommand('manual', 'set', 'manual-set', '(?P<channel>[^ ]+) (?P<url>[^ ]+)', self.setManualUrl) manager.registerCommand('manual', 'search', 'manual', '(?P<query>.*?)(?: for (?P<nickname>.+))?', self.search) def setManualUrl(self, source, channel, url): self.config[channel] = url self.config.save() self.client.reply(source, 'Manual URL has been set', 'notice') def search(self, source, query, nickname = None): if not source.target in self.config: self.client.reply(source, 'No manual URL has been set for this channel', 'notice') return manualUrl = self.config[source.target] params = urlencode({ 'v': '1.0', 'q': query + ' site:' + manualUrl }) url = 'http://ajax.googleapis.com/ajax/services/search/web?' + params if nickname is None: nickname = source.prefix['nickname'] getPage(url).addCallback(self._returnResult, source, nickname) def _returnResult(self, value, source, nickname): try: data = json.loads(value) except: self.client.reply(source, 'An error occured while processing the result', 'notice') return if not 'responseData' in data or \ not 'results' in data['responseData'] or \ len(data['responseData']['results']) == 0: self.client.reply(source, 'Nothing found') else: self.client.reply(source, nickname + ', see ' + data['responseData']['results'][0]['url'])
class Now: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'now')) manager.registerCommand('now', 'time', 'time', '(?P<identifier>[^ ]+)?', self.getTime) manager.registerCommand('now', 'time-set', 'time-set', '(?P<timezone>[^ ]+)', self.setTime) def setTime(self, source, timezone): try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone' % timezone, 'notice') return self.config[source.prefix['nickname']] = timezone self.config.save() self.client.reply(source, 'Your timezone has been set', 'notice') def getTime(self, source, identifier): now = datetime.datetime.fromtimestamp(time(), pytz.utc) if identifier: if identifier in self.config: timezone = self.config[identifier] else: timezone = identifier try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone' % timezone, 'notice') return now = now.astimezone(tz) self.client.reply( source, '%s (%s)' % (now.strftime('%Y-%m-%d %H:%M:%S %Z%z'), timezone)) else: self.client.reply(source, now.strftime('%Y-%m-%d %H:%M:%S %Z'))
class Remind: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'remind')) manager.registerCommand( 'remind', 'remind', 'remind', '(?P<nickname>[^ ]+) (?P<message>(?:to|about|that) .+)', self.remind) manager.registerMessage('remind', self.checkReminder) def remind(self, source, nickname, message): if not nickname in self.config: self.config[nickname] = [] self.config[nickname].append({ 'message': message, 'from': source.prefix['nickname'], 'time': time() }) self.config.save() self.client.reply(source, 'Reminder stored', 'notice') def checkReminder(self, message): if not message.prefix['nickname'] in self.config: return nickname = message.prefix['nickname'] for reminder in self.config[nickname]: # Fallback for old reminders if not 'time' in reminder: date = datetime.datetime.utcnow() else: date = datetime.datetime.utcfromtimestamp(reminder['time']) self.client.reply( message, '%s, %s wants me to remind you %s (written %s)' % (nickname, reminder['from'], reminder['message'], timesince(date))) del self.config[nickname] self.config.save()
class Remind: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'remind')) manager.registerCommand('remind', 'remind', 'remind', '(?P<nickname>[^ ]+) (?P<message>(?:to|about|that) .+)', self.remind) manager.registerMessage('remind', self.checkReminder) def remind(self, source, nickname, message): if not nickname in self.config: self.config[nickname] = [] self.config[nickname].append({ 'message': message, 'from': source.prefix['nickname'], 'time': time() }) self.config.save() self.client.reply(source, 'Reminder stored', 'notice') def checkReminder(self, message): if not message.prefix['nickname'] in self.config: return nickname = message.prefix['nickname'] for reminder in self.config[nickname]: # Fallback for old reminders if not 'time' in reminder: date = datetime.datetime.utcnow() else: date = datetime.datetime.utcfromtimestamp(reminder['time']) self.client.reply( message, '%s, %s wants me to remind you %s (written %s)' % ( nickname, reminder['from'], reminder['message'], timesince(date) ) ) del self.config[nickname] self.config.save()
class Now: help = 'https://github.com/DASPRiD/DASBiT/wiki/Now-Plugin' def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'now')) manager.registerCommand('now', 'time', 'time', '(?P<identifier>[^ ]+)?', self.getTime) manager.registerCommand('now', 'time-set', 'time-set', '(?P<timezone>[^ ]+)', self.setTime) def setTime(self, source, timezone): try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone' % timezone, 'notice') return self.config[source.prefix['nickname']] = timezone self.config.save() self.client.reply(source, 'Your timezone has been set', 'notice') def getTime(self, source, identifier): now = datetime.datetime.fromtimestamp(time(), pytz.utc) if identifier: if identifier in self.config: timezone = self.config[identifier] else: timezone = identifier try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone or identifier' % timezone, 'notice') return now = now.astimezone(tz) self.client.reply(source, 'Time for %s: %s' % (identifier, now.strftime('%A, %H:%M (%Z%z)'))) else: self.client.reply(source, 'Current time: %s' % now.strftime('%A, %H:%M (%Z%z)'))
class Now: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'now')) manager.registerCommand('now', 'time', 'time', '(?P<identifier>[^ ]+)?', self.getTime) manager.registerCommand('now', 'time-set', 'time-set', '(?P<timezone>[^ ]+)', self.setTime) def setTime(self, source, timezone): try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone' % timezone, 'notice') return self.config[source.prefix['nickname']] = timezone self.config.save() self.client.reply(source, 'Your timezone has been set', 'notice') def getTime(self, source, identifier): now = datetime.datetime.fromtimestamp(time(), pytz.utc) if identifier: if identifier in self.config: timezone = self.config[identifier] else: timezone = identifier try: tz = pytz.timezone(timezone) except pytz.UnknownTimeZoneError: self.client.reply(source, '%s is not a valid timezone' % timezone, 'notice') return now = now.astimezone(tz) self.client.reply(source, '%s (%s)' % (now.strftime('%Y-%m-%d %H:%M:%S %Z%z'), timezone)) else: self.client.reply(source, now.strftime('%Y-%m-%d %H:%M:%S %Z'))
class Remind: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'remind')) manager.registerCommand('remind', 'remind', 'remind', '(?P<nickname>[^ ]+) (?P<message>(?:to|about|that) .+)', self.remind) manager.registerMessage('remind', self.checkReminder) def remind(self, source, nickname, message): if not nickname in self.config: self.config[nickname] = [] self.config[nickname].append({ 'message': message, 'from': source.prefix['nickname'] }) self.config.save() self.client.reply(source, 'Reminder stored', 'notice') def checkReminder(self, message): if not message.prefix['nickname'] in self.config: return nickname = message.prefix['nickname'] for reminder in self.config[nickname]: self.client.reply( message, '%s, %s wants me to remind you %s' % ( nickname, reminder['from'], reminder['message'] ) ) del self.config[nickname] self.config.save()
class Channel: help = 'https://github.com/DASPRiD/DASBiT/wiki/Channel-Plugin' def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'channel')) manager.registerCommand('channel', 'join', 'channel-join', '(?P<channel>[^ ]+)(?: (?P<key>.+))?', self.join) manager.registerCommand('channel', 'part', 'channel-part', '(?P<channel>[^ ]+)', self.part) manager.registerNumeric('channel', [422, 376], self.connected) manager.registerNumeric('channel', [403, 405], self.channelError) def join(self, source, channel, key=None): self.client.join(channel, key) self.config[channel] = key self.config.save() self.client.reply(source, 'Joined %s' % channel, 'notice') def part(self, source, channel): if not channel in self.config: self.client.reply(source, 'Not present in channel %s' % channel, 'notice') return self.client.part(channel) del self.config[channel] self.config.save() self.client.reply(source, 'Parted %s' % channel, 'notice') def connected(self, message): for channel, key in self.config.iteritems(): self.client.join(channel, key) def channelError(self, message): channel = message.args[0] if channel in self.config: del self.config[channel] self.config.save()
class Channel: help = 'https://github.com/DASPRiD/DASBiT/wiki/Channel-Plugin' def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'channel')) manager.registerCommand('channel', 'join', 'channel-join', '(?P<channel>[^ ]+)(?: (?P<key>.+))?', self.join) manager.registerCommand('channel', 'part', 'channel-part', '(?P<channel>[^ ]+)', self.part) manager.registerNumeric('channel', [422, 376], self.connected) manager.registerNumeric('channel', [403, 405], self.channelError) def join(self, source, channel, key = None): self.client.join(channel, key) self.config[channel] = key self.config.save() self.client.reply(source, 'Joined %s' % channel, 'notice') def part(self, source, channel): if not channel in self.config: self.client.reply(source, 'Not present in channel %s' % channel, 'notice') return self.client.part(channel) del self.config[channel] self.config.save() self.client.reply(source, 'Parted %s' % channel, 'notice') def connected(self, message): for channel, key in self.config.iteritems(): self.client.join(channel, key) def channelError(self, message): channel = message.args[0] if channel in self.config: del self.config[channel] self.config.save()
class Twitter: def __init__(self, manager): self.client = manager.client self.parser = HTMLParser.HTMLParser() self.config = Config(os.path.join(manager.dataPath, 'twitter')) if not 'aliases' in self.config: self.config['aliases'] = {} manager.registerCommand('twitter', 'alias', 'alias-twitter', '(?P<handle>[^ ]+)', self.alias) manager.registerCommand( 'twitter', 'authenticate', 'authenticate-twitter', '(?P<key>[a-zA-Z0-9]+) (?P<secret>[a-zA-Z0-9]+)', self.authenticate) manager.registerCommand('twitter', 'lookup', 'twitter', '(?P<query>.*?)', self.lookup) def alias(self, source, handle): self.config['aliases'][source.prefix['nickname']] = handle self.config.save() self.client.reply(source, 'Alias stored', 'notice') def authenticate(self, source, key, secret): bearerTokenCredentials = base64.b64encode( '%s:%s' % (quote_plus(key), quote_plus(secret))) getPage('https://api.twitter.com/oauth2/token', method='POST', postdata='grant_type=client_credentials', headers={ 'Authorization': 'Basic %s' % bearerTokenCredentials, 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }).addCallback(self._authenticate, source).addErrback(self._error, source) def _error(self, failure, source): value = failure.value.response try: data = json.loads(value) except: self.client.reply( source, 'An error occured while processing the response', 'notice') return if 'errors' in data: self.client.reply(source, 'API failure: %s' % data['errors'][0]['message'], 'notice') return def _authenticate(self, value, source): try: data = json.loads(value) except: self.client.reply( source, 'An error occured while processing the authentication', 'notice') return if not 'token_type' in data: self.client.reply(source, 'Missing token type in authentication response', 'notice') return if not 'access_token' in data: self.client.reply( source, 'Missing access_token in authentication response', 'notice') return if data['token_type'] != 'bearer': self.client.reply(source, 'Returned token type is not bearer', 'notice') return self.config['access_token'] = data['access_token'] self.config.save() self.client.reply(source, 'Authentication succeeded', 'notice') def lookup(self, source, query): if not 'access_token' in self.config: self.client.reply(source, 'Twitter plugin not authenticated yet', 'notice') return if query.isdigit(): url = 'https://api.twitter.com/1.1/statuses/show/%s.json' % query elif len(query) > 0: url = 'https://api.twitter.com/1.1/users/show.json?%s' % urlencode( {'screen_name': query}) else: if source.prefix['nickname'] in self.config['aliases']: handle = self.config['aliases'][source.prefix['nickname']] else: handle = source.prefix['nickname'] url = 'https://api.twitter.com/1.1/users/show.json?%s' % urlencode( {'screen_name': handle}) getPage(url, method='GET', headers={ 'Authorization': 'Bearer %s' % self.config['access_token'] }).addCallback(self._returnResult, source, query.isdigit()).addErrback( self._error, source) def _returnResult(self, value, source, isNumericLookup): try: data = json.loads(value) except: self.client.reply(source, 'An error occured while processing the result', 'notice') return if isNumericLookup: user = data['user']['screen_name'] text = data['text'] id = data['id_str'] else: user = data['screen_name'] text = data['status']['text'] id = data['status']['id_str'] text = self.parser.unescape(text).replace('\n', ' ').replace('\r', '') url = 'https://twitter.com/#!/%s/status/%s' % (user, id) self.client.reply(source, '<%s> %s (%s)' % (user, text, url))
class Github: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'github')) if not 'instances' in self.config: self.config['instances'] = [] manager.registerCommand('github', 'add', 'github-add', '(?P<channel>[^ ]+) (?P<owner>[^ ]+) (?P<repository>[^ ]+)', self.add) manager.registerCommand('github', 'remove', 'github-remove', '(?P<channel>[^ ]+) (?P<owner>[^ ]+) (?P<repository>[^ ]+)', self.remove) manager.registerInterval('github', 180, self.watchForUpdates) def add(self, source, channel, owner, repository): for instance in self.config['instances']: if instance['channel'] == channel and instance['owner'] == owner and instance['repository'] == repository: self.client.reply(source, 'This project is already watched', 'notice') return d = self._getIssueUpdates(owner, repository, time.time(), 'open') d.addCallback(self._addSuccess, source, channel, owner, repository) d.addErrback(self._addError, source) def _addSuccess(self, data, source, channel, owner, repository): self.config['instances'].append({ 'channel': channel, 'owner': owner, 'repository': repository, 'last-issue-time': time.time() }) self.config.save() self.client.reply(source, 'Project added for watching', 'notice') def _addError(self, failure, source): self.client.reply(source, 'Could not reach Github instance', 'notice') def remove(self, source, channel, owner, repository): for index, instance in enumerate(self.config['instances']): if instance['channel'] == channel and instance['project'] == project and instance['repository'] == repository: del self.config['instances'][index] self.config.save() self.client.reply(source, 'Project removed from watching', 'notice') return self.client.reply(source, 'Could not find project', 'notice') def watchForUpdates(self): for instance in self.config['instances']: d = self._getIssueUpdates(instance['owner'], instance['repository'], instance['last-issue-time'], 'open') d.addCallback(self._updatesReceived, instance, instance['last-issue-time']) d.addErrback(self._watchError) d = self._getIssueUpdates(instance['owner'], instance['repository'], instance['last-issue-time'], 'closed') d.addCallback(self._updatesReceived, instance, instance['last-issue-time']) d.addErrback(self._watchError) def _watchError(self, failure): print 'Failure while watching for Github updates' def _updatesReceived(self, issues, instance, lastIssueTime): newIssueTime = time.gmtime(lastIssueTime) lastIssueTime = time.gmtime(lastIssueTime) for issue in issues: if issue['updated'] <= lastIssueTime: continue if issue['updated'] > newIssueTime: newIssueTime = issue['updated'] if not issue['labels']: labels = 'none' else: labels = ','.join(issue['labels']) self.client.sendPrivMsg( instance['channel'], '[Issue-Update:%s] [Labels:%s] [State:%s] %s, see: %s' % ( issue['number'], labels, issue['state'], issue['summary'], issue['link'] ) ) newTimestamp = timegm(newIssueTime) if newTimestamp > instance['last-issue-time']: instance['last-issue-time'] = newTimestamp self.config.save() def _getIssueUpdates(self, owner, repository, lastIssueTime, state): timeStruct = time.gmtime(lastIssueTime) url = "https://api.github.com/repos/%s/%s/issues?sort=updated&since=%s&state=%s" % ( owner, repository, time.strftime('%Y-%m-%dT%H:%M:%SZ', timeStruct), state ) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _parseIssueFeed(self, value, rd): try: issues = json.loads(value) except: rd.errback() return data = [] for issue in issues: timeStruct = time.strptime(issue['updated_at'], '%Y-%m-%dT%H:%M:%SZ') labels = [] if 'labels' in issue: for label in issue['labels']: labels.append(label['name']) data.append({ 'number': issue['number'], 'link': issue['html_url'], 'summary': issue['title'], 'state': issue['state'], 'labels': labels, 'updated': timeStruct, }) rd.callback(data)
class Jira: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'jira')) self.triggerRegex = re.compile('@(?P<key>[A-Za-z][A-Za-z0-9]*)-(?P<id>[1-9]\d*)') if not 'instances' in self.config: self.config['instances'] = [] manager.registerCommand('jira', 'add', 'jira-add', '(?P<channel>[^ ]+) (?P<url>[^ ]+) (?P<project>[^ ]+)', self.add) manager.registerCommand('jira', 'remove', 'jira-remove', '(?P<channel>[^ ]+) (?P<project>[^ ]+)', self.remove) manager.registerMessage('jira', self.lookup) manager.registerInterval('jira', 60, self.watchForUpdates) def add(self, source, channel, url, project): for instance in self.config['instances']: if instance['channel'] == channel and instance['project'] == project: self.client.reply(source, 'This project is already watched', 'notice') return d = self._getIssueUpdates(url, project, time.time()) d.addCallback(self._addSuccess, source, channel, url, project) d.addErrback(self._addError, source) def _addSuccess(self, data, source, channel, url, project): self.config['instances'].append({ 'channel': channel, 'url': url, 'project': project, 'last-issue-time': time.time() }) self.config.save() self.client.reply(source, 'Project added for watching', 'notice') def _addError(self, failure, source): self.client.reply(source, 'Could not reach Jira instance', 'notice') def remove(self, source, channel, project): for index, instance in enumerate(self.config['instances']): if instance['channel'] == channel and instance['project'] == project: del self.config['instances'][index] self.config.save() self.client.reply(source, 'Project removed from watching', 'notice') return self.client.reply(source, 'Could not find project', 'notice') def lookup(self, message): matches = self.triggerRegex.finditer(message.message) if matches is None: return for match in matches: result = match.groupdict() found = False for index, instance in enumerate(self.config['instances']): if instance['project'].lower() == result['key'].lower(): d = self._fetchIssue(message, instance, result['id']) d.addCallback(self._reportIssue, message) d.addErrback(self._reportIssueFailure, message, result['key'] + '-' + result['id']) found = True break if not found: self.client.reply(message, 'Could not find issue %s' % (result['key'] + '-' + result['id']), 'notice') def watchForUpdates(self): for instance in self.config['instances']: d = self._getIssueUpdates(instance['url'], instance['project'], instance['last-issue-time']) d.addCallback(self._updatesReceived, instance) def _updatesReceived(self, issues, instance): lastIssueTime = time.gmtime(instance['last-issue-time']) newIssueTime = time.gmtime(instance['last-issue-time']) for issue in issues: if issue['updated'] <= lastIssueTime: continue if issue['updated'] > newIssueTime: newIssueTime = issue['updated'] self.client.sendPrivMsg( instance['channel'], '[Issue-Update:%s] [Type:%s] [Status:%s] [Component:%s] %s, see: %s' % ( issue['key'], issue['type'], issue['status'], issue['component'], issue['summary'], issue['link'] ) ) instance['last-issue-time'] = timegm(newIssueTime) self.config.save() def _getIssueUpdates(self, baseUrl, project, lastIssueTime): timeStruct = time.gmtime(lastIssueTime) jqlQuery = "project = %s AND updated >= '%s' ORDER BY updated ASC, key DESC" % ( project, time.strftime('%Y-%m-%d %H:%M', timeStruct) ) url = '%s/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?%s' % ( baseUrl, urlencode([ ('jqlQuery', jqlQuery), ('field', 'key'), ('field', 'link'), ('field', 'summary'), ('field', 'type'), ('field', 'status'), ('field', 'component'), ('field', 'updated') ]) ) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _reportIssueFailure(self, failure, message, issueKey): self.client.reply(message, 'Could not find issue %s' % issueKey, 'notice') def _reportIssue(self, issues, message): issue = issues[0] self.client.reply( message, '[Issue:%s] [Type:%s] [Status:%s] [Component:%s] %s, see: %s' % ( issue['key'], issue['type'], issue['status'], issue['component'], issue['summary'], issue['link'] ) ) def _fetchIssue(self, message, instance, id): issueKey = instance['project'] + '-' + id url = '%s/si/jira.issueviews:issue-xml/%s/%s.xml' % ( instance['url'], quote_plus(issueKey), quote_plus(issueKey) ) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _parseIssueFeed(self, value, rd): try: feed = XML(value) except: rd.errback() return data = [] for item in feed.findall('channel/item'): timeStruct = time.strptime(item.find('updated').text, '%a, %d %b %Y %H:%M:%S +0000') component = item.find('component') if component is None: component = 'n/a' else: component = component.text data.append({ 'key': item.find('key').text, 'link': item.find('link').text, 'summary': item.find('summary').text, 'type': item.find('type').text, 'status': item.find('status').text, 'component': component, 'updated': timeStruct, }) rd.callback(data)
class Log: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'log')) self.logs = {} self.logPath = os.path.join(manager.dataPath, 'logs') if not os.path.exists(self.logPath): os.mkdir(self.logPath) manager.registerCommand('log', 'enable', 'log-enable', '(?P<channel>[^ ]+)', self.enable) manager.registerCommand('log', 'disable', 'log-disable', '(?P<channel>[^ ]+)', self.disable) manager.registerMessage('log', self.logMessage) def enable(self, source, channel): self.config[channel] = True self.config.save() self.client.reply(source, 'Logging has been enabled for %s' % channel, 'notice') def disable(self, source, channel): if not channel in self.config: self.client.reply(source, 'Logging is not enabled for %s' % channel, 'notice') return del self.config[channel] self.config.save() if channel in self.logs: self.logs[channel]['fp'].close() del self.logs[channel] self.client.reply(source, 'Logging has been disabled for %s' % channel, 'notice') def logMessage(self, message): if not message.target in self.config: return channel = message.target now = time.time() nowStruct = time.gmtime(now) date = time.strftime('%Y-%m-%d', nowStruct) if not channel in self.logs: self.logs[channel] = {'date': date, 'fp': None} elif self.logs[channel]['date'] != date: self.logs[channel]['fp'].close() self.logs[channel]['fp'] = None self.logs[channel]['date'] = date if self.logs[channel]['fp'] is None: channelPath = os.path.join(self.logPath, channel) yearPath = os.path.join(channelPath, time.strftime('%Y', nowStruct)) monthPath = os.path.join(yearPath, time.strftime('%m', nowStruct)) dayPath = os.path.join(monthPath, time.strftime('%d', nowStruct)) if not os.path.exists(channelPath): os.mkdir(channelPath) if not os.path.exists(yearPath): os.mkdir(yearPath) if not os.path.exists(monthPath): os.mkdir(monthPath) self.logs[channel]['fp'] = open(dayPath, 'ab') self.logs[channel]['fp'].write( struct.pack('<LB', int(now), len(message.prefix['nickname'])) + message.prefix['nickname'] + struct.pack('<H', len(message.message)) + message.message)
class Jira: help = 'https://github.com/DASPRiD/DASBiT/wiki/Jira-Plugin' def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'jira')) self.triggerRegex = re.compile('@(?P<key>[A-Za-z][A-Za-z0-9]*)-(?P<id>[1-9]\d*)') if not 'instances' in self.config: self.config['instances'] = [] manager.registerCommand('jira', 'add', 'jira-add', '(?P<channel>[^ ]+) (?P<url>[^ ]+) (?P<project>[^ ]+)', self.add) manager.registerCommand('jira', 'remove', 'jira-remove', '(?P<channel>[^ ]+) (?P<project>[^ ]+)', self.remove) manager.registerMessage('jira', self.lookup) manager.registerInterval('jira', 60, self.watchForUpdates) def add(self, source, channel, url, project): for instance in self.config['instances']: if instance['channel'] == channel and instance['project'] == project: self.client.reply(source, 'This project is already watched', 'notice') return d = self._getIssueUpdates(url, project, time.time()) d.addCallback(self._addSuccess, source, channel, url, project) d.addErrback(self._addError, source) def _addSuccess(self, data, source, channel, url, project): self.config['instances'].append({ 'channel': channel, 'url': url, 'project': project, 'last-issue-time': time.time() }) self.config.save() self.client.reply(source, 'Project added for watching', 'notice') def _addError(self, failure, source): self.client.reply(source, 'Could not reach Jira instance', 'notice') def remove(self, source, channel, project): for index, instance in enumerate(self.config['instances']): if instance['channel'] == channel and instance['project'] == project: del self.config['instances'][index] self.config.save() self.client.reply(source, 'Project removed from watching', 'notice') return self.client.reply(source, 'Could not find project', 'notice') def lookup(self, message): matches = self.triggerRegex.finditer(message.message) if matches is None: return for match in matches: result = match.groupdict() found = False for index, instance in enumerate(self.config['instances']): if instance['project'].lower() == result['key'].lower(): d = self._fetchIssue(message, instance, result['id']) d.addCallback(self._reportIssue, message) d.addErrback(self._reportIssueFailure, message, result['key'] + '-' + result['id']) found = True break if not found: self.client.reply(message, 'Could not find issue %s' % (result['key'] + '-' + result['id']), 'notice') def watchForUpdates(self): for instance in self.config['instances']: d = self._getIssueUpdates(instance['url'], instance['project'], instance['last-issue-time']) d.addCallback(self._updatesReceived, instance) def _updatesReceived(self, issues, instance): lastIssueTime = time.gmtime(instance['last-issue-time']) newIssueTime = time.gmtime(instance['last-issue-time']) for issue in issues: if issue['updated'] <= lastIssueTime: continue if issue['updated'] > newIssueTime: newIssueTime = issue['updated'] self.client.sendPrivMsg( instance['channel'], '[Issue-Update:%s] [Type:%s] [Status:%s] [Component:%s] %s, see: %s' % ( issue['key'], issue['type'], issue['status'], issue['component'], issue['summary'], issue['link'] ) ) instance['last-issue-time'] = timegm(newIssueTime) self.config.save() def _getIssueUpdates(self, baseUrl, project, lastIssueTime): timeStruct = time.gmtime(lastIssueTime) jqlQuery = "project = %s AND updated >= '%s' ORDER BY updated ASC, key DESC" % ( project, time.strftime('%Y-%m-%d %H:%M', timeStruct) ) url = '%s/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?%s' % ( baseUrl, urlencode([ ('jqlQuery', jqlQuery), ('field', 'key'), ('field', 'link'), ('field', 'summary'), ('field', 'type'), ('field', 'status'), ('field', 'component'), ('field', 'updated') ]) ) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _reportIssueFailure(self, failure, message, issueKey): self.client.reply(message, 'Could not find issue %s' % issueKey, 'notice') def _reportIssue(self, issues, message): issue = issues[0] self.client.reply( message, '[Issue:%s] [Type:%s] [Status:%s] [Component:%s] %s, see: %s' % ( issue['key'], issue['type'], issue['status'], issue['component'], issue['summary'], issue['link'] ) ) def _fetchIssue(self, message, instance, id): issueKey = instance['project'] + '-' + id url = '%s/si/jira.issueviews:issue-xml/%s/%s.xml' % ( instance['url'], quote_plus(issueKey), quote_plus(issueKey) ) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _parseIssueFeed(self, value, rd): try: feed = XML(value) except: rd.errback() return data = [] for item in feed.findall('channel/item'): timeStruct = time.strptime(item.find('updated').text, '%a, %d %b %Y %H:%M:%S +0000') component = item.find('component') if component is None: component = 'n/a' else: component = component.text data.append({ 'key': item.find('key').text, 'link': item.find('link').text, 'summary': item.find('summary').text, 'type': item.find('type').text, 'status': item.find('status').text, 'component': component, 'updated': timeStruct, }) rd.callback(data)
class User: help = 'https://github.com/DASPRiD/DASBiT/wiki/User-Plugin' def __init__(self, manager): self.manager = manager self.client = manager.client self.config = Config(os.path.join(self.manager.dataPath, 'acl')) self.acl = {} for username, aclString in self.config.iteritems(): self.acl[username] = Acl(aclString) self.identToUsername = {} self.nicknameToIdent = {} self.nicknameDefers = {} manager.registerCommand('user', None, 'master', None, self.setMaster) manager.registerCommand('user', 'acl', 'acl-set', '(?P<username>[^ ]+) (?P<aclString>.+)', self.setAcl) manager.registerCommand('user', 'acl', 'acl-add', '(?P<username>[^ ]+) (?P<aclString>.+)', self.addAcl) manager.registerCommand('user', 'acl', 'acl-remove', '(?P<username>[^ ]+) (?P<aclString>.+)', self.removeAcl) manager.registerCommand('user', 'acl', 'acl-show', '(?P<username>[^ ]+)', self.showAcl) manager.registerNumeric('user', [318, 330], self.whoisReceived) def setMaster(self, source): if len(self.acl) > 0: self.client.reply(source, 'Master has already been set', 'notice') return d = defer.maybeDeferred(self._determineUsername, source.prefix) d.addCallback(self._storeMaster, source) def _storeMaster(self, username, source): self.acl[username] = Acl('*.*') self.config[username] = repr(self.acl[username]) self.config.save() self.client.reply(source, 'You are now the master', 'notice') def setAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'set') def addAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'add') def removeAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'remove') def _storeAcl(self, source, username, aclString, mode): if username in self.acl: self.acl[username].modify(aclString, mode) elif mode != 'remove': self.acl[username] = Acl(aclString) self.config[username] = repr(self.acl[username]) self.config.save() self.client.reply(source, 'ACL has been modified', 'notice') def showAcl(self, source, username): if username in self.acl: self.client.reply( source, 'ACL for %s: %s' % (username, repr(self.acl[username])), 'notice') else: self.client.reply(source, 'No ACL for %s found' % username, 'notice') def verifyAccess(self, source, permission): if '*' in self.acl and self.acl['*'].isAllowed(permission): return True rd = defer.Deferred() d = defer.maybeDeferred(self._determineUsername, source.prefix) d.addCallback(self._checkAcl, rd, permission) return rd def _checkAcl(self, username, rd, permission): if not username in self.acl: rd.callback(False) else: rd.callback(self.acl[username].isAllowed(permission)) def _determineUsername(self, prefix): if prefix['ident'] in self.identToUsername: return self.identToUsername[prefix['ident']] if not prefix['nickname'] in self.nicknameDefers: self.nicknameToIdent[prefix['nickname']] = prefix['ident'] self.client.send('WHOIS', prefix['nickname']) self.nicknameDefers[prefix['nickname']] = defer.Deferred() return self.nicknameDefers[prefix['nickname']] def whoisReceived(self, message): if message.command == 330: nickname = message.args[1] username = message.args[2] self.identToUsername[self.nicknameToIdent[nickname]] = username if nickname in self.nicknameDefers: self.nicknameDefers[nickname].callback(username) del self.nicknameDefers[nickname] elif message.command == 318: nickname = message.args[1] if nickname in self.nicknameDefers: self.client.sendNotice(nickname, 'You are not identified with NickServ') del self.nicknameDefers[nickname]
class Twitter: help = 'https://github.com/DASPRiD/DASBiT/wiki/Twitter-Plugin' def __init__(self, manager): self.client = manager.client self.parser = HTMLParser.HTMLParser() self.config = Config(os.path.join(manager.dataPath, 'twitter')) if not 'aliases' in self.config: self.config['aliases'] = {} manager.registerCommand('twitter', 'alias', 'alias-twitter', '(?P<handle>[^ ]+)', self.alias) manager.registerCommand('twitter', 'authenticate', 'authenticate-twitter', '(?P<key>[a-zA-Z0-9]+) (?P<secret>[a-zA-Z0-9]+)', self.authenticate) manager.registerCommand('twitter', 'lookup', 'twitter', '(?P<query>.*?)', self.lookup) def alias(self, source, handle): self.config['aliases'][source.prefix['nickname']] = handle self.config.save() self.client.reply(source, 'Alias stored', 'notice') def authenticate(self, source, key, secret): bearerTokenCredentials = base64.b64encode('%s:%s' % (quote_plus(key), quote_plus(secret))) getPage( 'https://api.twitter.com/oauth2/token', method = 'POST', postdata = 'grant_type=client_credentials', headers = { 'Authorization' : 'Basic %s' % bearerTokenCredentials, 'Content-Type' : 'application/x-www-form-urlencoded;charset=UTF-8' } ).addCallback(self._authenticate, source).addErrback(self._error, source) def _error(self, failure, source): value = failure.value.response try: data = json.loads(value) except: self.client.reply(source, 'An error occured while processing the response', 'notice') return if 'errors' in data: self.client.reply(source, 'API failure: %s' % data['errors'][0]['message'], 'notice') return def _authenticate(self, value, source): try: data = json.loads(value) except: self.client.reply(source, 'An error occured while processing the authentication', 'notice') return if not 'token_type' in data: self.client.reply(source, 'Missing token type in authentication response', 'notice') return if not 'access_token' in data: self.client.reply(source, 'Missing access_token in authentication response', 'notice') return if data['token_type'] != 'bearer': self.client.reply(source, 'Returned token type is not bearer', 'notice') return self.config['access_token'] = data['access_token'] self.config.save() self.client.reply(source, 'Authentication succeeded', 'notice') def lookup(self, source, query): if not 'access_token' in self.config: self.client.reply(source, 'Twitter plugin not authenticated yet', 'notice') return if query.isdigit(): url = 'https://api.twitter.com/1.1/statuses/show/%s.json' % query elif len(query) > 0: url = 'https://api.twitter.com/1.1/users/show.json?%s' % urlencode({'screen_name' : query}) else: if source.prefix['nickname'] in self.config['aliases']: handle = self.config['aliases'][source.prefix['nickname']] else: handle = source.prefix['nickname'] url = 'https://api.twitter.com/1.1/users/show.json?%s' % urlencode({'screen_name' : handle}) getPage( url, method = 'GET', headers = {'Authorization' : 'Bearer %s' % self.config['access_token']} ).addCallback(self._returnResult, source, query.isdigit()).addErrback(self._error, source) def _returnResult(self, value, source, isNumericLookup): try: data = json.loads(value) except: self.client.reply(source, 'An error occured while processing the result', 'notice') return if isNumericLookup: user = data['user']['screen_name'] text = data['text'] id = data['id_str'] else: user = data['screen_name'] text = data['status']['text'] id = data['status']['id_str'] text = self.parser.unescape(text).replace('\n', ' ').replace('\r', '') url = 'https://twitter.com/#!/%s/status/%s' % (user, id) self.client.reply(source, '<%s> %s (%s)' % (user, text, url))
class Github: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'github')) if not 'instances' in self.config: self.config['instances'] = [] manager.registerCommand( 'github', 'add', 'github-add', '(?P<channel>[^ ]+) (?P<owner>[^ ]+) (?P<repository>[^ ]+)', self.add) manager.registerCommand( 'github', 'remove', 'github-remove', '(?P<channel>[^ ]+) (?P<owner>[^ ]+) (?P<repository>[^ ]+)', self.remove) manager.registerInterval('github', 180, self.watchForUpdates) def add(self, source, channel, owner, repository): for instance in self.config['instances']: if instance['channel'] == channel and instance[ 'owner'] == owner and instance['repository'] == repository: self.client.reply(source, 'This project is already watched', 'notice') return d = self._getIssueUpdates(owner, repository, time.time(), 'open') d.addCallback(self._addSuccess, source, channel, owner, repository) d.addErrback(self._addError, source) def _addSuccess(self, data, source, channel, owner, repository): self.config['instances'].append({ 'channel': channel, 'owner': owner, 'repository': repository, 'last-issue-time': time.time() }) self.config.save() self.client.reply(source, 'Project added for watching', 'notice') def _addError(self, failure, source): self.client.reply(source, 'Could not reach Github instance', 'notice') def remove(self, source, channel, owner, repository): for index, instance in enumerate(self.config['instances']): if instance['channel'] == channel and instance[ 'project'] == project and instance[ 'repository'] == repository: del self.config['instances'][index] self.config.save() self.client.reply(source, 'Project removed from watching', 'notice') return self.client.reply(source, 'Could not find project', 'notice') def watchForUpdates(self): for instance in self.config['instances']: d = self._getIssueUpdates(instance['owner'], instance['repository'], instance['last-issue-time'], 'open') d.addCallback(self._updatesReceived, instance, instance['last-issue-time']) d.addErrback(self._watchError) d = self._getIssueUpdates(instance['owner'], instance['repository'], instance['last-issue-time'], 'closed') d.addCallback(self._updatesReceived, instance, instance['last-issue-time']) d.addErrback(self._watchError) def _watchError(self, failure): print 'Failure while watching for Github updates' def _updatesReceived(self, issues, instance, lastIssueTime): newIssueTime = time.gmtime(lastIssueTime) lastIssueTime = time.gmtime(lastIssueTime) for issue in issues: if issue['updated'] <= lastIssueTime: continue if issue['updated'] > newIssueTime: newIssueTime = issue['updated'] if not issue['labels']: labels = 'none' else: labels = ','.join(issue['labels']) self.client.sendPrivMsg( instance['channel'], '[Issue-Update:%s] [Labels:%s] [State:%s] %s, see: %s' % (issue['number'], labels, issue['state'], issue['summary'], issue['link'])) newTimestamp = timegm(newIssueTime) if newTimestamp > instance['last-issue-time']: instance['last-issue-time'] = newTimestamp self.config.save() def _getIssueUpdates(self, owner, repository, lastIssueTime, state): timeStruct = time.gmtime(lastIssueTime) url = "https://api.github.com/repos/%s/%s/issues?sort=updated&since=%s&state=%s" % ( owner, repository, time.strftime('%Y-%m-%dT%H:%M:%SZ', timeStruct), state) rd = defer.Deferred() pd = getPage(url) pd.addCallback(self._parseIssueFeed, rd) pd.addErrback(rd.errback) return rd def _parseIssueFeed(self, value, rd): try: issues = json.loads(value) except: rd.errback() return data = [] for issue in issues: timeStruct = time.strptime(issue['updated_at'], '%Y-%m-%dT%H:%M:%SZ') labels = [] if 'labels' in issue: for label in issue['labels']: labels.append(label['name']) data.append({ 'number': issue['number'], 'link': issue['html_url'], 'summary': issue['title'], 'state': issue['state'], 'labels': labels, 'updated': timeStruct, }) rd.callback(data)
class Log: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'log')) self.logs = {} self.logPath = os.path.join(manager.dataPath, 'logs') if not os.path.exists(self.logPath): os.mkdir(self.logPath) manager.registerCommand('log', 'enable', 'log-enable', '(?P<channel>[^ ]+)', self.enable) manager.registerCommand('log', 'disable', 'log-disable', '(?P<channel>[^ ]+)', self.disable) manager.registerMessage('log', self.logMessage) def enable(self, source, channel): self.config[channel] = True self.config.save() self.client.reply(source, 'Logging has been enabled for %s' % channel, 'notice') def disable(self, source, channel): if not channel in self.config: self.client.reply(source, 'Logging is not enabled for %s' % channel, 'notice') return del self.config[channel] self.config.save() if channel in self.logs: self.logs[channel]['fp'].close() del self.logs[channel] self.client.reply(source, 'Logging has been disabled for %s' % channel, 'notice') def logMessage(self, message): if not message.target in self.config: return channel = message.target now = time.time() nowStruct = time.gmtime(now) date = time.strftime('%Y-%m-%d', nowStruct) if not channel in self.logs: self.logs[channel] = { 'date': date, 'fp': None } elif self.logs[channel]['date'] != date: self.logs[channel]['fp'].close() self.logs[channel]['fp'] = None self.logs[channel]['date'] = date if self.logs[channel]['fp'] is None: channelPath = os.path.join(self.logPath, channel) yearPath = os.path.join(channelPath, time.strftime('%Y', nowStruct)) monthPath = os.path.join(yearPath, time.strftime('%m', nowStruct)) dayPath = os.path.join(monthPath, time.strftime('%d', nowStruct)) if not os.path.exists(channelPath): os.mkdir(channelPath) if not os.path.exists(yearPath): os.mkdir(yearPath) if not os.path.exists(monthPath): os.mkdir(monthPath) self.logs[channel]['fp'] = open(dayPath, 'ab') self.logs[channel]['fp'].write(struct.pack( '<LB', int(now), len(message.prefix['nickname']) ) + message.prefix['nickname'] + struct.pack( '<H', len(message.message) ) + message.message)
class User: help = 'https://github.com/DASPRiD/DASBiT/wiki/User-Plugin' def __init__(self, manager): self.manager = manager self.client = manager.client self.config = Config(os.path.join(self.manager.dataPath, 'acl')) self.acl = {} for username, aclString in self.config.iteritems(): self.acl[username] = Acl(aclString) self.identToUsername = {} self.nicknameToIdent = {} self.nicknameDefers = {} manager.registerCommand('user', None, 'master', None, self.setMaster) manager.registerCommand('user', 'acl', 'acl-set', '(?P<username>[^ ]+) (?P<aclString>.+)', self.setAcl) manager.registerCommand('user', 'acl', 'acl-add', '(?P<username>[^ ]+) (?P<aclString>.+)', self.addAcl) manager.registerCommand('user', 'acl', 'acl-remove', '(?P<username>[^ ]+) (?P<aclString>.+)', self.removeAcl) manager.registerCommand('user', 'acl', 'acl-show', '(?P<username>[^ ]+)', self.showAcl) manager.registerNumeric('user', [318, 330], self.whoisReceived) def setMaster(self, source): if len(self.acl) > 0: self.client.reply(source, 'Master has already been set', 'notice') return d = defer.maybeDeferred(self._determineUsername, source.prefix) d.addCallback(self._storeMaster, source) def _storeMaster(self, username, source): self.acl[username] = Acl('*.*') self.config[username] = repr(self.acl[username]) self.config.save() self.client.reply(source, 'You are now the master', 'notice') def setAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'set') def addAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'add') def removeAcl(self, source, username, aclString): self._storeAcl(source, username, aclString, 'remove') def _storeAcl(self, source, username, aclString, mode): if username in self.acl: self.acl[username].modify(aclString, mode) elif mode != 'remove': self.acl[username] = Acl(aclString) self.config[username] = repr(self.acl[username]) self.config.save() self.client.reply(source, 'ACL has been modified', 'notice') def showAcl(self, source, username): if username in self.acl: self.client.reply(source, 'ACL for %s: %s' % (username, repr(self.acl[username])), 'notice') else: self.client.reply(source, 'No ACL for %s found' % username, 'notice') def verifyAccess(self, source, permission): if '*' in self.acl and self.acl['*'].isAllowed(permission): return True rd = defer.Deferred() d = defer.maybeDeferred(self._determineUsername, source.prefix) d.addCallback(self._checkAcl, rd, permission) return rd def _checkAcl(self, username, rd, permission): if not username in self.acl: rd.callback(False) else: rd.callback(self.acl[username].isAllowed(permission)) def _determineUsername(self, prefix): if prefix['ident'] in self.identToUsername: return self.identToUsername[prefix['ident']] if not prefix['nickname'] in self.nicknameDefers: self.nicknameToIdent[prefix['nickname']] = prefix['ident'] self.client.send('WHOIS', prefix['nickname']) self.nicknameDefers[prefix['nickname']] = defer.Deferred() return self.nicknameDefers[prefix['nickname']] def whoisReceived(self, message): if message.command == 330: nickname = message.args[1] username = message.args[2] self.identToUsername[self.nicknameToIdent[nickname]] = username if nickname in self.nicknameDefers: self.nicknameDefers[nickname].callback(username) del self.nicknameDefers[nickname] elif message.command == 318: nickname = message.args[1] if nickname in self.nicknameDefers: self.client.sendNotice(nickname, 'You are not identified with NickServ') del self.nicknameDefers[nickname]
class UriLookup: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'log')) manager.registerCommand('urilookup', 'enable', 'urilookup-enable', '(?P<channel>[^ ]+)', self.enable) manager.registerCommand('urilookup', 'disable', 'urilookup-disable', '(?P<channel>[^ ]+)', self.disable) manager.registerMessage('urilookup', self.lookupUri) def enable(self, source, channel): self.config[channel] = True self.config.save() self.client.reply(source, 'URI lookup has been enabled for %s' % channel, 'notice') def disable(self, source, channel): if not channel in self.config: self.client.reply(source, 'URI lookup is not enabled for %s' % channel, 'notice') return del self.config[channel] self.config.save() self.client.reply(source, 'URI lookup has been disabled for %s' % channel, 'notice') def lookupUri(self, message): if not message.target in self.config: return for uri in re.findall("https?://[^\s]+", message.message): treq.get(uri, unbuffered = True).addCallbacks( callback = self._gotResponse, errback = self._errorResult, callbackArgs = (message, uri), errbackArgs = (message, uri) ) def _gotResponse(self, response, message, uri): if response.headers.hasHeader('content-type'): mimeType = response.headers.getRawHeaders('content-type')[0].split(';')[0].strip().lower() if mimeType == 'text/html': d = Deferred() d.addCallbacks( callback = self._successResult, callbackArgs = (message, uri) ) response.deliverBody(_BodyCollector(d)) return self._prepareResult(uri, mimeType, message) return self._prepareResult(uri, 'Unknown', message) def _successResult(self, html, message, uri): tree = lxml.html.fromstring(html) titleElement = tree.find('./head/title') if titleElement == None: title = 'Undefined' else: title = titleElement.text title = (title[:40] + '...') if len(title) > 40 else title self._prepareResult(uri, title, message) def _errorResult(self, error, message, uri): self._prepareResult(uri, 'Error retrieving URI', message) def _prepareResult(self, uri, title, message): if len(uri) > 25: apiUri = 'http://tinyurl.com/api-create.php?%s' % urlencode({ 'url': uri }) getPage(apiUri).addCallback(self._returnResult, title, message) else: self._returnResult(uri, title, message) def _returnResult(self, uri, title, message): self.client.reply(message, '[ %s ] %s' % (uri, title))
class Factoid: def __init__(self, manager): self.client = manager.client self.config = Config(os.path.join(manager.dataPath, 'factoid')) manager.registerCommand( 'factoid', 'add', 'factoid-add', '(?:(?P<channel>#[^ ]+) )?(?P<key>.+?) => (?P<value>.+?)', self.add) manager.registerCommand('factoid', 'remove', 'factoid-remove', '(?:(?P<channel>#[^ ]+) )?(?P<key>.+?)', self.remove) manager.registerCommand('factoid', 'tell', 'tell', '(?P<nickname>[^ ]+) about (?P<key>.+)', self.tell) def tell(self, source, nickname, key): channel = source.target.lower() key = key.lower() if channel in self.config and \ key in self.config[channel]: value = self.config[channel][key] elif 'global' in self.config and \ key in self.config['global']: value = self.config['global'][key] else: self.client.reply(source, 'Factoid "%s" not found' % key, 'notice') return self.client.reply(source, '%s, %s' % (nickname, value)) def add(self, source, key, value, channel=None): if channel is None: group = 'global' else: group = channel.lower() key = key.lower() if not group in self.config: self.config[group] = {} self.config[group][key] = value self.config.save() self.client.reply(source, 'Factoid "%s" has been added to %s' % (key, group), 'notice') def remove(self, source, key, channel=None): if channel is None: group = 'global' else: group = channel.lower() key = key.lower() if not group in self.config or \ not key in self.config[group]: self.client.reply(source, 'Factoid "%s" not found in %s' % (key, group), 'notice') return del self.config[group][key] self.config.save() self.client.reply( source, 'Factoid "%s" has been removed from %s' % (key, group), 'notice')