예제 #1
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)
예제 #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?
        if self.twitchClientID is None:
            return IRCResponse(ResponseType.Say,
                               '[Twitch Client ID not found]', message.ReplyTo)

        chanData = {}
        channelOnline = False
        twitchHeaders = [('Accept', 'application/vnd.twitchtv.v3+json'),
                         ('Client-ID', self.twitchClientID)]
        webPage = WebUtils.fetchURL(
            u'https://api.twitch.tv/kraken/streams/{}'.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/{}'.format(channel),
                twitchHeaders)
            chanData = json.loads(webPage.body)

        if len(chanData) > 0:
            if channelOnline:
                channelInfo = assembleFormattedText(
                    A.fg.green['']) + u'{}'.format(
                        chanData['display_name']) + assembleFormattedText(
                            A.normal[''])
            else:
                channelInfo = assembleFormattedText(
                    A.fg.red['']) + u'{}'.format(
                        chanData['display_name']) + assembleFormattedText(
                            A.normal[''])
            channelInfo += u' "{}"'.format(
                re.sub(r'[\r\n]+', self.graySplitter,
                       chanData['status'].strip()))
            if chanData['game'] is not None:
                channelInfo += assembleFormattedText(
                    A.normal[A.fg.gray[', playing '],
                             u'{}'.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,
                {'urlfollowURL': 'https://twitch.tv/{}'.format(channel)})
예제 #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)
예제 #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)
예제 #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)
예제 #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)
예제 #7
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)
예제 #8
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)})
예제 #9
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)
예제 #10
0
파일: Gif.py 프로젝트: LBbot/PyMoronBot
    def execute(self, message):
        """
        @type message: IRCMessage
        """

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

        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:
                if len(message.ParameterList[0]) < 4:
                    year = int(message.ParameterList[0])
                else:
                    raise ValueError
            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)
예제 #11
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)
예제 #12
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)})
예제 #13
0
    def execute(self, message):
        """
        @type message: IRCMessage
        """

        url = "https://splatoon2.ink/data/schedules.json"
        response = WebUtils.fetchURL(url)
        jsonResponse = json.loads(response.body)

        if len(message.ParameterList) < 1:
            # do everything
            data = []
            data += filter(None, [self._regular(jsonResponse, short=True)])
            data += filter(None, [self._ranked(jsonResponse, short=True)])
            data += filter(None, [self._league(jsonResponse, short=True)])
            data += filter(None, [self._fest(jsonResponse, short=True)])
            return IRCResponse(ResponseType.Say, self.graySplitter.join(data),
                               message.ReplyTo)
        else:
            subCommands = {
                'regular': self._regular,
                'ranked': self._ranked,
                'league': self._league,
                'fest': self._fest
            }
            subCommand = message.ParameterList[0].lower()
            if subCommand in subCommands:
                return IRCResponse(
                    ResponseType.Say, subCommands[subCommand](jsonResponse,
                                                              short=False),
                    message.ReplyTo)
            else:
                return IRCResponse(ResponseType.Say, self.help,
                                   message.ReplyTo)
예제 #14
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)
예제 #15
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)
예제 #16
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]
예제 #17
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]
예제 #18
0
파일: Gif.py 프로젝트: HubbeKing/PyMoronBot
    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)
예제 #19
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)

        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)
예제 #20
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)
예제 #21
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
예제 #22
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:
                if len(message.ParameterList[1]) < 20:
                    topRange = int(message.ParameterList[1])
                else:
                    raise ValueError
                if topRange < 0:
                    raise ValueError
            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 'title' in image and image['title'] is not None:
            data.append(image['title'])
        if 'nsfw' in image and image['nsfw']:
            data.append(u'\x034\x02NSFW!\x0F')
        if 'animated' in image and 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)
예제 #23
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)
예제 #24
0
    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)
예제 #25
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
예제 #26
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
예제 #27
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)
예제 #28
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)})
예제 #29
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
예제 #30
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)})
예제 #31
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
예제 #32
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)
예제 #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)
예제 #34
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)
예제 #35
0
    def FollowKickstarter(self, ksID, message):
        webPage = WebUtils.fetchURL('https://www.kickstarter.com/projects/{}/'.format(ksID))

        soup = BeautifulSoup(webPage.body)

        data = []

        title = soup.find(class_='NS_projects__header')
        if title is not None:
            creator = soup.find(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.h2.text.strip(),
                                                                                   creator.text.strip()))
            else:
                data.append(title.h2.text.strip())

        stats = soup.find(id='stats')
        # all of this is now in page javascript, extracting it will be a pain...
        if stats is not None:
            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)']

                if int(backerCount['data-backers-count']) > 0:
                    pledgePerBacker = float(pledged['data-pledged']) / int(backerCount['data-backers-count'])
                else:
                    pledgePerBacker = 0
                
                pledgePerBackerString = A.fg.gray['{4:,.0f}/backer']

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

        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-suspended' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.blue['Suspended']]))
            
        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

                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': 'https://www.kickstarter.com/projects/{}/'.format(ksID)})
예제 #36
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)
예제 #37
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?]'})
예제 #38
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)
예제 #39
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)})
예제 #40
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})
예제 #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)
예제 #42
0
파일: Slurp.py 프로젝트: LBbot/PyMoronBot
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
                           }})
예제 #43
0
파일: Urban.py 프로젝트: HeNine/PyMoronBot
    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(search), 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().split('\r\n')])

        example = defn['example']
        example = graySplitter.join([s.strip() for s in example.strip().split('\r\n')])

        author = defn['author']

        up = defn['thumbs_up']
        down = defn['thumbs_down']
        
        more = 'http://{}.urbanup.com/'.format(word)

        if word.lower() != message.Parameters.lower():
            word = "{0} (Contains '{0}')".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
예제 #44
0
파일: Mtg.py 프로젝트: LBbot/PyMoronBot
    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)
예제 #45
0
파일: Urban.py 프로젝트: LBbot/PyMoronBot
    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
예제 #46
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']
예제 #47
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)
예제 #48
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)
            })
예제 #49
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://www.urbandictionary.com/define.php?term={0}'.format(search)
        
        webPage = WebUtils.fetchURL(url)

        soup = BeautifulSoup(webPage.body)
        # replace link tags with their contents
        [a.unwrap() for a in soup.find_all('a')]

        box = soup.find('div', {'class': 'box'})

        if not box:
            return IRCResponse(ResponseType.Say, "No entry found for '{0}'".format(search), message.ReplyTo)

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

        word = box.find('div', {'class': 'word'}).text.strip()

        # 2014-01-28 really, urban dictionary? 'definition' to 'meaning'? what an important change!
        definition = box.find('div', {'class': 'meaning'})
        if definition.br is not None:
            definition.br.replace_with('\n')
        definition = graySplitter.join([s.strip() for s in definition.text.strip().split('\n')])

        example = box.find('div', {'class': 'example'})
        if example.br is not None:
            example.br.replace_with('\n')
        example = graySplitter.join([s.strip() for s in example.text.strip().split('\n')])

        author = box.find('div', {'class': 'contributor'}).text.strip().replace('\n', ' ')

        counts = box.find('div', {'class': 'thumbs-counts'}).find_all('span', {'class': 'count'})
        up = counts[0].text
        down = counts[1].text

        if word.lower() != message.Parameters.lower():
            word = "{0} (Contains '{0}')".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, url),
                                 message.ReplyTo)]
        
        return responses
예제 #50
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:
            creator = soup.find(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 = stats.find(id='backers_count')
            if backerCount is not None:
                backerCount = int(backerCount['data-backers-count'])
        # completed projects
        else:
            backerCount = soup.find(class_='NS_projects__spotlight_stats')
            if backerCount is not None:
                backerCount = int(backerCount.b.text.strip().split()[0].replace(',', ''))

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

        if stats is not None:
            pledgeData = stats.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

            currency = stats.find_all(attrs={'data-currency': True})[-1]['data-currency']
        else:
            money = soup.select('span.money.no-code')
            if money:
                pledgedString = money[0].text.strip()
                goalString = money[1].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

            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['({3:,.0f}% funded)']
        else:
            percentageString = A.fg.red['({3:,.0f}% funded)']

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

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

        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-suspended' in findState['class']:
            data.append(assembleFormattedText(A.normal[A.fg.blue['Suspended']]))
            
        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

                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})
예제 #51
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'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
        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[' ({0}% sale!)'.format(prices['USD']['discount_percent'])]])

            data.append(priceString)
        
        # description
        if 'about_the_game' in appData and appData['about_the_game'] is not None:
            limit = 150
            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)})