Exemplo n.º 1
0
class DialogueManager(object):
    """
    Class responsible for the main functionality of the bot and for choosing the type of response for user's input.
    Methods:
    create_chitchat_bot - initializes and trains conversational chatbot.
    generate_answer - recognizes intent of the question and gives an appropriate response.
    """
    def __init__(self, config):
        print("Loading resources...")

        # Intent recognition:
        self.intent_recognizer = unpickle_file(config['PATH']['INTENT_RECOGNIZER'])
        self.tfidf_vectorizer = unpickle_file(config['PATH']['TFIDF_VECTORIZER'])

        self.ANSWER_TEMPLATE = 'I think its about %s\n This thread might help you: https://stackoverflow.com/questions/%s'

        # Goal-oriented part:
        self.tag_classifier = unpickle_file(config['PATH']['TAG_CLASSIFIER'])
        self.thread_ranker = ThreadRanker(config)
        self.chitchat_bot = self.create_chitchat_bot()

        # Twitter keys
        self.twitter_keys = config['TWITTER_API']


    def create_chitchat_bot(self):
        """Initialize self.chitchat_bot with some conversational model."""
        #self.chitchat_bot = ChatBot('Little Bot',
        #                            trainer='chatterbot.trainers.ChatterBotCorpusTrainer')
        #self.chitchat_bot.train("chatterbot.corpus.english")
        self.chitchat_bot = ChatBot()

        return self.chitchat_bot

    def generate_weather_answer(self, question):
        """
        Generate weather forecast for a selected city.
        
        At first try to extract city from user request. If not possible, then generate usual answer.
        Connects to openweathermap and gets forecast, then generates plot of temperature
        in Celsius and Fahrenheit; also shows unique weather conditions.
        """
        # remove previous image, as it isn't needed anymore
        for i in glob.glob(os.path.join(os.getcwd(), '*.png')):
            os.remove(i)

        good_symbols_re = re.compile('[^a-zA-Z -]')
        question_cleaned = good_symbols_re.sub('', question)

        # Extract entities.
        tagged = tree2conlltags(ne_chunk(pos_tag(word_tokenize(question.title()))))
        cities = [i[0] for i in tagged if i[1] == 'NNP']
        city = ''
        for c in cities:
            data = requests.get('http://api.openweathermap.org/data/2.5/forecast?q={0}&appid=f00cf7123615727d162770891d4fd225'.format(c)).json()
            if data['cod'] == '200':
                city = c
                break
        if city == '':
               return self.generate_usual_answer(question)
        else:
            forecast = requests.get('http://api.openweathermap.org/data/2.5/forecast?q={0}&appid=f00cf7123615727d162770891d4fd225'.format(city)).json()
            if forecast['message'] == 'city not found':
                return "I don't know this city!"

            # Generate temperature and date lists for plotting
            date_list = []
            temp_list_c = []
            temp_list_f = []

            for reading in forecast['list']:
                date = datetime.fromtimestamp(int(reading['dt']))
                temperature_c = reading['main']['temp'] - 273.15
                temperature_f = reading['main']['temp'] * 9 / 5 - 459.67
                date_list.append(date)
                temp_list_c.append(temperature_c)
                temp_list_f.append(temperature_f)

            # make chart
            fig, ax = plt.subplots()
            ax.plot_date(date_list, temp_list_c, '-', label='Celsius')
            ax.plot_date(date_list, temp_list_f, '-', label='Fahrenheit')
            ax.grid(True)

            plt.xticks(rotation=30)
            plt.yticks(range(int(min(temp_list_c)) - 1, int(max(temp_list_f) + 1), 5))
            dtFmt = mdates.DateFormatter('%m/%d')
            ax.xaxis.set_major_formatter(dtFmt)
            plt.title('Temperature in {0}'.format(city))
            plt.legend()
            # save image, so it can be sent to user
            plt.savefig('plot.png')

            # List of possible unique weather conditions
            weather = ', '.join(list(set([i['weather'][0]['description'] for i in forecast['list']])))

            return 'Possible weather in the next few days: {0}.;{1}'.format(weather, 'plot.png')

    def generate_twitter_answer(self, question):
        """
        Show the latest tweet for the defined user.
        """
        api = twitter.Api(consumer_key=self.twitter_keys['consumer_key'],
                          consumer_secret=self.twitter_keys['consumer_secret'],
                          access_token_key=self.twitter_keys['access_token_key'],
                          access_token_secret=self.twitter_keys['access_token_secret'])

        good_symbols_re = re.compile('[^a-zA-Z0-9 _]')
        question_cleaned = good_symbols_re.sub('', question.replace('/', ' '))
        account_name = question_cleaned.split(' ')[-1]

        try:
            tweet_id = api.GetUserTimeline(screen_name=account_name, count=1)[0].id
        except twitter.TwitterError:
            return "I don't know this user! Let's continue talking!" + "\n" + self.generate_usual_answer(question)

        return 'https://twitter.com/i/web/status/{0}'.format(tweet_id)

    def generate_usual_answer(self, question):
        """
        Combine stackoverflow and chitchat parts using intent recognition.
        Returns either link to stackoverflow or an answer from chatterbot.
        """

        # Recognize intent of the question using `intent_recognizer`.
        prepared_question = text_prepare(question)
        features = self.tfidf_vectorizer.transform([prepared_question])
        intent = self.intent_recognizer.predict(features)

        # Chit-chat part:
        if intent == 'dialogue':
            # Pass question to chitchat_bot to generate a response.
            #response = self.chitchat_bot.get_response(question).text
            response = self.chitchat_bot.predict(question)
            return response

        # Goal-oriented part:
        else:
            # Pass features to tag_clasifier to get predictions.
            tag = self.tag_classifier.predict(features)

            # Pass prepared_question to thread_ranker to get predictions.
            thread_id = self.thread_ranker.get_best_thread(question, tag[0])

            return self.ANSWER_TEMPLATE % (tag, thread_id)

    def generate_answer(self, question):
        print('question', question)
        if 'weather' in question.lower():
            return self.generate_weather_answer(question)

        elif 'tweet' in question.lower() or 'twitter' in question.lower():
            return self.generate_twitter_answer(question)

        elif question.lower() == 'today!' or question.lower() == 'today':
            # return a random fact about current date
            return requests.get('http://numbersapi.com/{0}/{1}/date'.format(datetime.today().month, datetime.today().day)).text

        elif question.lower() == 'help!' or question.lower() == 'help':
            return """ This chatbot was created based on the final project of this course, for the honor task:
            https://www.coursera.org/learn/language-processing/home/welcome
            Possible commands:
            sentence with word 'weather' and city name - shows weather forecast using openweathermap api;
            'tweet/twitter account_name' - shows the latest tweet by the user;
            'today!' - shows current date and random fact about it;
            Otherwise bot will either chat or try to answer a programming question.
            Programming languages for which the bot can try to give an answer: c\c++, c#, java, javascript, php, python, r, ruby, swift, vb.
            
            Code on github: https://github.com/Erlemar/Simple_chat_bot
            Chatbot idea is taked from this paper: https://www.researchgate.net/publication/321347271_End-to-end_Adversarial_Learning_for_Generative_Conversational_Agents
            and repo: https://github.com/oswaldoludwig/Seq2seq-Chatbot-for-Keras
            """

        else:
            return self.generate_usual_answer(question)