3
\$\begingroup\$

I wrote a program for a basic AI with voice recognition that makes request to News API to retrieve metadata for the headlines currently published on a range of news sources and blogs. For now it does everything I need it to do. What I want to do is improve the quality of the code with best practices and I am hoping someone could tell me where I could improve. I optimized the code the best I could but I think it could be better.

Links:

News API - https://newsapi.org/

Github Repo - https://github.com/zakarh/Fido-AI

Code - Python 3.6

import json
import sys
import urllib.request
import webbrowser

import pyttsx3
import speech_recognition


class FidoAI:
    def __init__(self):
        # Speech Recognition and Listening controls
        self.__speech_engine = pyttsx3.init("sapi5")
        self.__speech_listener = speech_recognition
        self.__speech_engine.setProperty("rate", 150)

        # API key from https://newsapi.org/
        self.__api_key = ""

        # Variables used for making GET requesting for news articles from https://newsapi.org/
        # URL FORMAT: https://newsapi.org/v1/articles?source=%s&apiKey=%s
        self.__command = ""
        self.__request_url = ""

        # Store json data received from GET request url
        self.__retrieved_articles = []

        # The total number of retrieved articles
        self.__num_of_articles = 0

        # News platforms from https://newsapi.org/
        self.__news_platforms = {
            "abc news australia": {"id": "abc-news-au"},
            "al jazeera english": {"id": "al-jazeera-english"},
            "ars technica": {"id": "ars-technica"},
            "associated press": {"id": "associated-press"},
            "bbc news": {"id": "bbc-news"},
            "bbc sport": {"id": "bbc-sport"},
            "build": {"id": "bild"},
            "bloomberg": {"id": "bloomberg"},
            "breitbart news": {"id": "breitbart-news"},
            "business insider": {"id": "business-insider"},
            "business insider ukraine": {"id": "business-insider-uk"},
            "buzzfeed": {"id": "buzzfeed"},
            "cnbc": {"id": "cnbc"},
            "cnn": {"id": "cnn"},
            "daily mail": {"id": "daily-mail"},
            # "der tagesspiegel": {"id": "der-tagesspiegel"},
            "die zeit": {"id": "die-zeit"},
            "engadget": {"id": "engadget"},
            "entertainment weekly": {"id": "entertainment-weekly"},
            "espn": {"id": "espn"},
            # "espn cric info": {"id": "espn-cric-info"},
            "financial times": {"id": "financial-times"},
            "focus": {"id": "focus"},
            "football italia": {"id": "football-italia"},
            "fortune": {"id": "fortune"},
            "442": {"id": "four-four-two"},
            "fox sports": {"id": "fox-sports"},
            "google news": {"id": "google-news"},
            # "gruenderszene": {"id": "gruenderszene"},
            "hacker news": {"id": "hacker-news"},
            # "handelsblatt": {"id": "handelsblatt"},
            "ign": {"id": "ign"},
            "independent": {"id": "independent"},
            "mashable": {"id": "mashable"},
            "metro": {"id": "metro"},
            "mirror": {"id": "mirror"},
            "mtv news": {"id": "mtv-news"},
            "mtv news ukraine": {"id": "mtv-news-uk"},
            "national geographic": {"id": "national-geographic"},
            "new scientist": {"id": "new-scientist"},
            "newsweek": {"id": "newsweek"},
            "new york magazine": {"id": "new-york-magazine"},
            "nfl news": {"id": "nfl-news"},
            "polygon": {"id": "polygon"},
            # "recode": {"id": "recode"},
            # "reddit r all": {"id": "reddit-r-all"},
            "reuters": {"id": "reuters"},
            "spiegel online": {"id": "spiegel-online"},
            "te3n": {"id": "t3n"},
            "talksport": {"id": "talksport"},
            "techcrunch": {"id": "techcrunch"},
            "techradar": {"id": "techradar"},
            "the economist": {"id": "the-economist"},
            "the guardian australia": {"id": "the-guardian-au"},
            "the guardian ukraine": {"id": "the-guardian-uk"},
            "the hindu": {"id": "the-hindu"},
            "the huffington post": {"id": "the-huffington-post"},
            "the lad bible": {"id": "the-lad-bible"},
            "the new york times": {"id": "the-new-york-times"},
            "the next web": {"id": "the-next-web"},
            "the sport bible": {"id": "the-sport-bible"},
            "the telegraph": {"id": "the-telegraph"},
            "the times of india": {"id": "the-times-of-india"},
            "the verge": {"id": "the-verge"},
            "the wall street journal": {"id": "the-wall-street-journal"},
            "the washington post": {"id": "the-washington-post"},
            "time": {"id": "time"},
            "usa today": {"id": "usa-today"},
            "wired": {"id": "wired-de"},
            # "wirtshafts woche": {"id": "wirtshafts-woche"}
        }

    def speak(self, phrase):
        """
        Fido will say the given phrase.
        :phrase: String.
        """
        self.__speech_engine.say(phrase)
        self.__speech_engine.runAndWait()

    def listen(self):
        """
        Fido will listen for an intelligible phrase.
        :return: String.
        :except: None.
        """
        sr = self.__speech_listener
        r = sr.Recognizer()
        with sr.Microphone() as source:
            audio = r.listen(source)

        # Speech recognition using Google Speech Recognition
        try:
            # for testing purposes, we're just using the default API key
            # to use another API key, use `r.recognize_google(audio, key="GOOGLE_SPEECH_RECOGNITION_API_KEY")`
            # instead of `r.recognize_google(audio)`
            phrase = r.recognize_google(audio)
            return str(phrase).lower()
        except sr.UnknownValueError:
            # print("Google Speech Recognition could not understand audio")
            # self.speak("Sorry, I couldn't understand you. Say that again.")
            return None
        except sr.RequestError:
            # print("Could not request results from Google Speech Recognition service; {0}".format(e))
            return None

    def start_ai(self):
        while True:
            self.speak("Hello I am Fido. Where do you want to get your news today?")
            self.speak("Say the phrase \"help one\" for a list of commands.")

            # Select a news resource
            while True:
                # self.__command = input("")
                self.__command = self.listen()
                print(self.__command)
                if self.__command == "exit 1":
                    sys.exit()
                elif self.__command == "sleep one":
                    self.speak("I'll be on standby")
                    sleeping = True
                    while sleeping:
                        wake_up_command = self.listen()
                        if wake_up_command == "listen one":
                            break
                    self.speak("I'm listening")
                elif self.__command == "listen one":
                    self.speak("I'm already awake")
                elif self.__command == "help one":
                    self.speak("Say the phrase \"exit 1\" to close the program")
                    self.speak("Say the phrase \"sleep one\" so I stop listening")
                    self.speak("Say the phrase \"listen one\" so I start listening")
                    self.speak("Say the phrase \"list one\" for a list of available news resources.")
                elif self.__command == "list one":
                    self.speak("Here are a list of available news resources. Please Listen.")
                    self.speak(", ".join(self.__news_platforms.keys()))
                elif self.__command in self.__news_platforms:
                    # User Questions
                    self.speak("Here are articles from %s" % self.__command)
                    self.__request_url = "https://newsapi.org/v1/articles?source=%s&apiKey=%s" % \
                                         (self.__news_platforms[self.__command]["id"], self.__api_key)
                    try:
                        self.__retrieved_articles = \
                            json.loads(urllib.request.urlopen(self.__request_url).read())["articles"]
                        self.__num_of_articles = len(self.__retrieved_articles)
                        for i in range(self.__num_of_articles):
                            print("%d, %s, %s" %
                                  ((i + 1),
                                   self.__retrieved_articles[i]["title"],
                                   self.__retrieved_articles[i]["description"]))
                            self.speak("%d, %s, %s" %
                                       ((i + 1),
                                        self.__retrieved_articles[i]["title"],
                                        self.__retrieved_articles[i]["description"]))

                        # Open a specific article in default web browser
                        self.speak("To open an article on your web browser, say the word article followed by a number.")
                        self.speak("To select a different platform, say continue.")
                        while True:
                            self.__command = self.listen()
                            print(self.__command)
                            if self.__command == "continue":
                                break
                            try:
                                article_num = int(self.__command.split(" ")[1]) - 1
                                print(article_num)
                                if 0 <= article_num < self.__num_of_articles:
                                    print(self.__retrieved_articles[article_num]["url"])
                                    self.__open_browser(self.__retrieved_articles[article_num]["url"])
                            except AttributeError or IndexError:
                                pass

                    except urllib.request.HTTPError as e:
                        print(e)
                        self.speak("My apologies. It appear's I can't connect to %s. "
                                   "Please select a different platform." % self.__command)

    def set_api_key(self, key):
        """
        :param key: String
        """
        self.__api_key = key

    @staticmethod
    def __open_browser(url):
        """
        :param url: String
        """
        print(webbrowser.open_new_tab(url))


def main():
    # Implementation of FidoAI
    fido = FidoAI()
    fido.set_api_key("69500e25d19d467e96c57d0d75abdcf5")
    fido.start_ai()


if __name__ == '__main__':
    main()
\$\endgroup\$
  • \$\begingroup\$ I hope you're aware that you posted your API key here. You should revoke and regenerate it on newsapi.org. \$\endgroup\$ – omgimanerd Aug 14 '17 at 21:03
  • \$\begingroup\$ Yes, I am aware that I posted my API key here. Thank you for the suggestion I have already changed the API key. \$\endgroup\$ – Zakar H. Aug 16 '17 at 0:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.