When I ran the program below, the state transition did not go well. linebot> Please enter the place name
User> Nara Prefecture
linebot> Please enter the date
User> Today
linebot> Please enter the place name
Since it has transitioned from ask_place to ask_date once by sending the place name ・ Transition information cannot be retained -The transition information is initialized every time a message is received from the user. It may be either.
In weather_system1.py, every time the reply method is called, sm.start () transitions to the initial state, so when sm.activeStateNames () is empty, it transitions to the initial state.
    def reply(self, push_text):
         sm  = QtScxml.QScxmlStateMachine.fromFile('states.scxml')
         if sm.activeStateNames() == []:
             sm.start()
             self.el.processEvents() 
         else:
             text = push_text
             self.sessiondic = {"statemachine":sm, "place":"", "date":"", "type":""}
             current_state = sm.activeStateNames()[0]
             print("current_state=", current_state)....... 
No change.
How to make state transitions through conversation. Whether there is a dispatcher or idle of python-telegram-bot in line-bot-sdk. (I searched for it, but I'm not good at English, so I might miss it.)
weather_system1.py
import sys
from PySide2 import QtCore, QtScxml
import requests
import json
from datetime import datetime, timedelta, time
class WeatherSystem:
    #List of prefecture names
    prefs = ['Triple', 'Kyoto', 'Saga', 'Hyogo', 'Hokkaido', 'Chiba', 'Wakayama', 'Saitama', 'Oita',
             'Osaka', 'Nara', 'Miyagi', 'Miyazaki', 'Toyama', 'Yamaguchi', 'Yamagata', 'Yamanashi', 'Gifu', 'Okayama',
             'Iwate', 'Shimane', 'Hiroshima', 'Tokushima', 'Ehime', 'Aichi', 'Niigata', 'Tokyo',
             'Tochigi', 'Okinawa', 'Shiga', 'Kumamoto', 'Ishikawa', 'Kanagawa', 'Fukui', 'Fukuoka', 'Fukushima', 'Akita',
             'Gunma', 'Ibaraki', 'Nagasaki', 'Nagano', 'Aomori', 'Shizuoka', 'Kagawa', 'Kochi', 'Tottori', 'Kagoshima','ksjgihurhtknskhifhsignrw22']
    
    #Dictionary for getting latitude and longitude from prefecture name
    latlondic = {'Hokkaido': (43.06, 141.35), 'Aomori': (40.82, 140.74), 'Iwate': (39.7, 141.15), 'Miyagi': (38.27, 140.87),
                 'Akita': (39.72, 140.1), 'Yamagata': (38.24, 140.36), 'Fukushima': (37.75, 140.47), 'Ibaraki': (36.34, 140.45),
                 'Tochigi': (36.57, 139.88), 'Gunma': (36.39, 139.06), 'Saitama': (35.86, 139.65), 'Chiba': (35.61, 140.12),
                 'Tokyo': (35.69, 139.69), 'Kanagawa': (35.45, 139.64), 'Niigata': (37.9, 139.02), 'Toyama': (36.7, 137.21),
                 'Ishikawa': (36.59, 136.63), 'Fukui': (36.07, 136.22), 'Yamanashi': (35.66, 138.57), 'Nagano': (36.65, 138.18),
                 'Gifu': (35.39, 136.72), 'Shizuoka': (34.98, 138.38), 'Aichi': (35.18, 136.91), 'Triple': (34.73, 136.51),
                 'Shiga': (35.0, 135.87), 'Kyoto': (35.02, 135.76), 'Osaka': (34.69, 135.52), 'Hyogo': (34.69, 135.18),
                 'Nara': (34.69, 135.83), 'Wakayama': (34.23, 135.17), 'Tottori': (35.5, 134.24), 'Shimane': (35.47, 133.05),
                 'Okayama': (34.66, 133.93), 'Hiroshima': (34.4, 132.46), 'Yamaguchi': (34.19, 131.47), 'Tokushima': (34.07, 134.56),
                 'Kagawa': (34.34, 134.04), 'Ehime': (33.84, 132.77), 'Kochi': (33.56, 133.53), 'Fukuoka': (33.61, 130.42),
                 'Saga': (33.25, 130.3), 'Nagasaki': (32.74, 129.87), 'Kumamoto': (32.79, 130.74), 'Oita': (33.24, 131.61),
                 'Miyazaki': (31.91, 131.42), 'Kagoshima': (31.56, 130.56), 'Okinawa': (26.21, 127.68)}
    #A dictionary that links states and system utterances
    uttdic = {"ask_place": "Please say the place name",
              "ask_date": "Please say the date",
              "ask_type": "Please tell me the information type"}    
    current_weather_url = 'http://api.openweathermap.org/data/2.5/weather'
    forecast_url = 'http://api.openweathermap.org/data/2.5/forecast'
    appid = '6dbf61393fba9e88099d19dcdafc6c25' #Please enter your APP ID
    
    def __init__(self):
        #Magic about Qt
        app = QtCore.QCoreApplication()
        #Dictionary for managing dialogue sessions
        self.sessiondic = {}
       
        
    
    #A function that extracts prefecture names from text. If not found, an empty string is returned.
    def get_place(self, text):
        for pref in self.prefs:
            if pref in text:
                print(pref)
                return pref
            elif pref == 'ksjgihurhtknskhifhsignrw22':
                return ""
            else:
                continue
    #If the text has "today" or "tomorrow", it is returned. If not found, an empty string is returned.
    def get_date(self, text):
        if "today" in text:
            return "today"
        elif "tomorrow" in text:
            return "tomorrow"
        else:
            return ""
    #If the text has "weather" or "temperature", it is returned. If not found, an empty string is returned.
    def get_type(self, text):
        if "weather" in text:
            return "weather"
        elif "temperature" in text:
            return "temperature"
        else:
            return ""
    def get_current_weather(self, lat,lon):
        #Get weather information
        response = requests.get("{}?lat={}&lon={}&lang=ja&units=metric&APPID={}".format(self.current_weather_url,lat,lon,self.appid))
        return response.json()
    def get_tomorrow_weather(self, lat,lon):
        #Get time today
        today = datetime.today()
        #Get tomorrow's time
        tomorrow = today + timedelta(days=1)
        #Get tomorrow noon time
        tomorrow_noon = datetime.combine(tomorrow, time(12,0))
        #Convert to UNIX time
        timestamp = tomorrow_noon.timestamp()
        #Get weather information
        response = requests.get("{}?lat={}&lon={}&lang=ja&units=metric&APPID={}".format(self.forecast_url,lat,lon,self.appid))
        dic = response.json()
        #Loop for weather information every 3 hours
        for i in range(len(dic["list"])):
            #i-th weather information (UNIX time)
            dt = float(dic["list"][i]["dt"])
            #The weather information will be returned when the data becomes data after noon tomorrow.
            if dt >= timestamp:
                return dic["list"][i]
        return ""
    """def initial_message(self):
        self.el  = QtCore.QEventLoop()        
        #Read SCXML file
        sm  = QtScxml.QScxmlStateMachine.fromFile('states.scxml')
        #A dictionary containing session IDs and session-related information
        self.sessiondic = {"statemachine":sm, "place":"", "date":"", "type":""}
        #Transition to the initial state
        sm.start()
        self.el.processEvents()
        #Get the initial state
        current_state = sm.activeStateNames()[0]
        print("current_state=", current_state)
        #Acquisition and output of system utterances linked to the initial state
        sysutt = self.uttdic[current_state]
        return {"utt":"This is a weather information guidance system." + sysutt, "end":False}"""
    def reply(self, push_text):
        self.el  = QtCore.QEventLoop()        
        #Read SCXML file
        sm  = QtScxml.QScxmlStateMachine.fromFile('states.scxml')
        #A dictionary containing session IDs and session-related information
        self.sessiondic = {"statemachine":sm, "place":"", "date":"", "type":""}
        #Transition to the initial state
        sm.start()
        self.el.processEvents()
        print('It's cool so far')
        text = push_text
        self.sessiondic = {"statemachine":sm, "place":"", "date":"", "type":""}
        current_state = sm.activeStateNames()[0]
        print("current_state=", current_state)        
        #State transition using user input
        if current_state == "ask_place":
            place = self.get_place(text)
            if place != "":
                sm.submitEvent("place")
                self.el.processEvents()
                self.sessiondic["place"] = place
                print('Cheeks')
                print("current_state=", current_state)
        elif current_state == "ask_date":
            date = self.get_date(text)
            if date != "":
                sm.submitEvent("date")
                self.el.processEvents()
                self.sessiondic["date"] = date
                print('Hehehe')
                print("current_state=", current_state)
        elif current_state == "ask_type":
            _type = self.get_type(text)
            if _type != "":
                sm.submitEvent("type")
                self.el.processEvents()
                self.sessiondic["type"] = _type
        #Get the state of the transition destination
        current_state = sm.activeStateNames()[0]
        print("current_state=", current_state)
        #The transition destination is tell_In the case of info, tell the information and finish
        if current_state == "tell_info":
            utts = []
            utts.append("I will tell you")
            place = self.sessiondic["place"]
            date = self.sessiondic["date"]
            _type = self.sessiondic["type"]
            lat = self.latlondic[place][0] #Get latitude from place
            lon = self.latlondic[place][1] #Get longitude from place
            print("lat=",lat,"lon=",lon)
            if date == "today":
                cw = self.get_current_weather(lat,lon)
                if _type == "weather":
                    utts.append(cw["weather"][0]["description"]+"is")
                elif _type == "temperature":
                    utts.append(str(cw["main"]["temp"])+"Degree")
            elif date == "tomorrow":
                tw = self.get_tomorrow_weather(lat,lon)
                if _type == "weather":
                    utts.append(tw["weather"][0]["description"]+"is")
                elif _type == "temperature":
                    utts.append(str(tw["main"]["temp"])+"Degree")
            utts.append("Thank you for using")
            del self.sessiondic
            return {"utt":"。".join(utts), "end": True}
        else:
            #For other transition destinations, generate system utterances associated with the state
            sysutt = self.uttdic[current_state]
            return {'utt':sysutt,'end':False}
main.py
from flask import Flask,request,abort
from linebot import LineBotApi,WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent,TextMessage,TextSendMessage
import os
import requests
import pprint
from weather_system1 import WeatherSystem
import weather_system1
app=Flask(__name__)
#Get environment variables
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
line_bot_api=LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler=WebhookHandler(YOUR_CHANNEL_SECRET)
weather = WeatherSystem()
@app.route("/callback",methods=["POST"])
def callback():
    print("Callback 1")
    signature=request.headers["X-Line-Signature"]
    print(signature)
    body=request.get_data(as_text=True)
    app.logger.info("Request body"+body)
    try:
        handler.handle(body,signature)
    except InvalidSignatureError:
        abort(400)
    print('Callback')
    return "OK"
@handler.add(MessageEvent,message=TextMessage)
def handle_message(event):
    print('Handle message')
    #Stores the entered character string
    push_text = event.message.text
    #weather_system1.Weather in py_Instantiate the system class
   
    reply_text = weather.reply(push_text)["utt"]
    #Description of reply part
    line_bot_api.reply_message(event.reply_token,TextSendMessage(text=reply_text))
if __name__=="__main__":
    port=int(os.getenv("PORT",5000))
    app.run(host="0.0.0.0",port=port)
state.scxml
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="ask_place">
  <state id="ask_place">			      
    <transition event="place" target="ask_date"/>
  </state>
  <state id="ask_date">			      
    <transition event="date" target="ask_type"/>
  </state>
  <state id="ask_type">			      
    <transition event="type" target="tell_info"/>
  </state>
  <final id="tell_info"/>
</scxml>
        Recommended Posts