def searchPokemon(self, name: str) -> PokepediaPokemon:
        if not utils.isValidStr(name):
            raise ValueError(f'name argument is malformed: \"{name}\"')

        name = utils.cleanStr(name)

        rawResponse = None
        try:
            rawResponse = requests.get(
                url = f'https://pokeapi.co/api/v2/pokemon/{name}/',
                timeout = utils.getDefaultTimeout()
            )
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError, Timeout) as e:
            print(f'Exception occurred when attempting to fetch Pokemon \"{name}\": {e}')
            raise RuntimeError(f'Exception occurred when attempting to fetch Pokemon \"{name}\": {e}')

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(f'Exception occurred when attempting to decode Pokemon response into JSON for \"{name}\": {e}')
            raise RuntimeError(f'Exception occurred when attempting to decode Pokemon response into JSON for \"{name}\": {e}')

        return PokepediaPokemon(
            name = jsonResponse['name'].capitalize()
        )
Esempio n. 2
0
    def validateAndRefreshAccessToken(self, twitchClientId: str,
                                      twitchClientSecret: str,
                                      twitchHandle: str):
        if not utils.isValidStr(twitchClientId):
            raise ValueError(
                f'twitchClientId argument is malformed: \"{twitchClientId}\"')
        elif not utils.isValidStr(twitchClientSecret):
            raise ValueError(
                f'twitchClientSecret argument is malformed: \"{twitchClientSecret}\"'
            )
        elif not utils.isValidStr(twitchHandle):
            raise ValueError(
                f'twitchHandle argument is malformed: \"{twitchHandle}\"')

        print(
            f'Validating Twitch access token for \"{twitchHandle}\"... ({utils.getNowTimeText(includeSeconds = True)})'
        )

        rawResponse = None
        try:
            rawResponse = requests.get(
                url=self.__oauth2ValidateUrl,
                params={
                    'Authorization':
                    f'OAuth {self.getAccessToken(twitchHandle)}'
                },
                timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to validate Twitch access token: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to validate Twitch access token: {e}'
            )

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode Twitch\'s response into JSON: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode Twitch\'s response into JSON: {e}'
            )

        if jsonResponse.get('client_id') is None or len(
                jsonResponse['client_id']) == 0:
            print(
                f'Requesting new Twitch tokens for \"{twitchHandle}\"... ({utils.getNowTimeText(includeSeconds = True)})'
            )

            self.__refreshTokens(twitchClientId=twitchClientId,
                                 twitchClientSecret=twitchClientSecret,
                                 twitchHandle=twitchHandle)
        else:
            print(
                f'No need to request new Twitch tokens for \"{twitchHandle}\" ({utils.getNowTimeText(includeSeconds = True)})'
            )
    def __fetchWotd(self, languageEntry: LanguageEntry) -> Wotd:
        if languageEntry is None:
            raise ValueError(f'languageEntry argument is malformed: \"{languageEntry}\"')

        cacheValue = self.__cache[languageEntry]

        if cacheValue is not None:
            return cacheValue

        print(f'Refreshing Word Of The Day for \"{languageEntry.getApiName()}\"... ({utils.getNowTimeText()})')

        ##############################################################################
        # retrieve word of the day from https://www.transparent.com/word-of-the-day/ #
        ##############################################################################

        rawResponse = None
        try:
            rawResponse = requests.get(
                url = f'https://wotd.transparent.com/rss/{languageEntry.getApiName()}-widget.xml?t=0',
                timeout = utils.getDefaultTimeout()
            )
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError, Timeout) as e:
            print(f'Exception occurred when attempting to fetch Word Of The Day for \"{languageEntry.getApiName()}\": {e}')
            raise RuntimeError(f'Exception occurred when attempting to fetch Word Of The Day for \"{languageEntry.getApiName()}\": {e}')

        xmlTree = xmltodict.parse(rawResponse.content)
        if not utils.hasItems(xmlTree):
            print(f'xmlTree for \"{languageEntry.getApiName()}\" is malformed: {xmlTree}')
            raise RuntimeError(f'xmlTree for \"{languageEntry.getApiName()}\" is malformed: {xmlTree}')

        wordsTree = xmlTree['xml']['words']
        word = wordsTree['word']
        definition = wordsTree['translation']
        englishExample = wordsTree.get('enphrase')
        foreignExample = wordsTree.get('fnphrase')
        languageName = wordsTree['langname']
        transliteration = wordsTree.get('wotd:transliteratedWord')

        return Wotd(
            languageEntry = languageEntry,
            word = word,
            definition = definition,
            englishExample = englishExample,
            languageName = languageName,
            foreignExample = foreignExample,
            transliteration = transliteration
        )
Esempio n. 4
0
    def __fetchAirQuality(self, location: Location) -> int:
        if location is None:
            raise ValueError(f'location argument is malformed: \"{location}\"')

        # Retrieve air quality from: https://api-docs.iqair.com/
        # Doing this requires an API key, which you can get here:
        # https://www.iqair.com/us/commercial/air-quality-monitors/airvisual-platform/api

        requestUrl = 'https://api.airvisual.com/v2/nearest_city?key={}&lat={}&lon={}'.format(
            self.__iqAirApiKey, location.getLatitude(),
            location.getLongitude())

        rawResponse = None
        try:
            rawResponse = requests.get(url=requestUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to fetch air quality from IQAir for \"{location.getLocationId()}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to fetch air quality from IQAir for \"{location.getLocationId()}\": {e}'
            )

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode IQAir\'s response into JSON for \"{location.getLocationId()}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode IQAir\'s response into JSON for \"{location.getLocationId()}\": {e}'
            )

        if jsonResponse.get('status') != 'success':
            print(
                f'IQAir\'s response \"status\" was not \"success\": {jsonResponse}'
            )
            raise ValueError(
                f'IQAir\'s response \"status\" was not \"success\": {jsonResponse}'
            )

        return utils.getIntFromDict(
            d=jsonResponse['data']['current']['pollution'], key='aqius')
Esempio n. 5
0
    def __refreshStoreStock(self) -> AnalogueStoreStock:
        print(f'Refreshing Analogue store stock... ({utils.getNowTimeText()})')

        rawResponse = None
        try:
            rawResponse = requests.get(url=self.__storeUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to fetch Analogue store stock: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to fetch Analogue store stock: {e}'
            )

        htmlTree = html.fromstring(rawResponse.content)
        if htmlTree is None:
            print(f'Analogue store\'s htmlTree is malformed: \"{htmlTree}\"')
            raise ValueError(
                f'Analogue store\'s htmlTree is malformed: \"{htmlTree}\"')

        productTrees = htmlTree.find_class('store_product-header__1rLY-')
        if not utils.hasItems(productTrees):
            print(
                f'Analogue store\'s productTrees list is malformed: \"{productTrees}\"'
            )
            raise ValueError(
                f'Analogue store\'s productTrees list is malformed: \"{productTrees}\"'
            )

        products = list()

        for productTree in productTrees:
            productTrees = productTree.find_class('store_title__3eCzb')
            if productTrees is None or len(productTrees) != 1:
                continue

            nameAndPrice = utils.cleanStr(productTrees[0].text_content())
            if len(nameAndPrice) == 0:
                continue
            elif '8BitDo'.lower() in nameAndPrice.lower():
                # don't show 8BitDo products in the final stock listing
                continue

            name = None
            price = None
            indexOfDollar = nameAndPrice.find('$')

            if indexOfDollar == -1:
                name = utils.cleanStr(nameAndPrice)
            else:
                name = utils.cleanStr(nameAndPrice[0:indexOfDollar])
                price = utils.cleanStr(
                    nameAndPrice[indexOfDollar:len(nameAndPrice)])

            if name[len(name) - 1] == '1':
                name = name[0:len(name) - 1]

            productType = AnalogueProductType.fromStr(name)

            inStock = True
            outOfStockElement = productTree.find_class(
                'button_Disabled__2CEbR')
            if utils.hasItems(outOfStockElement):
                inStock = False

            products.append(
                AnalogueStoreEntry(productType=productType,
                                   inStock=inStock,
                                   name=name,
                                   price=price))

        return AnalogueStoreStock(products=products)
Esempio n. 6
0
    def __refreshTokens(self, twitchClientId: str, twitchClientSecret: str,
                        twitchHandle: str):
        if not utils.isValidStr(twitchClientId):
            raise ValueError(
                f'twitchClientId argument is malformed: \"{twitchClientId}\"')
        elif not utils.isValidStr(twitchClientSecret):
            raise ValueError(
                f'twitchClientSecret argument is malformed: \"{twitchClientSecret}\"'
            )
        elif not utils.isValidStr(twitchHandle):
            raise ValueError(
                f'twitchHandle argument is malformed: \"{twitchHandle}\"')

        rawResponse = None
        try:
            rawResponse = requests.post(url=self.__oauth2TokenUrl,
                                        params={
                                            'client_id':
                                            twitchClientId,
                                            'client_secret':
                                            twitchClientSecret,
                                            'grant_type':
                                            'refresh_token',
                                            'refresh_token':
                                            self.getRefreshToken(twitchHandle)
                                        },
                                        timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to request new Twitch tokens: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to request new Twitch tokens: {e}'
            )

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode new Twitch tokens response into JSON: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode new Twitch tokens response into JSON: {e}'
            )

        if 'access_token' not in jsonResponse or len(
                jsonResponse['access_token']) == 0:
            raise ValueError(
                f'Received malformed \"access_token\" Twitch token: {jsonResponse}'
            )
        elif 'refresh_token' not in jsonResponse or len(
                jsonResponse['refresh_token']) == 0:
            raise ValueError(
                f'Received malformed \"refresh_token\" Twitch token: {jsonResponse}'
            )

        jsonContents = dict()
        jsonContents[twitchHandle] = {
            'accessToken': jsonResponse['access_token'],
            'refreshToken': jsonResponse['refresh_token']
        }

        with open(self.__twitchTokensFile, 'w') as file:
            json.dump(jsonContents, file, indent=4, sort_keys=True)

        print(
            f'Saved new Twitch tokens for \"{twitchHandle}\" ({utils.getNowTimeText(includeSeconds = True)})'
        )
Esempio n. 7
0
    def __refreshJoke(self) -> JokeResponse:
        print(f'Refreshing joke... ({utils.getNowTimeText()})')

        rawResponse = None

        try:
            rawResponse = requests.get(url=self.__apiUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(f'Exception occurred when attempting to fetch new joke: {e}')
            raise RuntimeError(
                f'Exception occurred when attempting to fetch new joke: {e}')

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode joke\'s response into JSON: {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode joke\'s response into JSON: {e}'
            )

        if utils.getBoolFromDict(jsonResponse, 'error', True):
            print(f'Rejecting joke due to bad \"error\" value: {jsonResponse}')
            raise ValueError(
                f'Rejecting joke due to bad \"error\" value: {jsonResponse}')
        elif utils.getBoolFromDict(jsonResponse, 'safe', False):
            print(f'Rejecting joke due to bad \"safe\" value: {jsonResponse}')
            raise ValueError(
                f'Rejecting joke due to bad \"safe\" value: {jsonResponse}')

        flagsJson = jsonResponse['flags']
        isExplicit = flagsJson['explicit']
        isNsfw = flagsJson['nsfw']
        isPolitical = flagsJson['political']
        isRacist = flagsJson['racist']
        isReligious = flagsJson['religious']
        isSexist = flagsJson['sexist']

        if isExplicit or isNsfw or isPolitical or isRacist or isReligious or isSexist:
            print(
                f'Rejecting joke due to one or more bad flags: {jsonResponse}')
            raise ValueError(
                f'Rejecting joke due to one or more bad flags: {jsonResponse}')

        jokeText = None

        if jsonResponse['type'] == 'twopart':
            setup = utils.cleanStr(jsonResponse['setup'])
            delivery = utils.cleanStr(jsonResponse['delivery'])
            jokeText = f'{setup} {delivery}'
        elif jsonResponse['type'] == 'single':
            jokeText = utils.cleanStr(jsonResponse['joke'])
        else:
            print(f'Rejecting joke due to unknown \"type\": {jsonResponse}')
            raise ValueError(
                f'Rejecting joke due to unknown \"type\": {jsonResponse}')

        return JokeResponse(text=jokeText)
Esempio n. 8
0
    def search(self, query: str) -> JishoResult:
        if not utils.isValidStr(query):
            raise ValueError(f'query argument is malformed: \"{query}\"')

        query = query.strip()
        print(f'Looking up \"{query}\"... ({utils.getNowTimeText()})')

        encodedQuery = urllib.parse.quote(query)
        requestUrl = f'https://jisho.org/search/{encodedQuery}'

        rawResponse = None
        try:
            rawResponse = requests.get(url=requestUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to search Jisho for \"{query}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to search Jisho for \"{query}\": {e}'
            )

        htmlTree = html.fromstring(rawResponse.content)
        if htmlTree is None:
            print(
                f'Exception occurred when attempting to decode Jisho\'s response for \"{query}\" into HTML tree'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode Jisho\'s response for \"{query}\" into HTML tree'
            )

        parentElements = htmlTree.find_class('concept_light-representation')
        if not utils.hasItems(parentElements):
            print(
                f'Exception occurred when attempting to find parent elements in Jisho\'s HTML tree in query for \"{query}\"'
            )
            raise ValueError(
                f'Exception occurred when attempting to find parent elements in Jisho\'s HTML tree in query for \"{query}\"'
            )

        textElements = parentElements[0].find_class('text')
        if textElements is None or len(textElements) != 1:
            print(
                f'Exception occurred when attempting to find text elements in Jisho\'s HTML tree in query for \"{query}\"'
            )
            raise ValueError(
                f'Exception occurred when attempting to find text elements in Jisho\'s HTML tree in query for \"{query}\"'
            )

        word = utils.cleanStr(textElements[0].text_content())
        if not utils.isValidStr(word):
            print(
                f'Exception occurred when checking that Jisho\'s word is valid in query for \"{query}\"'
            )
            raise ValueError(
                f'Exception occurred when checking that Jisho\'s word is valid in query for \"{query}\"'
            )

        definitionElements = htmlTree.find_class('meaning-meaning')
        if not utils.hasItems(definitionElements):
            print(
                f'Exception occurred when attempting to find definition elements in Jisho\'s HTML tree in query for \"{query}\"'
            )
            raise ValueError(
                f'Exception occurred when attempting to find definition elements in Jisho\'s HTML tree in query for \"{query}\"'
            )

        definitions = list()

        for definitionElement in definitionElements:
            breakUnitElements = definitionElement.find_class('break-unit')
            if breakUnitElements is None or len(breakUnitElements) != 0:
                continue

            definition = utils.cleanStr(definitionElement.text_content())
            if not utils.isValidStr(definition):
                continue

            number = locale.format_string("%d",
                                          len(definitions) + 1,
                                          grouping=True)
            definitions.append(f'#{number} {definition}')

            if len(definitions) >= self.__definitionsMaxSize:
                # keep from adding tons of definitions
                break

        if not utils.hasItems(definitions):
            print(f'Unable to find any viable definitions for \"{query}\"')
            raise ValueError(
                f'Unable to find any viable definitions for \"{query}\"')

        furigana = None
        furiganaElements = htmlTree.find_class('furigana')
        if utils.hasItems(furiganaElements):
            furigana = utils.cleanStr(furiganaElements[0].text_content())

        return JishoResult(definitions=definitions,
                           furigana=furigana,
                           url=requestUrl,
                           word=word)
Esempio n. 9
0
    def __fetchWeather(self, location: Location) -> WeatherReport:
        if location is None:
            raise ValueError(f'location argument is malformed: \"{location}\"')

        cacheValue = self.__cache[location.getLocationId()]

        if cacheValue is not None:
            return cacheValue

        print(
            f'Refreshing weather for \"{location.getLocationId()}\"... ({utils.getNowTimeText()})'
        )

        # Retrieve weather report from https://openweathermap.org/api/one-call-api
        # Doing this requires an API key, which you can get here:
        # https://openweathermap.org/api

        requestUrl = "https://api.openweathermap.org/data/2.5/onecall?appid={}&lat={}&lon={}&exclude=minutely,hourly&units=metric".format(
            self.__oneWeatherApiKey, location.getLatitude(),
            location.getLongitude())

        rawResponse = None
        try:
            rawResponse = requests.get(url=requestUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to fetch weather conditions from Open Weather for \"{location.getLocationId()}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to fetch weather conditions from Open Weather for \"{location.getLocationId()}\": {e}'
            )

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode Open Weather\'s response into JSON for \"{location.getLocationId()}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode Open Weather\'s response into JSON for \"{location.getLocationId()}\": {e}'
            )

        currentJson = jsonResponse['current']
        humidity = currentJson['humidity']
        pressure = currentJson['pressure']
        temperature = currentJson['temp']

        conditions = list()
        if 'weather' in currentJson and len(currentJson['weather']) >= 1:
            for conditionJson in currentJson['weather']:
                conditions.append(self.__prettifyCondition(conditionJson))

        alerts = list()
        if 'alerts' in jsonResponse and len(jsonResponse['alerts']) >= 1:
            for alertJson in jsonResponse['alerts']:
                event = alertJson.get('event')
                senderName = alertJson.get('sender_name')

                if event is not None and len(event) >= 1:
                    if senderName is None or len(senderName) == 0:
                        alerts.append(f'Alert: {event}.')
                    else:
                        alerts.append(f'Alert from {senderName}: {event}.')

        tomorrowsJson = self.__chooseTomorrowFromForecast(jsonResponse)
        tomorrowsHighTemperature = tomorrowsJson['temp']['max']
        tomorrowsLowTemperature = tomorrowsJson['temp']['min']

        tomorrowsConditions = list()
        if 'weather' in tomorrowsJson and len(tomorrowsJson['weather']) >= 1:
            for conditionJson in tomorrowsJson['weather']:
                tomorrowsConditions.append(conditionJson['description'])

        airQuality = None
        if utils.isValidStr(self.__iqAirApiKey):
            airQuality = self.__fetchAirQuality(location)

        return WeatherReport(airQuality=airQuality,
                             humidity=int(round(humidity)),
                             pressure=int(round(pressure)),
                             temperature=temperature,
                             tomorrowsHighTemperature=tomorrowsHighTemperature,
                             tomorrowsLowTemperature=tomorrowsLowTemperature,
                             alerts=alerts,
                             conditions=conditions,
                             tomorrowsConditions=tomorrowsConditions)
    def search(self, query: str) -> EnEsDictionaryResult:
        if not utils.isValidStr(query):
            raise ValueError(f'query argument is malformed: \"{query}\"')

        query = query.strip()
        print(f'Looking up \"{query}\"... ({utils.getNowTimeText()})')

        encodedQuery = urllib.parse.quote(query)
        requestUrl = 'https://www.dictionaryapi.com/api/v3/references/spanish/json/{}?key={}'.format(
            encodedQuery, self.__merriamWebsterApiKey)

        rawResponse = None
        try:
            rawResponse = requests.get(url=requestUrl,
                                       timeout=utils.getDefaultTimeout())
        except (ConnectionError, HTTPError, MaxRetryError, NewConnectionError,
                Timeout) as e:
            print(
                f'Exception occurred when attempting to search Merriam Webster for \"{query}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to search Merriam Webster for \"{query}\": {e}'
            )

        jsonResponse = None
        try:
            jsonResponse = rawResponse.json()
        except JSONDecodeError as e:
            print(
                f'Exception occurred when attempting to decode Merriam Webster\'s response into JSON for \"{query}\": {e}'
            )
            raise RuntimeError(
                f'Exception occurred when attempting to decode Merriam Webster\'s response into JSON for \"{query}\": {e}'
            )

        if not utils.hasItems(jsonResponse):
            print(
                f'jsonResponse for \"{query}\" has no definitions: {jsonResponse}'
            )
            raise ValueError(
                f'jsonResponse \"{query}\" has no definitions: {jsonResponse}')

        definitions = list()

        for entry in jsonResponse:
            definition = None

            if isinstance(entry, str):
                definition = entry
            elif not entry['meta'].get('offensive', True) and utils.hasItems(
                    entry['shortdef']):
                definition = entry['shortdef'][0]

            definition = utils.cleanStr(definition)
            if not utils.isValidStr(definition):
                continue

            index = 0
            add = True

            while (index < len(definitions) and add):
                if definitions[index].lower() == definition.lower():
                    add = False

                index = index + 1

            if add:
                number = locale.format_string("%d",
                                              len(definitions) + 1,
                                              grouping=True)
                definitions.append(f'#{number} {definition}')

                if len(definitions) >= self.__definitionsMaxSize:
                    # keep from adding tons of definitions
                    break

        if not utils.hasItems(definitions):
            print(f'Unable to find any viable definitions for \"{query}\"')
            raise ValueError(
                f'Unable to find any viable definitions for \"{query}\"')

        return EnEsDictionaryResult(definitions=definitions, word=query)