Beispiel #1
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        wtfsimfd = "http://whatthefuckshouldimakefordinner.com/{}"

        options = {'meat': 'index.php', 'veg': 'veg.php', 'drink': 'drinks.php'}

        option = 'meat'
        if len(message.ParameterList) > 0:
            option = message.ParameterList[0]

        if option in options:
            webPage = WebUtils.fetchURL(wtfsimfd.format(options[option]))

            soup = BeautifulSoup(webPage.body)

            phrase = soup.find('dl').text
            item = soup.find('a')
            link = WebUtils.shortenGoogl(item['href'])

            return IRCResponse(ResponseType.Say,
                               u"{}... {} {}".format(phrase, item.text, link),
                               message.ReplyTo)

        else:
            error = u"'{}' is not a recognized dinner type, please choose one of {}"\
                .format(option, u'/'.join(options.keys()))
            return IRCResponse(ResponseType.Say, error, message.ReplyTo)
Beispiel #2
0
    def FollowTwitch(self, channel, message):
        # Heavily based on Didero's DideRobot code for the same
        # https://github.com/Didero/DideRobot/blob/06629fc3c8bddf8f729ce2d27742ff999dfdd1f6/commands/urlTitleFinder.py#L37
        # TODO: other stats?
        chanData = {}
        channelOnline = False
        twitchHeaders = [('Accept', 'application/vnd.twitchtv.v2+json')]
        webPage = WebUtils.fetchURL(u'https://api.twitch.tv/kraken/streams/{0}'.format(channel), twitchHeaders)

        streamData = json.loads(webPage.body)

        if 'stream' in streamData and streamData['stream'] is not None:
            chanData = streamData['stream']['channel']
            channelOnline = True
        elif 'error' not in streamData:
            webPage = WebUtils.fetchURL(u'https://api.twitch.tv/kraken/channels/{0}'.format(channel), twitchHeaders)
            chanData = json.loads(webPage.body)

        if len(chanData) > 0:
            if channelOnline:
                channelInfo = assembleFormattedText(A.fg.green['']) + '{0}'.format(chanData['display_name']) + assembleFormattedText(A.normal[''])
            else:
                channelInfo = assembleFormattedText(A.fg.red['']) + '{0}'.format(chanData['display_name']) + assembleFormattedText(A.normal[''])
            channelInfo += u' "{0}"'.format(re.sub('[\r\n]+', self.graySplitter, chanData['status'].strip()))
            if chanData['game'] is not None:
                channelInfo += assembleFormattedText(A.normal[A.fg.gray[', playing '], '{0}'.format(chanData['game'])])
            if chanData['mature']:
                channelInfo += assembleFormattedText(A.normal[A.fg.lightRed[' [Mature]']])
            if channelOnline:
                channelInfo += assembleFormattedText(A.normal[A.fg.green[' (Live with {0:,d} viewers)'.format(streamData['stream']['viewers'])]])
            else:
                channelInfo += assembleFormattedText(A.normal[A.fg.red[' (Offline)']])

            return IRCResponse(ResponseType.Say, channelInfo, message.ReplyTo)
Beispiel #3
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) == 0:
            return IRCResponse(ResponseType.Say,
                               "You didn't give a word! Usage: {0}".format(self.help),
                               message.ReplyTo)
        
        word = message.Parameters
        
        webPage = WebUtils.fetchURL('http://www.etymonline.com/index.php?allowed_in_frame=0&search={0}'.format(word))
        root = BeautifulSoup(webPage.body)
        etymTitle = root.find('dt')
        etymDef = root.find('dd')
        
        if not etymTitle or not etymDef:
            return IRCResponse(ResponseType.Say,
                               "No etymology found for '{0}'".format(word),
                               message.ReplyTo)

        response = "{0}: {1}".format(etymTitle.text.strip(), etymDef.text.strip())
        
        return IRCResponse(ResponseType.Say,
                           response,
                           message.ReplyTo)
Beispiel #4
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) < 3:
            return IRCResponse(ResponseType.Say, self.help, message.ReplyTo)

        try:
            amount = float(message.ParameterList[0])
            offset = 1
        except ValueError:
            amount = 1.0
            offset = 0

        ccFrom = message.ParameterList[offset]
        ccTo   = message.ParameterList[offset+2:]
        ccTo   = ",".join(ccTo)

        url = "https://api.fixer.io/latest?base={}&symbols={}"
        url = url.format(ccFrom, ccTo)
        response = WebUtils.fetchURL(url)
        jsonResponse = json.loads(response.body)
        rates = jsonResponse['rates']

        if not rates:
            return IRCResponse(ResponseType.Say,
                               "Some or all of those currencies weren't recognized!",
                               message.ReplyTo)

        data = []
        for curr,rate in rates.iteritems():
            data.append("{} {}".format(rate*amount, curr))

        graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' '])
        return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
Beispiel #5
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) > 0:
            if self.api_key is None:
                return IRCResponse(ResponseType.Say, "[Bing Maps API key not found]", message.ReplyTo)

            url = "http://dev.virtualearth.net/REST/v1/Locations?q={0}&key={1}".format(urllib.quote_plus(message.Parameters), self.api_key)

            page = WebUtils.fetchURL(url)
            result = json.loads(page.body)

            if result['resourceSets'][0]['estimatedTotal'] == 0:
                print result
                return IRCResponse(ResponseType.Say,
                                   "Couldn't find GPS coords for '{0}', sorry!".format(message.Parameters),
                                   message.ReplyTo)

            coords = result['resourceSets'][0]['resources'][0]['point']['coordinates']

            return IRCResponse(ResponseType.Say,
                               "GPS coords for '{0}' are: {1},{2}".format(message.Parameters, coords[0], coords[1]),
                               message.ReplyTo)

        else:
            return IRCResponse(ResponseType.Say,
                               "You didn't give an address to look up",
                               message.ReplyTo)
Beispiel #6
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) != 1:
            return IRCResponse(ResponseType.Say, self.help, message.ReplyTo)

        subreddit = message.ParameterList[0].lower()

        url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}"
        url = url.format(subreddit, random.randint(0, 100))
        response = WebUtils.fetchURL(url, self.headers)
        jsonResponse = json.loads(response.body)
        images = jsonResponse['data']

        if not images:
            return IRCResponse(ResponseType.Say,
                               "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)"
                               .format(subreddit),
                               message.ReplyTo)

        image = random.choice(images)

        data = []
        if image['title'] is not None:
            data.append(image['title'])
        if image['nsfw']:
            data.append(u'\x034\x02NSFW!\x0F')
        if image['animated']:
            data.append(u'\x032\x02Animated!\x0F')
        data.append(image['link'])

        graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' '])
        return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
Beispiel #7
0
    def FollowTwitter(self, tweeter, tweetID, message):
        webPage = WebUtils.fetchURL('https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID))

        soup = BeautifulSoup(webPage.body)

        tweet = soup.find(class_='permalink-tweet')
        
        user = tweet.find(class_='username').text

        tweetText = tweet.find(class_='tweet-text')
        
        tweetTimeText = tweet.find(class_='client-and-actions').text.strip()
        try:
            tweetTimeText = time.strftime('%Y/%m/%d %H:%M', time.strptime(tweetTimeText, '%I:%M %p - %d %b %Y'))
        except ValueError:
            pass

        links = tweetText.find_all('a', {'data-expanded-url': True})
        for link in links:
            link.string = ' ' + link['data-expanded-url']

        embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'})
        for link in embeddedLinks:
            link.string = ' ' + link['href']

        text = StringUtils.unescapeXHTML(tweetText.text)
        text = re.sub('[\r\n]+', self.graySplitter, text)

        formatString = unicode(assembleFormattedText(A.normal[A.fg.gray['[{0}]'], A.bold[' {1}:'], ' {2}']))

        return IRCResponse(ResponseType.Say,
                           formatString.format(tweetTimeText, user, text),
                           message.ReplyTo,
                           {'urlfollowURL': 'https://twitter.com/{}/status/{}'.format(tweeter, tweetID)})
Beispiel #8
0
 def execute(self, message):
     """
     @type message: IRCMessage
     """
     if len(message.ParameterList) == 0:
         return IRCResponse(ResponseType.Say,
                            "You didn't give an expression to calculate! {0}".format(self.help),
                            message.ReplyTo)
     
     query = urllib.quote(message.Parameters.encode('utf8'))
     j = WebUtils.sendToServer('http://www.google.com/ig/calculator?hl=en&q=' + query)
     j = re.sub(r"{\s*'?(\w)", r'{"\1', j)
     j = re.sub(r",\s*'?(\w)", r',"\1', j)
     j = re.sub(r"(\w)'?\s*:", r'\1":', j)
     j = re.sub(r":\s*'(\w)'\s*([,}])", r':"\1"\2', j)
     j = self.htmlParser.unescape(j)
     j = j.replace('\xa0', ',')
     j = j.replace('\\x3c', '<')
     j = j.replace('\\x3e', '>')
     j = j.replace('<sup>', '^(')
     j = j.replace('</sup>', ')')
     print j
     result = json.loads(j)
     
     if len(result['rhs']) > 0:
         return IRCResponse(ResponseType.Say,
                            "{1}'s Result: {0}".format(result['rhs'], message.User.Name),
                            message.ReplyTo)
     if len(result['error']) > 0:
         return IRCResponse(ResponseType.Say,
                            'Calculation Error or Unsupported Operations',
                            message.ReplyTo)
Beispiel #9
0
    def FollowTwitter(self, tweeter, tweetID, message):
        webPage = WebUtils.fetchURL('https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID))

        soup = BeautifulSoup(webPage.body)

        tweet = soup.find(class_='permalink-tweet')
        
        user = tweet.find(class_='username').text

        tweetText = tweet.find(class_='tweet-text')

        links = tweetText.find_all('a', {'data-expanded-url': True})
        for link in links:
            link.string = link['data-expanded-url']

        embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'})
        for link in embeddedLinks:
            link.string = link['href']

        text = StringUtils.unescapeXHTML(tweetText.text)
        text = re.sub('[\r\n]+', self.graySplitter, text)

        formatString = unicode(assembleFormattedText(A.normal[A.bold['{0}:'], ' {1}']))

        return IRCResponse(ResponseType.Say, formatString.format(user, text), message.ReplyTo)
Beispiel #10
0
    def _stringifyTweet(self, tweet):
        """
        turn a tweet object into a nice string for us to send to IRC
        @type tweet: dict[str, T/str]
        """
        retweet = None
        # get the original tweet if this is a retweet
        if 'retweeted_status' in tweet:
            retweet = tweet
            tweet = retweet['retweeted_status']

        tweetText = StringUtils.unescapeXHTML(tweet['text'])
        tweetText = re.sub('[\r\n]+', StringUtils.graySplitter, tweetText)
        for url in tweet['entities']['urls']:
            tweetText = tweetText.replace(url['url'], url['expanded_url'])

        timestamp = parser.parse(tweet['created_at'])
        timeString = timestamp.strftime('%A, %Y-%m-%d %H:%M:%S %Z')
        delta = datetime.datetime.utcnow() - timestamp.replace(tzinfo=None)
        deltaString = StringUtils.deltaTimeToString(delta)
        tweetText = u'{} (tweeted {}, {} ago)'.format(tweetText, timeString, deltaString)

        if retweet is None:
            user = tweet['user']['screen_name']
        else:
            user = retweet['user']['screen_name']
            tweetText = 'RT {}: {}'.format(tweet['user']['screen_name'], tweetText)
            
        link = u'https://twitter.com/{}/status/{}'.format(tweet['user']['screen_name'], tweet['id_str'])
        link = WebUtils.shortenGoogl(link)

        formatString = unicode(assembleFormattedText(A.normal[A.bold['@{0}>'], ' {1} {2}']))
        newTweet = formatString.format(user, tweetText, link)
        return newTweet
Beispiel #11
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """

        baseURL = "http://greywool.com/desertbus/{}/gifs/random.php"
        years = range(7, 9)

        if len(message.ParameterList) > 0:
            invalid = u"'{}' is not a valid year, valid years are {} to {}"\
                .format(message.ParameterList[0], years[0], years[-1])
            try:
                year = int(message.ParameterList[0])
            except ValueError:
                return IRCResponse(ResponseType.Say, invalid, message.ReplyTo)

            if year not in years:
                return IRCResponse(ResponseType.Say, invalid, message.ReplyTo)
        else:
            year = random.choice(years)

        url = baseURL.format(year)

        webPage = WebUtils.fetchURL(url)

        link = webPage.body

        return IRCResponse(ResponseType.Say,
                           u"Random DB{} gif: {}".format(year, link),
                           message.ReplyTo)
Beispiel #12
0
 def execute(self, message):
     """
     @type message: IRCMessage
     """
     if len(message.ParameterList) == 0:
         return IRCResponse(ResponseType.Say, "You didn't give a URL to shorten!", message.ReplyTo)
     
     url = WebUtils.shortenGoogl(message.Parameters)
     
     return IRCResponse(ResponseType.Say, url, message.ReplyTo)
Beispiel #13
0
    def FollowKickstarter(self, ksID, message):
        webPage = WebUtils.fetchURL('https://www.kickstarter.com/projects/{0}/'.format(ksID))

        soup = BeautifulSoup(webPage.body)

        data = []

        title = soup.find(class_='title')
        if title is not None:
            creator = soup.find(id='name')
            if creator is not None:
                data.append(assembleFormattedText(A.normal['{0}', A.fg.gray[' by '], '{1}']).format(title.h2.text.strip(), creator.text.strip()))
            else:
                data.append(title.h2.text.strip())

        stats = soup.find(id='stats')

        backerCount = stats.find(id='backers_count')
        if backerCount is not None:
            data.append('Backers: {0:,}'.format(int(backerCount['data-backers-count'])))

        pledged = stats.find(id='pledged')
        if pledged is not None:
            if float(pledged['data-percent-raised']) >= 1.0:
                percentageString = A.fg.green['({3:,.0f}% funded)']
            else:
                percentageString = A.fg.red['({3:,.0f}% funded)']
                
            pledgedString = assembleFormattedText(A.normal['Pledged: {0:,.0f}', A.fg.gray['/'], '{1:,.0f} {2} ', percentageString])
            data.append(pledgedString.format(float(pledged['data-pledged']),
                                             float(pledged['data-goal']),
                                             pledged.data['data-currency'],
                                             float(pledged['data-percent-raised']) * 100))

        findState = soup.find(id='main_content')
        if 'Project-state-canceled' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.red['Cancelled']]))
            
        elif 'Project-state-failed' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.red['Failed']]))

        elif 'Project-state-successful' in findState['class']:
                data.append(assembleFormattedText(A.normal[A.fg.green['Successful']]))

        elif 'Project-state-live' in findState['class']:
            duration = stats.find(id='project_duration_data')

            if duration is not None:
                remaining = float(duration['data-hours-remaining'])
                days = math.floor(remaining/24)
                hours = remaining/24 - days

                data.append('Duration: {0:.0f} days {1:.1f} hours to go'.format(days, hours))

        return IRCResponse(ResponseType.Say, self.graySplitter.join(data), message.ReplyTo)
Beispiel #14
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        url = "http://greywool.com/desertbus/gifs/random.php"

        webPage = WebUtils.fetchURL(url)

        link = webPage.body

        return IRCResponse(ResponseType.Say, u"Random DB gif: {0}".format(link), message.ReplyTo)
Beispiel #15
0
 def getSteamPrice(cls, appType, appId, region):
     webPage = WebUtils.fetchURL('http://store.steampowered.com/api/{0}details/?{0}ids={1}&cc={2}&l=english&v=1'.format(appType, appId, region))
     priceField = {'app': 'price_overview', 'package': 'price'}[appType]
     response = json.loads(webPage.body)
     
     if 'data' not in response[appId]:
         return
     
     if region == 'AU':
         response[appId]['data'][priceField]['currency'] = 'AUD'
     return response[appId]['data'][priceField]
Beispiel #16
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        responses = []
        for feedName, feedDeets in DataStore.LRRChecker.iteritems():
            if feedDeets['lastCheck'] > datetime.datetime.utcnow() - datetime.timedelta(minutes=10):
                continue
            
            DataStore.LRRChecker[feedName]['lastCheck'] = datetime.datetime.utcnow()
            
            feedPage = WebUtils.fetchURL(feedDeets['url'])
            
            if feedPage is None:
                #TODO: log an error here that the feed likely no longer exists!
                continue
            
            root = ET.fromstring(feedPage.body)
            item = root.find('channel/item')
            
            if item is None:
                #TODO: log an error here that the feed likely no longer exists!
                continue

            title = DataStore.LRRChecker[feedName]['lastTitle'] = item.find('title').text
            link = DataStore.LRRChecker[feedName]['lastLink'] = WebUtils.shortenGoogl(item.find('link').text)
            newestDate = dparser.parse(item.find('pubDate').text, fuzzy=True, ignoretz=True)
            
            if newestDate > feedDeets['lastUpdate']:
                DataStore.LRRChecker[feedName]['lastUpdate'] = newestDate
                
                if feedDeets['suppress']:
                    DataStore.LRRChecker[feedName]['suppress'] = False
                else:
                    response = 'New {0}! Title: {1} | {2}'.format(feedName, title, link)
                    responses.append(IRCResponse(ResponseType.Say, response, '#desertbus'))
            
        return responses
Beispiel #17
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) == 0:
            return IRCResponse(ResponseType.Say, "You didn't give a URL! Usage: {0}".format(self.help), message.ReplyTo)

        url = message.Parameters
        
        webPage = WebUtils.fetchURL('http://www.downforeveryoneorjustme.com/{0}'.format(url))
        root = BeautifulSoup(webPage.body)
        downText = root.find('div').text.splitlines()[1].strip()
        
        return IRCResponse(ResponseType.Say, downText, message.ReplyTo)
Beispiel #18
0
    def FollowStandard(self, url, message):
        webPage = WebUtils.fetchURL(url)
        
        if webPage is None:
            return

        if webPage.responseUrl != url:
            return self.DispatchToFollows(webPage.responseUrl, message)
        
        title = self.GetTitle(webPage.body)
        if title is not None:
            return IRCResponse(ResponseType.Say, u'{0} (at {1})'.format(title, webPage.domain), message.ReplyTo)
        
        return
Beispiel #19
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) == 0 or len(message.ParameterList) > 2:
            return IRCResponse(ResponseType.Say, self.help, message.ReplyTo)

        subreddit = message.ParameterList[0].lower()
        if len(message.ParameterList) == 2:
            try:
                topRange = int(message.ParameterList[1])
                if topRange < 0:
                    return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo)
            except ValueError:
                return IRCResponse(ResponseType.Say, "The range should be a positive integer!", message.ReplyTo)
        else:
            topRange = 100

        url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}"
        url = url.format(subreddit, random.randint(0, topRange))
        response = WebUtils.fetchURL(url, self.headers)
        jsonResponse = json.loads(response.body)
        images = jsonResponse["data"]

        if not images:
            return IRCResponse(
                ResponseType.Say,
                "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)".format(
                    subreddit
                ),
                message.ReplyTo,
            )

        image = random.choice(images)

        data = []
        if image["title"] is not None:
            data.append(image["title"])
        if image["nsfw"]:
            data.append(u"\x034\x02NSFW!\x0F")
        if image["animated"]:
            data.append(u"\x032\x02Animated!\x0F")
        if "gifv" in image:
            data.append(image["gifv"])
        else:
            data.append(image["link"])

        graySplitter = assembleFormattedText(A.normal[" ", A.fg.gray["|"], " "])
        return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
Beispiel #20
0
    def execute(self, response):
        """
        @type response: IRCResponse
        """
        limit = 700  # chars
        expire = 10  # minutes
        if len(response.Response) > limit:
            replaced = WebUtils.pasteEE(response.Response,
                                        u'Response longer than {0} chars intended for {1}'.format(limit,
                                                                                                  response.Target),
                                        expire)

            response.Response = u'Response too long, pasted here instead: {0} (Expires in {1} minutes)'.format(replaced,
                                                                                                               expire)
        return response
    def _pizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        steps = {6: u"{}'s pizza order has been placed",
                 7: u"{}'s pizza is being prepared",
                 5: u"{}'s pizza is in the oven",
                 8: u"{}'s pizza is sitting on a shelf, waiting for a driver",
                 9: u"{}'s pizza is out for delivery",
                 3: u"{}'s pizza has been delivered! Tracking stopped"}

        trackingDetails = self.trackers[orderID]

        trackURL = u'https://www.dominos.co.uk/pizzaTracker/getOrderDetails?id={}'.format(orderID)
        page = WebUtils.fetchURL(trackURL)

        if page is None:
            # tracking API didn't respond
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"The pizza tracking page linked by {} "
                                  u"had some kind of error, tracking stopped".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return

        j = json.loads(page.body)

        if j['customerName'] is None:
            self._stopPizzaTracker(orderID)
            self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                  u"There are no pizza tracking details at the page linked by {}.".format(trackingDetails.orderer),
                                  trackingDetails.channel.Name))
            return
        
        response = None
        
        step = j['statusId']
        if step != trackingDetails.step:
            trackingDetails.step = step
            response = IRCResponse(ResponseType.Say,
                                   steps[step].format(trackingDetails.orderer),
                                   trackingDetails.channel.Name)

        if step == 3:
            self._stopPizzaTracker(orderID)
        
        if response is not None:
            self.bot.sendResponse(response)
Beispiel #22
0
    def FollowStandard(self, url, message):
        webPage = WebUtils.fetchURL(url)
        
        if webPage is None:
            return

        if webPage.responseUrl != url:
            return self.DispatchToFollows(webPage.responseUrl, message)
        
        title = self.GetTitle(webPage.body)
        if title is not None:
            return IRCResponse(ResponseType.Say,
                               u'{0} (at {1})'.format(title, webPage.domain),
                               message.ReplyTo,
                               {'urlfollowURL': url})
        
        return
Beispiel #23
0
    def _export(self, message):
        """export [<alias name(s)] - exports all aliases - or the specified aliases - to paste.ee, \
        and returns a link"""
        if len(message.ParameterList) > 1:
            # filter the alias dictionary by the listed aliases
            params = [alias.lower() for alias in message.ParameterList[1:]]
            aliases = {alias: self.aliases[alias]
                       for alias in self.aliases
                       if alias in params}
            aliasHelp = {alias: self.aliasHelpDict[alias]
                         for alias in self.aliasHelpDict
                         if alias in params}

            if len(aliases) == 0:
                return IRCResponse(ResponseType.Say,
                                   u"I don't have any of the aliases listed for export",
                                   message.ReplyTo)
        else:
            aliases = self.aliases
            aliasHelp = self.aliasHelpDict

            if len(aliases) == 0:
                return IRCResponse(ResponseType.Say,
                                   u"There are no aliases for me to export!",
                                   message.ReplyTo)

        addCommands = [u"{}alias add {} {}".format(self.bot.commandChar,
                                                   name, u" ".join(command))
                       for name, command in aliases.iteritems()]
        helpCommands = [u"{}alias help {} {}".format(self.bot.commandChar,
                                                     name, helpText)
                        for name, helpText in aliasHelp.iteritems()]

        export = u"{}\n\n{}".format(u"\n".join(sorted(addCommands)),
                                    u"\n".join(sorted(helpCommands)))

        url = WebUtils.pasteEE(export,
                               u"Exported {} aliases for {}".format(self.bot.nickname, cmdArgs.server),
                               60)
        return IRCResponse(ResponseType.Say,
                           u"Exported {} aliases and {} help texts to {}".format(len(addCommands),
                                                                                 len(helpCommands),
                                                                                 url),
                           message.ReplyTo)
Beispiel #24
0
    def _export(self, message):
        """export [<alias name(s)] - exports all aliases - or the specified aliases - to paste.ee, \
        and returns a link"""
        if len(message.ParameterList) > 1:
            # filter the alias dictionary by the listed aliases
            params = [alias.lower() for alias in message.ParameterList[1:]]
            aliases = {alias: self.aliases[alias]
                       for alias in self.aliases
                       if alias in params}
            aliasHelp = {alias: self.aliasHelpDict[alias]
                         for alias in self.aliasHelpDict
                         if alias in params}

            if len(aliases) == 0:
                return IRCResponse(ResponseType.Say,
                                   u"I don't have any of the aliases listed for export",
                                   message.ReplyTo)
        else:
            aliases = self.aliases
            aliasHelp = self.aliasHelpDict

            if len(aliases) == 0:
                return IRCResponse(ResponseType.Say,
                                   u"There are no aliases for me to export!",
                                   message.ReplyTo)

        addCommands = [u"{}alias add {} {}".format(self.bot.commandChar,
                                                   name, u" ".join(command))
                       for name, command in aliases.iteritems()]
        helpCommands = [u"{}alias help {} {}".format(self.bot.commandChar,
                                                     name, helpText)
                        for name, helpText in aliasHelp.iteritems()]

        export = u"{}\n\n{}".format(u"\n".join(sorted(addCommands)),
                                    u"\n".join(sorted(helpCommands)))

        url = WebUtils.pasteEE(export,
                               u"Exported {} aliases for {}".format(self.bot.nickname, cmdArgs.server),
                               60)
        return IRCResponse(ResponseType.Say,
                           u"Exported {} aliases and {} help texts to {}".format(len(addCommands),
                                                                                 len(helpCommands),
                                                                                 url),
                           message.ReplyTo)
Beispiel #25
0
    def _pizzaTracker(self, orderID):
        """
        @type orderID: str
        """
        steps = [u"{}'s pizza order has been placed",
                 u"{}'s pizza is being prepared",
                 u"{}'s pizza is in the oven",
                 u"{}'s pizza is sitting on a shelf, waiting for a driver",
                 u"{}'s pizza is out for delivery",
                 u"{}'s pizza has been delivered! Tracking stopped"]

        trackingDetails = self.trackers[orderID]

        trackURL = u'http://www.dominos.co.uk/checkout/pizzaTrackeriFrame.aspx?id={}'.format(orderID)
        page = WebUtils.fetchURL(trackURL)

        if page is not None:
            root = BeautifulSoup(page.body)
            stepImg = root.find('img')
            if stepImg is not None and stepImg.has_attr('alt'):
                stepImgAlt = stepImg['alt']
                stepMatch = re.search(r'^Step (?P<step>[0-9]+)$', stepImgAlt)

                if stepMatch:
                    step = int(stepMatch.group('step'))
                    if step > trackingDetails.step:
                        trackingDetails.step = step
                        self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                                          steps[step-1].format(trackingDetails.orderer),
                                                          trackingDetails.channel.Name))

                    if step == 6:
                        self._stopPizzaTracker(orderID)

                    return

        # if we reach here the tracking page was invalid in some way
        self.bot.sendResponse(IRCResponse(ResponseType.Say,
                                          u"The pizza tracking page linked by {} "
                                          u"had some kind of error, tracking stopped".format(trackingDetails.orderer),
                                          trackingDetails.channel.Name))
        self._stopPizzaTracker(orderID)
Beispiel #26
0
    def FollowTwitter(self, tweeter, tweetID, message):
        webPage = WebUtils.fetchURL(
            'https://twitter.com/{0}/status/{1}'.format(tweeter, tweetID))

        soup = BeautifulSoup(webPage.body)

        tweet = soup.find(class_='permalink-tweet')

        user = tweet.find(class_='username').text

        tweetText = tweet.find(class_='tweet-text')

        tweetTimeText = tweet.find(class_='client-and-actions').text.strip()
        try:
            tweetTimeText = time.strftime(
                '%Y/%m/%d %H:%M',
                time.strptime(tweetTimeText, '%I:%M %p - %d %b %Y'))
        except ValueError:
            pass
        tweetTimeText = re.sub(ur'[\r\n\s]+', u' ', tweetTimeText)

        links = tweetText.find_all('a', {'data-expanded-url': True})
        for link in links:
            link.string = ' ' + link['data-expanded-url']

        embeddedLinks = tweetText.find_all('a', {'data-pre-embedded': 'true'})
        for link in embeddedLinks:
            link.string = ' ' + link['href']

        text = StringUtils.unescapeXHTML(tweetText.text)
        text = re.sub('[\r\n]+', self.graySplitter, text)

        formatString = unicode(
            assembleFormattedText(A.normal[A.fg.gray['[{0}]'], A.bold[' {1}:'],
                                           ' {2}']))

        return IRCResponse(
            ResponseType.Say, formatString.format(tweetTimeText, user, text),
            message.ReplyTo, {
                'urlfollowURL':
                'https://twitter.com/{}/status/{}'.format(tweeter, tweetID)
            })
Beispiel #27
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        try:
            results = WebUtils.googleSearch(message.Parameters)

            firstResult = results['responseData']['results'][0]

            title = firstResult['titleNoFormatting']
            content = firstResult['content']
            content = re.sub(r'<.*?>', '', content)  # strip html tags
            content = re.sub(r'\s+', ' ', content)  # replace multiple spaces with single ones (includes newlines?)
            content = StringUtils.unescapeXHTML(content)
            url = firstResult['unescapedUrl']
            replyText = u'{1}{0}{2}{0}{3}'.format(StringUtils.graySplitter, title, content, url)

            return IRCResponse(ResponseType.Say, replyText, message.ReplyTo)
        except Exception, x:
            print str(x)
            return IRCResponse(ResponseType.Say, x.args, message.ReplyTo)
Beispiel #28
0
    def _stringifyTweet(self, tweet):
        """
        turn a tweet object into a nice string for us to send to IRC
        @type tweet: dict[str, T/str]
        """
        retweet = None
        # get the original tweet if this is a retweet
        if 'retweeted_status' in tweet:
            retweet = tweet
            tweet = retweet['retweeted_status']

        tweetText = StringUtils.unescapeXHTML(tweet['text'])
        tweetText = re.sub('[\r\n]+', StringUtils.graySplitter, tweetText)
        for url in tweet['entities']['urls']:
            tweetText = tweetText.replace(url['url'], url['expanded_url'])

        timestamp = parser.parse(tweet['created_at'])
        timeString = timestamp.strftime('%A, %Y-%m-%d %H:%M:%S %Z')
        delta = datetime.datetime.utcnow() - timestamp.replace(tzinfo=None)
        deltaString = StringUtils.deltaTimeToString(delta)
        tweetText = u'{} (tweeted {}, {} ago)'.format(tweetText, timeString,
                                                      deltaString)

        if retweet is None:
            user = tweet['user']['screen_name']
        else:
            user = retweet['user']['screen_name']
            tweetText = 'RT {}: {}'.format(tweet['user']['screen_name'],
                                           tweetText)

        link = u'https://twitter.com/{}/status/{}'.format(
            tweet['user']['screen_name'], tweet['id_str'])
        link = WebUtils.shortenGoogl(link)

        formatString = unicode(
            assembleFormattedText(A.normal[A.bold['@{0}>'], ' {1} {2}']))
        newTweet = formatString.format(user, tweetText, link)
        return newTweet
Beispiel #29
0
    def FollowYouTube(self, videoID, message):
        if self.youtubeKey is None:
            return IRCResponse(ResponseType.Say, '[YouTube API key not found]', message.ReplyTo)

        fields = 'items(id,snippet(title,description,channelTitle),contentDetails(duration))'
        parts = 'snippet,contentDetails'
        url = 'https://www.googleapis.com/youtube/v3/videos?id={}&fields={}&part={}&key={}'.format(videoID, fields, parts, self.youtubeKey)
        
        webPage = WebUtils.fetchURL(url)
        webPage.body = webPage.body.decode('utf-8')
        j = json.loads(webPage.body)

        if 'items' not in j:
            return None

        title = j['items'][0]['snippet']['title']
        description = j['items'][0]['snippet']['description']
        channel = j['items'][0]['snippet']['channelTitle']
        length = parse_duration(j["items"][0]["contentDetails"]["duration"]).total_seconds()

        m, s = divmod(int(length), 60)
        h, m = divmod(m, 60)
        if h > 0:
            length = u'{0:02d}:{1:02d}:{2:02d}'.format(h, m, s)
        else:
            length = u'{0:02d}:{1:02d}'.format(m, s)

        if not description:
            description = u'<no description available>'
        description = re.sub('(\n|\s)+', ' ', description)
        limit = 150
        if len(description) > limit:
            description = u'{} ...'.format(description[:limit].rsplit(' ', 1)[0])

        return IRCResponse(ResponseType.Say,
                           self.graySplitter.join([title, length, channel, description]),
                           message.ReplyTo,
                           {'urlfollowURL': 'http://youtu.be/{}'.format(videoID)})
Beispiel #30
0
    def FollowYouTube(self, videoID, message):
        if self.youtubeKey is None:
            return IRCResponse(ResponseType.Say, '[YouTube API key not found]', message.ReplyTo)

        url = 'https://gdata.youtube.com/feeds/api/videos/{0}?v=2&key={1}'.format(videoID, self.youtubeKey)
        
        webPage = WebUtils.fetchURL(url)
        webPage.body = webPage.body.decode('utf-8')
        
        titleMatch = re.search('<title>(?P<title>[^<]+?)</title>', webPage.body)
        
        if titleMatch:
            lengthMatch = re.search("<yt:duration seconds='(?P<length>[0-9]+?)'/>", webPage.body)
            descMatch = re.search("<media:description type='plain'>(?P<desc>[^<]+?)</media:description>", webPage.body)
            
            title = titleMatch.group('title')
            title = self.htmlParser.unescape(title)
            length = lengthMatch.group('length')
            m, s = divmod(int(length), 60)
            h, m = divmod(m, 60)
            if h > 0:
                length = u'{0:02d}:{1:02d}:{2:02d}'.format(h, m, s)
            else:
                length = u'{0:02d}:{1:02d}'.format(m, s)

            description = u'<no description available>'
            if descMatch:
                description = descMatch.group('desc')
                description = re.sub('<[^<]+?>', '', description)
                description = self.htmlParser.unescape(description)
                description = re.sub('\n+', ' ', description)
                description = re.sub('\s+', ' ', description)
                if len(description) > 150:
                    description = description[:147] + u'...'
                
            return IRCResponse(ResponseType.Say, self.graySplitter.join([title, length, description]), message.ReplyTo)
        
        return
Beispiel #31
0
    def FollowYouTube(self, videoID, message):
        if self.youtubeKey is None:
            return IRCResponse(ResponseType.Say, '[YouTube API key not found]', message.ReplyTo)

        fields = 'items(id,snippet(title,description,channelTitle),contentDetails(duration))'
        parts = 'snippet,contentDetails'
        url = 'https://www.googleapis.com/youtube/v3/videos?id={}&fields={}&part={}&key={}'.format(videoID, fields, parts, self.youtubeKey)
        
        webPage = WebUtils.fetchURL(url)
        webPage.body = webPage.body.decode('utf-8')
        j = json.loads(webPage.body)

        if 'items' not in j:
            return None

        title = j['items'][0]['snippet']['title']
        description = j['items'][0]['snippet']['description']
        channel = j['items'][0]['snippet']['channelTitle']
        length = parse_duration(j["items"][0]["contentDetails"]["duration"]).total_seconds()

        m, s = divmod(int(length), 60)
        h, m = divmod(m, 60)
        if h > 0:
            length = u'{0:02d}:{1:02d}:{2:02d}'.format(h, m, s)
        else:
            length = u'{0:02d}:{1:02d}'.format(m, s)

        if not description:
            description = u'<no description available>'
        description = re.sub('(\n|\s)+', ' ', description)
        limit = 150
        if len(description) > limit:
            description = u'{} ...'.format(description[:limit].rsplit(' ', 1)[0])

        return IRCResponse(ResponseType.Say,
                           self.graySplitter.join([title, length, channel, description]),
                           message.ReplyTo,
                           {'urlfollowURL': 'http://youtu.be/{}'.format(videoID)})
Beispiel #32
0
 def execute(self, message):
     """
     @type message: IRCMessage
     """
     if len(message.ParameterList) == 0:
         return IRCResponse(ResponseType.Say, 'Define what?', message.ReplyTo)
     
     query = urllib.quote(message.Parameters.encode('utf8'))
     url = 'http://www.google.com/dictionary/json?callback=a&sl=en&tl=en&q={0}'.format(query)
     j = WebUtils.sendToServer(url)
     j = j[2:]
     j = j[:-10]
     j = j.decode('string_escape')
     j = re.sub(r'</?[^>]+?>', '', j)
     defs = json.loads(j)
     
     numDefs = 0
     defsToDisp = 3
     responses = [IRCResponse(ResponseType.Say, 'Definitions for {0}:'.format(message.Parameters), message.ReplyTo)]
     defToDispIf = None
     for wordType in defs['primaries']:
         for definition in wordType['entries']:
             if definition['type'] == 'meaning':
                 numDefs += 1
                 if numDefs <= defsToDisp:
                     responses.append(IRCResponse(ResponseType.Say, '{0}. {1}'.format(numDefs, definition['terms'][0]['text']), message.ReplyTo))
                 if numDefs == defsToDisp+1:
                     defToDispIf = IRCResponse(ResponseType.Say, '{0}. {1}'.format(numDefs, definition['terms'][0]['text']), message.ReplyTo)
     if numDefs > defsToDisp+1:
         responses.append(IRCResponse(ResponseType.Say, 'And {1} more here: www.google.com/#tbs=dfn:1&q={0}'.format(query, numDefs-defsToDisp), message.ReplyTo))
     elif numDefs == defsToDisp+1:
         responses.append(defToDispIf)
                 
     if numDefs == 0:
         return IRCResponse(ResponseType.Say, 'No definitions found for {0}'.format(message.Parameters), message.ReplyTo)
     
     return responses
Beispiel #33
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) != 1:
            return IRCResponse(ResponseType.Say, self.help, message.ReplyTo)

        subreddit = message.ParameterList[0].lower()

        url = "https://api.imgur.com/3/gallery/r/{}/time/all/{}"
        url = url.format(subreddit, random.randint(0, 100))
        response = WebUtils.fetchURL(url, self.headers)
        jsonResponse = json.loads(response.body)
        images = jsonResponse['data']

        if not images:
            return IRCResponse(
                ResponseType.Say,
                "The subreddit '{}' doesn't seem to have any images posted to it (or it doesn't exist!)"
                .format(subreddit), message.ReplyTo)

        image = random.choice(images)

        data = []
        if image['title'] is not None:
            data.append(image['title'])
        if image['nsfw']:
            data.append(u'\x034\x02NSFW!\x0F')
        if image['animated']:
            data.append(u'\x032\x02Animated!\x0F')
        data.append(image['link'])

        graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'],
                                                      ' '])
        return IRCResponse(ResponseType.Say, graySplitter.join(data),
                           message.ReplyTo)
Beispiel #34
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) < 3:
            return IRCResponse(ResponseType.Say, self.help, message.ReplyTo)

        try:
            amount = float(message.ParameterList[0])
            offset = 1
        except ValueError:
            amount = 1.0
            offset = 0

        ccFrom = message.ParameterList[offset].upper()
        ccTo   = message.ParameterList[offset+2:]
        ccTo   = ",".join(ccTo)
        ccTo   = ccTo.upper()

        url = "https://api.fixer.io/latest?base={}&symbols={}"
        url = url.format(ccFrom, ccTo)
        response = WebUtils.fetchURL(url)
        jsonResponse = json.loads(response.body)
        rates = jsonResponse['rates']

        if not rates:
            return IRCResponse(ResponseType.Say,
                               "Some or all of those currencies weren't recognized!",
                               message.ReplyTo)

        data = []
        for curr,rate in rates.iteritems():
            data.append("{} {}".format(rate*amount, curr))

        graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'], ' '])
        return IRCResponse(ResponseType.Say, graySplitter.join(data), message.ReplyTo)
Beispiel #35
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        try:
            results = WebUtils.googleSearch(message.Parameters)

            firstResult = results[u'items'][0]

            title = firstResult[u'title']
            title = re.sub(r'\s+', ' ', title)
            content = firstResult[u'snippet']
            content = re.sub(
                r'\s+', ' ', content
            )  # replace multiple spaces with single ones (includes newlines?)
            content = StringUtils.unescapeXHTML(content)
            url = firstResult[u'link']
            replyText = u'{1}{0}{2}{0}{3}'.format(StringUtils.graySplitter,
                                                  title, content, url)

            return IRCResponse(ResponseType.Say, replyText, message.ReplyTo)
        except Exception, x:
            print str(x)
            return IRCResponse(ResponseType.Say, x.args, message.ReplyTo)
Beispiel #36
0
 def getSteamPrice(cls, appId, region):
     webPage = WebUtils.fetchURL('http://store.steampowered.com/api/appdetails/?appids={0}&cc={1}&l=english&v=1'.format(appId, region))
     response = json.loads(webPage.body)
     if region == 'AU':
         response[appId]['data']['price_overview']['currency'] = 'AUD'
     return response[appId]['data']['price_overview']
Beispiel #37
0
    def FollowSteam(self, steamAppId, message):
        webPage = WebUtils.fetchURL('http://store.steampowered.com/api/appdetails/?appids={0}&cc=US&l=english&v=1'.format(steamAppId))

        response = json.loads(webPage.body)
        if not response[steamAppId]['success']:
            return  # failure

        appData = response[steamAppId]['data']

        data = []

        # name
        data.append(assembleFormattedText(A.normal[appData['name'], A.fg.gray[' by '], u', '.join(appData['developers'])]))

        # genres
        data.append(u'Genres: ' + ', '.join([genre['description'] for genre in appData['genres']]))

        # release date
        releaseDate = appData['release_date']
        if not releaseDate['coming_soon']:
            data.append(u'Release Date: ' + releaseDate['date'])
        else:
            data.append(assembleFormattedText(A.normal['Release Date: ', A.fg.cyan[str(releaseDate['date'])]]))

        # metacritic
        # http://www.metacritic.com/faq#item32 (Why is the breakdown of green, yellow, and red scores different for games?)
        if 'metacritic' in appData:
            metaScore = appData['metacritic']['score']
            if metaScore < 50:
                metacritic = assembleFormattedText(A.normal[A.fg.red[str(metaScore)]])
            elif metaScore < 75:
                metacritic = assembleFormattedText(A.normal[A.fg.yellow[str(metaScore)]])
            else:
                metacritic = assembleFormattedText(A.normal[A.fg.green[str(metaScore)]])
            data.append(u'Metacritic: {0}'.format(metacritic))

        # prices
        if 'price_overview' in appData:
            prices = {'USD': appData['price_overview'],
                      'GBP': self.getSteamPrice(steamAppId, 'GB'),
                      'EUR': self.getSteamPrice(steamAppId, 'FR'),
                      'AUD': self.getSteamPrice(steamAppId, 'AU')}

            currencies = {'USD': u'$',
                          'GBP': u'\u00A3',
                          'EUR': u'\u20AC',
                          'AUD': u'AU$'}

            if prices['AUD']['final'] == prices['USD']['final']:
                del prices['AUD']

            priceString = u'/'.join([currencies[val['currency']] + unicode(val['final'] / 100.0) for val in prices.values()])
            if prices['USD']['discount_percent'] > 0:
                priceString += assembleFormattedText(A.normal[A.fg.green[' ({0}% sale!)'.format(prices['USD']['discount_percent'])]])

            data.append(priceString)
        
        # description
        description = appData['about_the_game']
        if description is not None:
            limit = 150
            description = re.sub(r'(<[^>]+>|[\r\n\t])+', assembleFormattedText(A.normal[' ', A.fg.gray['>'], ' ']), description)
            if len(description) > limit:
                description = u'{0} ...'.format(description[:limit].rsplit(' ', 1)[0])
            data.append(description)

        return IRCResponse(ResponseType.Say, self.graySplitter.join(data), message.ReplyTo)
Beispiel #38
0
    def FollowYouTube(self, videoID, message):
        if self.youtubeKey is None:
            return IRCResponse(ResponseType.Say, '[YouTube API key not found]',
                               message.ReplyTo)

        fields = 'items(id,snippet(title,description,channelTitle,liveBroadcastContent),contentDetails(duration),statistics(viewCount),liveStreamingDetails(scheduledStartTime))'
        parts = 'snippet,contentDetails,statistics,liveStreamingDetails'
        url = 'https://www.googleapis.com/youtube/v3/videos?id={}&fields={}&part={}&key={}'.format(
            videoID, fields, parts, self.youtubeKey)

        webPage = WebUtils.fetchURL(url)
        webPage.body = webPage.body.decode('utf-8')
        j = json.loads(webPage.body)

        if 'items' not in j:
            return None

        data = []

        vid = j['items'][0]

        title = vid['snippet']['title']
        data.append(title)
        channel = vid['snippet']['channelTitle']
        data.append(channel)
        if vid['snippet']['liveBroadcastContent'] == 'none':
            length = parse_duration(
                vid['contentDetails']['duration']).total_seconds()
            m, s = divmod(int(length), 60)
            h, m = divmod(m, 60)
            if h > 0:
                length = u'{0:02d}:{1:02d}:{2:02d}'.format(h, m, s)
            else:
                length = u'{0:02d}:{1:02d}'.format(m, s)

            data.append(length)
        elif vid['snippet']['liveBroadcastContent'] == 'upcoming':
            startTime = vid['liveStreamingDetails']['scheduledStartTime']
            startDateTime = dateutil.parser.parse(startTime)
            now = datetime.datetime.now(dateutil.tz.tzutc())
            delta = startDateTime - now
            timespan = StringUtils.deltaTimeToString(delta, 'm')
            timeString = assembleFormattedText(
                A.normal['Live in ', A.fg.cyan[A.bold[timespan]]])
            data.append(timeString)
            pass  # time till stream starts, indicate it's upcoming
        elif vid['snippet']['liveBroadcastContent'] == 'live':
            status = unicode(
                assembleFormattedText(A.normal[A.fg.red[A.bold['{} Live']]]))
            status = status.format(u'●')
            data.append(status)
        else:
            pass  # if we're here, wat

        views = int(vid['statistics']['viewCount'])
        data.append('{:,}'.format(views))

        description = vid['snippet']['description']
        if not description:
            description = u'<no description available>'
        description = re.sub('(\n|\s)+', ' ', description)
        limit = 150
        if len(description) > limit:
            description = u'{} ...'.format(description[:limit].rsplit(' ',
                                                                      1)[0])
        data.append(description)

        return IRCResponse(
            ResponseType.Say, self.graySplitter.join(data), message.ReplyTo,
            {'urlfollowURL': 'http://youtu.be/{}'.format(videoID)})
Beispiel #39
0
    def FollowImgur(self, imgurID, message):
        if self.imgurClientID is None:
            return IRCResponse(ResponseType.Say, '[imgur Client ID not found]', message.ReplyTo)

        if imgurID.startswith('gallery/'):
            imgurID = imgurID.replace('gallery/', '')

        albumLink = False
        if imgurID.startswith('a/'):
            imgurID = imgurID.replace('a/', '')
            url = 'https://api.imgur.com/3/album/{0}'.format(imgurID)
            albumLink = True
        else:
            url = 'https://api.imgur.com/3/image/{0}'.format(imgurID)

        headers = [('Authorization', 'Client-ID {0}'.format(self.imgurClientID))]
        
        webPage = WebUtils.fetchURL(url, headers)
        
        if webPage is None:
            url = 'https://api.imgur.com/3/gallery/{0}'.format(imgurID)
            webPage = WebUtils.fetchURL(url, headers)

        if webPage is None:
            return
        
        response = json.loads(webPage.body)
        
        imageData = response['data']

        if imageData['title'] is None:
            url = 'https://api.imgur.com/3/gallery/{0}'.format(imgurID)
            webPage = WebUtils.fetchURL(url, headers)
            if webPage is not None:
                imageData = json.loads(webPage.body)['data']

            if imageData['title'] is None:
                webPage = WebUtils.fetchURL('http://imgur.com/{0}'.format(imgurID))
                imageData['title'] = self.GetTitle(webPage.body).replace(' - Imgur', '')
                if imageData['title'] == 'imgur: the simple image sharer':
                    imageData['title'] = None
        
        data = []
        if imageData['title'] is not None:
            data.append(imageData['title'])
        else:
            data.append(u'<No Title>')
        if imageData['nsfw']:
            data.append(u'\x034\x02NSFW!\x0F')
        if albumLink:
            data.append(u'Album: {0} Images'.format(imageData['images_count']))
        else:
            if 'is_album' in imageData and imageData['is_album']:
                data.append(u'Album: {0:,d} Images'.format(len(imageData['images'])))
            else:
                if imageData[u'animated']:
                    data.append(u'\x032\x02Animated!\x0F')
                data.append(u'{0:,d}x{1:,d}'.format(imageData['width'], imageData['height']))
                data.append(u'Size: {0:,d}kb'.format(int(imageData['size'])/1024))
        data.append(u'Views: {0:,d}'.format(imageData['views']))
        
        return IRCResponse(ResponseType.Say, self.graySplitter.join(data), message.ReplyTo)
Beispiel #40
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        searchTerm = 'http://gatherer.wizards.com/pages/search/default.aspx?name='
        for param in message.ParameterList:
            searchTerm += '+[%s]' % param

        webPage = WebUtils.fetchURL(searchTerm)

        soup = BeautifulSoup(webPage.body)

        name = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_nameRow'
        })
        if name is None:
            searchResults = soup.find(
                'div', {
                    'id':
                    'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_searchResultsContainer'
                })
            if searchResults is None:
                return IRCResponse(ResponseType.Say,
                                   'No cards found: ' + searchTerm,
                                   message.ReplyTo)
            else:
                cardItems = searchResults.find_all(class_='cardItem')
                # potentially return first item here
                return IRCResponse(
                    ResponseType.Say,
                    '{0} cards found: {1}'.format(len(cardItems),
                                                  searchTerm), message.ReplyTo)

        name = name.find('div', 'value').text.strip()
        types = u' | T: ' + soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_typeRow'
        }).find('div', 'value').text.strip()
        rarity = u' | R: ' + soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_rarityRow'
        }).find('div', 'value').text.strip()

        manaCost = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_manaRow'
        })
        if manaCost is not None:
            manaCost = unicode(manaCost.find('div', 'value'))
            manaCost = u' | MC: ' + self.translateSymbols(manaCost)
            manaCost = re.sub('<[^>]+?>', '', manaCost)
            manaCost = manaCost.replace('\n', '')
        else:
            manaCost = u''

        convCost = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_cmcRow'
        })
        if convCost is not None:
            convCost = u' | CMC: ' + convCost.find('div', 'value').text.strip()
        else:
            convCost = u''

        cardText = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_textRow'
        })
        if cardText is not None:
            cardTexts = cardText.find_all('div', 'cardtextbox')
            texts = []
            for text in cardTexts:
                text = self.translateSymbols(text)
                text = re.sub('<[^>]+?>', '', text)
                texts.append(text)
            cardText = u' | CT: ' + u' > '.join(texts)
        else:
            cardText = u''

        flavText = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_FlavorText'
        })
        if message.Command.endswith('f') and flavText is not None:
            flavTexts = flavText.find_all('div', 'cardtextbox')
            texts = []
            for text in flavTexts:
                texts.append(unicode(text.text))
            flavText = u' | FT: ' + ' > '.join(texts)
        else:
            flavText = u''

        powTough = soup.find('div', {
            'id':
            'ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ptRow'
        })
        if powTough is not None:
            powTough = u' | P/T: ' + powTough.find(
                'div', 'value').text.strip().replace(' ', '')
        else:
            powTough = u''

        reply = name + manaCost + convCost + types + cardText + flavText + powTough + rarity

        return IRCResponse(ResponseType.Say, reply, message.ReplyTo)
Beispiel #41
0
    def _import(self, message):
        """import <url> [<alias(es)>] - imports all aliases from the given address, or only the listed aliases"""
        if message.User.Name not in GlobalVars.admins:
            return IRCResponse(ResponseType.Say,
                               u"Only my admins may import aliases!",
                               message.ReplyTo)
        if len(message.ParameterList) < 2:
            return IRCResponse(ResponseType.Say,
                               u"You didn't give a url to import from!",
                               message.ReplyTo)

        if len(message.ParameterList) > 2:
            onlyListed = True
            importList = [alias.lower() for alias in message.ParameterList[2:]]
        else:
            onlyListed = False

        url = message.ParameterList[1]
        try:
            page = WebUtils.fetchURL(url)
        except ValueError:
            return IRCResponse(ResponseType.Say,
                               u"'{}' is not a valid URL".format(url),
                               message.ReplyTo)
        if page is None:
            return IRCResponse(ResponseType.Say,
                               u"Failed to open page at {}".format(url),
                               message.ReplyTo)

        text = page.body
        text = UnicodeDammit(text).unicode_markup
        lines = text.splitlines()
        numAliases = 0
        numHelpTexts = 0
        for lineNumber, line in enumerate(lines):
            # Skip over blank lines
            if line == u"":
                continue
            splitLine = line.split()
            if splitLine[0].lower() != u"{}alias".format(self.bot.commandChar):
                return IRCResponse(
                    ResponseType.Say,
                    u"Line {} at {} does not begin with {}alias".format(
                        lineNumber, url,
                        self.bot.commandChar), message.ReplyTo)
            subCommand = splitLine[1].lower()
            if subCommand not in [u"add", u"help"]:
                return IRCResponse(
                    ResponseType.Say,
                    u"Line {} at {} is not an add or help command".format(
                        lineNumber, url), message.ReplyTo)

            aliasName = splitLine[2].lower()
            aliasCommand = splitLine[3:]
            aliasCommand[0] = aliasCommand[0].lower()

            # Skip over aliases that weren't listed, if any were listed
            if onlyListed and aliasName not in importList:
                continue

            if subCommand == u"add":
                self._newAlias(aliasName, aliasCommand)
                numAliases += 1
            elif subCommand == u"help":
                aliasHelp = u" ".join(splitLine[3:])
                self.aliasHelpDict[aliasName] = aliasHelp
                numHelpTexts += 1

        return IRCResponse(
            ResponseType.Say,
            u"Imported {} alias(es) and {} help string(s) from {}".format(
                numAliases, numHelpTexts, url), message.ReplyTo)
Beispiel #42
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) == 0:
            return IRCResponse(
                ResponseType.Say,
                "You didn't give a word! Usage: {0}".format(self.help),
                message.ReplyTo)

        search = urllib.quote(message.Parameters)

        url = 'http://api.urbandictionary.com/v0/define?term={0}'.format(
            search)

        webPage = WebUtils.fetchURL(url)

        response = json.loads(webPage.body)

        if len(response['list']) == 0:
            return IRCResponse(
                ResponseType.Say,
                "No entry found for '{0}'".format(message.Parameters),
                message.ReplyTo)

        graySplitter = assembleFormattedText(A.normal[' ', A.fg.gray['|'],
                                                      ' '])

        defn = response['list'][0]

        word = defn['word']

        definition = defn['definition']
        definition = graySplitter.join(
            [s.strip() for s in definition.strip().splitlines() if s])

        example = defn['example']
        example = graySplitter.join(
            [s.strip() for s in example.strip().splitlines() if s])

        author = defn['author']

        up = defn['thumbs_up']
        down = defn['thumbs_down']

        more = 'http://{}.urbanup.com/'.format(word.replace(' ', '-'))

        if word.lower() != message.Parameters.lower():
            word = "{0} (Contains '{1}')".format(word, message.Parameters)

        defFormatString = unicode(
            assembleFormattedText(A.normal[A.bold["{0}:"], " {1}"]))
        exampleFormatString = unicode(
            assembleFormattedText(A.normal[A.bold["Example(s):"], " {0}"]))
        byFormatString = unicode(
            assembleFormattedText(A.normal["{0}", graySplitter,
                                           A.fg.lightGreen["+{1}"],
                                           A.fg.gray["/"],
                                           A.fg.lightRed["-{2}"], graySplitter,
                                           "More defs: {3}"]))
        responses = [
            IRCResponse(ResponseType.Say,
                        defFormatString.format(word, definition),
                        message.ReplyTo),
            IRCResponse(ResponseType.Say, exampleFormatString.format(example),
                        message.ReplyTo),
            IRCResponse(ResponseType.Say,
                        byFormatString.format(author, up, down, more),
                        message.ReplyTo)
        ]

        return responses
Beispiel #43
0
class Slurp(CommandInterface):
    triggers = ['slurp']
    help = "slurp <attribute> <url> <css selector> - scrapes the given attribute from the tag selected at the given url"
    runInThread = True

    runInThread = True

    htmlParser = HTMLParser.HTMLParser()

    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) < 3:
            return IRCResponse(
                ResponseType.Say,
                u"Not enough parameters, usage: {}".format(self.help),
                message.ReplyTo)

        prop, url, selector = (message.ParameterList[0],
                               message.ParameterList[1],
                               u" ".join(message.ParameterList[2:]))

        if not re.match(ur'^\w+://', url):
            url = u"http://{}".format(url)

        if 'slurp' in message.Metadata and url in message.Metadata['slurp']:
            soup = message.Metadata['slurp'][url]
        else:
            page = WebUtils.fetchURL(url)
            if page is None:
                return IRCResponse(ResponseType.Say,
                                   u"Problem fetching {}".format(url),
                                   message.ReplyTo)
            soup = BeautifulSoup(page.body)

        tag = soup.select_one(selector)

        if tag is None:
            return IRCResponse(
                ResponseType.Say,
                u"'{}' does not select a tag at {}".format(selector, url),
                message.ReplyTo)

        specials = {'tagname': tag.name, 'text': tag.text}

        if prop in specials:
            value = specials[prop]
        elif prop in tag.attrs:
            value = tag[prop]
        else:
            return IRCResponse(
                ResponseType.Say,
                u"The tag selected by '{}' ({}) does not have attribute '{}'".
                format(selector, tag.name, prop), message.ReplyTo)

        if not isinstance(value, basestring):
            value = u" ".join(value)

        # sanitize the value
        value = value.strip()
        value = re.sub(ur'[\r\n]+', u' ', value)
        value = re.sub(ur'\s+', u' ', value)
        value = self.htmlParser.unescape(value)

        return IRCResponse(ResponseType.Say,
                           value,
                           message.ReplyTo,
                           extraVars={'slurpURL': url},
                           metadata={'slurp': {
                               url: soup
                           }})
Beispiel #44
0
class JQ(CommandInterface):
    triggers = ['jq']
    help = "jq <url> <filter> - filters json returned by the given url, returning values. \
filter syntax here: https://stedolan.github.io/jq/manual/#Basicfilters"

    runInThread = True

    htmlParser = HTMLParser.HTMLParser()

    def execute(self, message):
        """
        @type message: IRCMessage
        """
        if len(message.ParameterList) < 2:
            return IRCResponse(
                ResponseType.Say,
                u"Not enough parameters, usage: {}".format(self.help),
                message.ReplyTo)

        url, jqfilter = (message.ParameterList[0],
                         u" ".join(message.ParameterList[1:]))

        if not re.match(ur'^\w+://', url):
            url = u"http://{}".format(url)

        if 'jq' in message.Metadata and url in message.Metadata['jq']:
            page = message.Metadata['jq'][url]
        else:
            page = WebUtils.fetchURL(url)
        if page is None:
            return IRCResponse(ResponseType.Say,
                               u"Problem fetching {}".format(url),
                               message.ReplyTo)

        try:
            value = jq(jqfilter).transform(text=page.body)
        except ValueError as e:
            response = re.sub(ur'[\r\n]+', u' ', e.message)
            return IRCResponse(ResponseType.Say, response, message.ReplyTo)

        if value is None:
            return IRCResponse(ResponseType.Say,
                               u"{} does not match a value".format(jqfilter),
                               message.ReplyTo)
        if isinstance(value, dict):
            return IRCResponse(ResponseType.Say,
                               u"{} matches a dict".format(jqfilter),
                               message.ReplyTo)
        if isinstance(value, list):
            return IRCResponse(ResponseType.Say,
                               u"{} matches a list".format(jqfilter),
                               message.ReplyTo)

        # sanitize the value
        value = u'{}'.format(value)
        value = value.strip()
        value = re.sub(ur'[\r\n]+', u' ', value)
        value = re.sub(ur'\s+', u' ', value)
        value = self.htmlParser.unescape(value)

        return IRCResponse(ResponseType.Say,
                           value,
                           message.ReplyTo,
                           extraVars={'jqURL': url},
                           metadata={'jq': {
                               url: page
                           }})
Beispiel #45
0
    def FollowImgur(self, imgurID, message):
        if self.imgurClientID is None:
            return IRCResponse(ResponseType.Say, '[imgur Client ID not found]',
                               message.ReplyTo)

        if imgurID.startswith('gallery/'):
            imgurID = imgurID.replace('gallery/', '')

        albumLink = False
        if imgurID.startswith('a/'):
            imgurID = imgurID.replace('a/', '')
            url = 'https://api.imgur.com/3/album/{0}'.format(imgurID)
            albumLink = True
        else:
            url = 'https://api.imgur.com/3/image/{0}'.format(imgurID)

        headers = [('Authorization',
                    'Client-ID {0}'.format(self.imgurClientID))]

        webPage = WebUtils.fetchURL(url, headers)

        if webPage is None:
            url = 'https://api.imgur.com/3/gallery/{0}'.format(imgurID)
            webPage = WebUtils.fetchURL(url, headers)

        if webPage is None:
            return

        response = json.loads(webPage.body)

        imageData = response['data']

        if imageData['title'] is None:
            url = 'https://api.imgur.com/3/gallery/{0}'.format(imgurID)
            webPage = WebUtils.fetchURL(url, headers)
            if webPage is not None:
                imageData = json.loads(webPage.body)['data']

            if imageData['title'] is None:
                webPage = WebUtils.fetchURL(
                    'http://imgur.com/{0}'.format(imgurID))
                imageData['title'] = self.GetTitle(webPage.body).replace(
                    ' - Imgur', '')
                if imageData['title'] == 'imgur: the simple image sharer':
                    imageData['title'] = None

        data = []
        if imageData['title'] is not None:
            data.append(imageData['title'])
        else:
            data.append(u'<No Title>')
        if imageData['nsfw']:
            data.append(u'\x034\x02NSFW!\x0F')
        if albumLink:
            data.append(u'Album: {0} Images'.format(imageData['images_count']))
        else:
            if 'is_album' in imageData and imageData['is_album']:
                data.append(u'Album: {0:,d} Images'.format(
                    len(imageData['images'])))
            else:
                if imageData[u'animated']:
                    data.append(u'\x032\x02Animated!\x0F')
                data.append(u'{0:,d}x{1:,d}'.format(imageData['width'],
                                                    imageData['height']))
                data.append(u'Size: {0:,d}kb'.format(
                    int(imageData['size']) / 1024))
        data.append(u'Views: {0:,d}'.format(imageData['views']))

        return IRCResponse(
            ResponseType.Say, self.graySplitter.join(data), message.ReplyTo,
            {'urlfollowURL': '[nope, imgur is too hard. also, pointless?]'})
Beispiel #46
0
    def FollowSteam(self, steamType, steamId, message):
        steamType = {'app': 'app', 'sub': 'package'}[steamType]
        webPage = WebUtils.fetchURL(
            'http://store.steampowered.com/api/{0}details/?{0}ids={1}&cc=US&l=english&v=1'
            .format(steamType, steamId))

        response = json.loads(webPage.body)
        if not response[steamId]['success']:
            return  # failure

        appData = response[steamId]['data']

        data = []

        # name
        if 'developers' in appData:
            name = assembleFormattedText(
                A.normal[appData['name'], A.fg.gray[' by '],
                         u', '.join(appData['developers'])])
        else:
            name = appData['name']
        data.append(name)

        # package contents (might need to trim this...)
        if 'apps' in appData:
            appNames = [app['name'] for app in appData['apps']]
            apps = u'Package containing: {}'.format(u', '.join(appNames))
            data.append(apps)

        # genres
        if 'genres' in appData:
            data.append(u'Genres: ' + ', '.join(
                [genre['description'] for genre in appData['genres']]))

        # release date
        releaseDate = appData['release_date']
        if not releaseDate['coming_soon']:
            if releaseDate['date']:
                data.append(u'Released: ' + releaseDate['date'])
        else:
            data.append(
                assembleFormattedText(
                    A.normal['To Be Released: ',
                             A.fg.cyan[A.bold[str(releaseDate['date'])]]]))

        # metacritic
        # http://www.metacritic.com/faq#item32 (Why is the breakdown of green, yellow, and red scores different for games?)
        if 'metacritic' in appData:
            metaScore = appData['metacritic']['score']
            if metaScore < 50:
                metacritic = assembleFormattedText(
                    A.normal[A.fg.red[str(metaScore)]])
            elif metaScore < 75:
                metacritic = assembleFormattedText(
                    A.normal[A.fg.orange[str(metaScore)]])
            else:
                metacritic = assembleFormattedText(
                    A.normal[A.fg.green[str(metaScore)]])
            data.append(u'Metacritic: {0}'.format(metacritic))

        # prices
        priceField = {'app': 'price_overview', 'package': 'price'}[steamType]
        if priceField in appData:
            prices = {
                'USD': appData[priceField],
                'GBP': self.getSteamPrice(steamType, steamId, 'GB'),
                'EUR': self.getSteamPrice(steamType, steamId, 'FR'),
                'AUD': self.getSteamPrice(steamType, steamId, 'AU')
            }

            currencies = {
                'USD': u'$',
                'GBP': u'\u00A3',
                'EUR': u'\u20AC',
                'AUD': u'AU$'
            }

            if not prices['AUD'] or prices['AUD']['final'] == prices['USD'][
                    'final']:
                del prices['AUD']

            # filter out any missing prices
            prices = {key: val for key, val in prices.iteritems() if val}

            priceString = u'/'.join([
                currencies[val['currency']] + unicode(val['final'] / 100.0)
                for val in prices.values()
            ])
            if prices['USD']['discount_percent'] > 0:
                priceString += assembleFormattedText(
                    A.normal[A.fg.green[A.bold[' ({0}% sale!)'.format(
                        prices['USD']['discount_percent'])]]])

            data.append(priceString)

        # platforms
        if 'platforms' in appData:
            platforms = appData['platforms']
            platformArray = []
            if platforms['windows']:
                platformArray.append(u'Win')
            else:
                platformArray.append(u'---')
            if platforms['mac']:
                platformArray.append(u'Mac')
            else:
                platformArray.append(u'---')
            if platforms['linux']:
                platformArray.append(u'Lin')
            else:
                platformArray.append(u'---')
            data.append(u'/'.join(platformArray))

        # description
        if 'about_the_game' in appData and appData[
                'about_the_game'] is not None:
            limit = 100
            description = re.sub(
                r'(<[^>]+>|[\r\n\t])+',
                assembleFormattedText(A.normal[' ', A.fg.gray['>'], ' ']),
                appData['about_the_game'])
            if len(description) > limit:
                description = u'{0} ...'.format(description[:limit].rsplit(
                    ' ', 1)[0])
            data.append(description)

        return IRCResponse(
            ResponseType.Say, self.graySplitter.join(data), message.ReplyTo, {
                'urlfollowURL':
                'http://store.steampowered.com/{}/{}'.format({
                    'app': 'app',
                    'package': 'sub'
                }[steamType], steamId)
            })
Beispiel #47
0
    def FollowKickstarter(self, ksID, message):
        webPage = WebUtils.fetchURL(
            'https://www.kickstarter.com/projects/{}/description'.format(ksID))

        soup = BeautifulSoup(webPage.body)

        data = []

        shorturl = soup.find(rel='shorturl')['href']
        if shorturl is None:
            shorturl = 'https://www.kickstarter.com/projects/{}/'.format(ksID)

        title = soup.find(property='og:title')
        if title is not None:
            # live projects
            creator = soup.find(attrs={'data-modal-class': 'modal_project_by'})
            # completed projects
            if creator is None or not creator.text:
                creator = soup.find(
                    class_='green-dark',
                    attrs={'data-modal-class': 'modal_project_by'})
            if creator is not None:
                data.append(
                    unicode(
                        assembleFormattedText(
                            A.normal['{0}', A.fg.gray[' by '],
                                     '{1}'])).format(title['content'].strip(),
                                                     creator.text.strip()))
            else:
                data.append(title['content'].strip())

        stats = soup.find(id='stats')
        # projects in progress
        if stats is not None:
            backerCount = soup.find(id='backers_count')
            if backerCount is not None:
                backerCount = int(backerCount['data-backers-count'])
        # completed projects
        else:
            backerCount = soup.find(class_='NS_campaigns__spotlight_stats')
            if backerCount is not None:
                backerCount = int(
                    backerCount.b.text.strip().split()[0].replace(',', ''))

        data.append('Backers: {:,d}'.format(backerCount))

        if stats is not None:
            pledgeData = soup.find(id='pledged')
            if pledgeData is not None:
                pledged = float(pledgeData['data-pledged'])
                goal = float(pledgeData['data-goal'])
                percentage = float(pledgeData['data-percent-raised'])
                if backerCount > 0:
                    pledgePerBacker = pledged / backerCount
                else:
                    pledgePerBacker = 0
        else:
            money = soup.select('span.money')
            if money:
                pledgedString = money[1].text.strip()
                goalString = money[2].text.strip()
                pledged = float(re.sub(ur'[^0-9.]', u'', pledgedString))
                goal = float(re.sub(ur'[^0-9.]', u'', goalString))
                percentage = (pledged / goal)
                if backerCount > 0:
                    pledgePerBacker = pledged / backerCount
                else:
                    pledgePerBacker = 0

        # no longer any way to get this?
        #currency = soup.select('span.money.no-code')[-1]['class']
        #currency.remove('money')
        #currency.remove('no-code')
        #currency = currency[0].upper()

        if percentage >= 1.0:
            percentageString = A.fg.green['({2:,.0f}% funded)']
        else:
            percentageString = A.fg.red['({2:,.0f}% funded)']

        pledgePerBackerString = A.fg.gray['{3:,.0f}/backer']

        pledgedString = assembleFormattedText(A.normal['Pledged: {0:,.0f}',
                                                       A.fg.gray['/'],
                                                       '{1:,.0f} ',
                                                       percentageString, ' ',
                                                       pledgePerBackerString])
        data.append(
            pledgedString.format(pledged, goal, percentage * 100,
                                 pledgePerBacker))

        findState = soup.find(id='main_content')
        if 'Campaign-state-canceled' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.red['Cancelled']]))

        elif 'Campaign-state-suspended' in findState['class']:
            data.append(assembleFormattedText(
                A.normal[A.fg.blue['Suspended']]))

        elif 'Campaign-state-failed' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.red['Failed']]))

        elif 'Campaign-state-successful' in findState['class']:
            data.append(
                assembleFormattedText(A.normal[A.fg.green['Successful']]))

        elif 'Campaign-state-live' in findState['class']:
            duration = soup.find(id='project_duration_data')

            if duration is not None:
                remaining = float(duration['data-hours-remaining'])
                days = math.floor(remaining / 24)
                hours = remaining % 24

                data.append(
                    'Duration: {0:.0f} days {1:.1f} hours to go'.format(
                        days, hours))

        return IRCResponse(ResponseType.Say, self.graySplitter.join(data),
                           message.ReplyTo, {'urlfollowURL': shorturl})