Example #1
0
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()
Example #2
0
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()
Example #3
0
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()
Example #4
0
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')
Example #5
0
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'])
Example #6
0
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'))
Example #7
0
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()
Example #8
0
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()
Example #9
0
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)'))
Example #10
0
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'))
Example #11
0
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()
Example #12
0
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()
Example #13
0
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()
Example #14
0
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))
Example #15
0
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)
Example #16
0
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)
Example #17
0
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)
Example #18
0
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)
Example #19
0
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]
Example #20
0
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))
Example #21
0
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)
Example #22
0
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)
Example #23
0
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]
Example #24
0
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))
Example #25
0
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')