from typing import Dict, List from fastapi import FastAPI, APIRouter, HTTPException, Request, Response # type: ignore from fastapi.responses import JSONResponse, RedirectResponse, HTMLResponse # type: ignore from datetime import datetime, timedelta from fastapi.templating import Jinja2Templates from app.dialogflow.services.dialog_service import dialog_service from utils.format_data_dialog import format_time, get_weekday_name from common.external.external_api import api from app.dialogflow.services.origin_codes import origin_codes from common.response.respone_dialogflow import DialogFlowResponseAPI router = APIRouter() templates = Jinja2Templates(directory="templates") @router.post('/search/origin-city/from/office') async def search_origin_office(request: Request): body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") raw_departure_city = parameters.get("departure_city", None) origin_office = parameters.get("origin_office") departure_city = None if origin_office and raw_departure_city is None: departure_city = await dialog_service.get_origin_city_from_office(origin_office) parameters = { "departure_city": departure_city } return DialogFlowResponseAPI(parameters=parameters) @router.post('/search/destination-city/from/office') async def search_destination_office(request: Request): body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") raw_destination_city = parameters.get("destination_city", None) dest_office = parameters.get("dest_office") destination_city = None if dest_office and raw_destination_city is None: destination_city = await dialog_service.get_destination_city_from_office(dest_office) parameters = { "destination_city": destination_city } return DialogFlowResponseAPI(parameters=parameters) @router.post('/info/confirm') async def confirm(request: Request): body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters", {}) if isinstance(session_info.get("parameters"), dict) else {} raw_date = parameters.get("date") departure_city = parameters.get("departure_city") destination_city = parameters.get("destination_city") origin_office = parameters.get("origin_office") dest_office = parameters.get("dest_office") raw_ticket_number = parameters.get("ticket_number") ticket_number = int(raw_ticket_number) if raw_ticket_number else 1 date = dialog_service.to_datetime_from_Dialogflow(raw_date) date, week_day = get_weekday_name(date) text = [f"""**Ngày:** {date} {week_day}\n**Số vé:** {ticket_number}"""] temp = "" if origin_office and dest_office: temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {dest_office}\n""" elif origin_office and destination_city: temp = f"""**Điểm đi:** {origin_office}\n**Điểm đến:** {destination_city}\n""" elif dest_office and departure_city: temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {dest_office}\n""" elif departure_city and destination_city: temp = f"""**Điểm đi:** {departure_city}\n**Điểm đến:** {destination_city}\n""" text[0] = temp + text[0] payload = { "richContent": [ [ { "type": "chips", "options": [ {"text": "Tìm chuyến xe"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) @router.post('/routes') async def route(request: Request): body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") origin_office = parameters.get("origin_office") dest_office = parameters.get("dest_office") raw_departure_city, raw_destination_city, raw_ticket_number , raw_date, _ = dialog_service.get_param_from_dialogflow(body) ticket_count = int(raw_ticket_number) if raw_ticket_number else 1 if raw_date is None: from_time, to_time = dialog_service.process_dates_to_timestamp() date = datetime.today().date().strftime('%m-%d-%Y') else: date = raw_date.strftime('%m-%d-%Y') from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date) origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None if origin_office: origin_code, origin_id = await dialog_service.find_id_and_code_provine_by_name_office(origin_office) origin_ids = await dialog_service.find_id_office_by_name_office(origin_office) elif raw_departure_city: origin_code = origin_codes.get(raw_departure_city) if dest_office: dest_code, dest_id = await dialog_service.find_id_and_code_provine_by_name_office(dest_office) dest_ids = await dialog_service.find_id_office_by_name_office(dest_office) elif raw_destination_city: dest_code = origin_codes.get(raw_destination_city) route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids) payload = { "from_time": from_time, "to_time": to_time, "route_ids": route_ids, "ticket_count": 1, "sort_by": ["price", "departure_time"] } try: respone = await api.post("/search/trips", payload=payload) data = respone["data"]["items"] total = respone["data"]["total"] if total > 0: price = data[0]["price"] list_raw_departure_times = sorted(list(set([ trip["raw_departure_time"] for trip in data]))) list_raw_seat_type = list(set([ trip["seat_type_name"] for trip in data])) seat_type_trip_string = "**" + "** | **".join(map(str, list_raw_seat_type)) + "**" schedule_time_trip = "**" + "** | **".join(map(str, list_raw_departure_times)) + "**" duration = data[0]["duration"] link = f'https://stag.futabus.vn/dat-ve?from={departure_code}&fromTime={date}&isReturn=false&ticketCount={ticket_count}&to={destination_code}&toTime=' text = [f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n \ Loại xe: {seat_type_trip_string} \n \ Thời gian hành trình: {duration} giờ \n \ Giá vé: **{price}** VND"] payload = { "richContent": [ [ { "type": "chips", "options": [ {"text": "Tìm chuyến xe"}, {"text": "Xem lịch trình khác"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."] payload = { "richContent": [ [ { "type": "chips", "options": [ {"text": "Xem lịch trình khác"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) except Exception as e: return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/price') async def price(request: Request): body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") raw_departure_city, raw_destination_city, _, raw_date, _ = dialog_service.get_param_from_dialogflow(body) if raw_date is None: from_time, to_time = dialog_service.process_dates_to_timestamp() from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date) origin_office = parameters.get("origin_office") dest_office = parameters.get("dest_office") origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None if origin_office: origin_code, origin_id = await dialog_service.find_id_and_code_provine_by_name_office(origin_office) origin_ids = await dialog_service.find_id_office_by_name_office(origin_office) elif raw_departure_city: origin_code = origin_codes.get(raw_departure_city) if dest_office: dest_code, dest_id = await dialog_service.find_id_and_code_provine_by_name_office(dest_office) dest_ids = await dialog_service.find_id_office_by_name_office(dest_office) elif raw_destination_city: dest_code = origin_codes.get(raw_destination_city) route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids) payload = { "from_time": from_time, "to_time": to_time, "route_ids": route_ids, "ticket_count": 1, "sort_by": ["price", "departure_time"] } try: respone = await api.post("/search/trips", payload=payload) total = respone["data"]["total"] if total > 0: price = respone["data"]["items"][0]["price"] text = [f"Tuyến xe **{raw_departure_city}** - **{raw_destination_city}**\n" f"Giá vé: **{price}** VND.\n \ Bạn có muốn đặt vé không?" ] payload = { "richContent": [ [ { "type": "chips", "options": [ {"text": "Có, tôi muốn đặt vé"}, {"text": "Xem giá vé tuyến xe khác"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": "Xem giá vé lịch trình khác"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) except: return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/trip/route/list') async def booking_trip(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, raw_time_of_day = dialog_service.get_param_from_dialogflow(body) origin_office = parameters.get("origin_office") dest_office = parameters.get("dest_office") from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date) ticket_count = int(raw_ticket_number) if raw_ticket_number else 1 origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None if origin_office: origin_code, origin_id = await dialog_service.find_id_and_code_provine_by_name_office(origin_office) origin_ids = await dialog_service.find_id_office_by_name_office(origin_office) elif raw_departure_city: origin_code = origin_codes.get(raw_departure_city) if dest_office: dest_code, dest_id = await dialog_service.find_id_and_code_provine_by_name_office(dest_office) dest_ids = await dialog_service.find_id_office_by_name_office(dest_office) elif raw_destination_city: dest_code = origin_codes.get(raw_destination_city) route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids) try: data = await dialog_service.search_trip(from_time, to_time, route_ids, ticket_count) total = len(data) if total > 0: trips = [] routes_name = [] for trip in data: if ticket_count <= trip["empty_seat_quantity"]: route = f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}" if trip["route"] and route not in routes_name: routes_name.append(f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}") trips.append({ "route_name": f"{trip['route']['origin_hub_name']} => {trip['route']['dest_hub_name']}", "route_id": trip["route_id"], "id": trip["id"], "departure_date": trip["raw_departure_date"], "departure_time": trip["raw_departure_time"], "kind": trip["seat_type_name"], "way_id": trip["way_id"] }) text = ["Quý khách vui lòng lựa chọn chuyến xe\n" + "\n".join(f"{i+1}. {name}" for i, name in enumerate(routes_name))] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": name} for name in (routes_name) ] } ] ] } parameters = { "trip_list": trips, "routes_name": routes_name } return DialogFlowResponseAPI(text=text, payload=payload,parameters=parameters) text = [f"Hệ thống không tìm thấy tuyến xe **{raw_departure_city}** - **{raw_destination_city}**.\n Quý khách vui lòng thử lại với lộ trình khác hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": "Xem tuyến xe khác"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) except Exception as e: print(e) return JSONResponse( { "fulfillment_response": { "messages": [ { "text": { "text": ["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."] } } ] }, } ) @router.post('/trip/route/check-route-select') async def is_valid_select_trip(request: Request) -> Response: body = await request.json() raw_input = body.get("text", "") session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") routes_name = parameters.get("routes_name") if raw_input: raw_input = raw_input.strip() if raw_input in routes_name: trip_list: List[Dict[str, any]] = parameters.get("trip_list") for trip in trip_list: if trip["route_name"] == raw_input: route_id = int(trip["route_id"]) kind = trip["kind"] way_id = trip["way_id"] break parameters = { "is_valid_trip": True, "route_name": raw_input, "kind": kind, "route_id": route_id, "way_id": way_id, } else: parameters = { "is_valid_trip": False, } return DialogFlowResponseAPI(parameters=parameters) @router.post('/trip/time-trip-list') async def time_trip(request: Request) -> Response: try: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip_list: List[Dict[str, any]] = parameters.get("trip_list", []) raw_route_id = parameters.get("route_id", None) route_name = parameters.get("route_name", "") route_id = int(raw_route_id) if raw_route_id else None time_list = [] for trip in trip_list: if (trip["route_id"]) == route_id: time_list.append({"time": trip["departure_time"], "id": trip["id"]}) parameters = { "time_list": time_list } text = [f"Quý khách lựa chọn thời gian chuyến {route_name}\n" + " | ".join([item["time"] for item in time_list])] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": time["time"]} for time in (time_list) ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload ,parameters=parameters) except Exception as e: print(e) return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/trip/check-time-select') async def is_valid_select_time(request: Request) -> Response: try: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") raw_departure_city, raw_destination_city, raw_ticket_number, raw_date, raw_time_of_day = dialog_service.get_param_from_dialogflow(body) from_time, to_time = dialog_service.process_dates_to_timestamp(raw_date) ticket_count = int(raw_ticket_number) if raw_ticket_number else 1 origin_office = parameters.get("origin_office") dest_office = parameters.get("dest_office") origin_code, origin_id, origin_ids, dest_code, dest_id, dest_ids = None, None, None, None, None, None if origin_office: origin_code, origin_id = await dialog_service.find_id_and_code_provine_by_name_office(origin_office) origin_ids = await dialog_service.find_id_office_by_name_office(origin_office) elif raw_departure_city: origin_code = origin_codes.get(raw_departure_city) if dest_office: dest_code, dest_id = await dialog_service.find_id_and_code_provine_by_name_office(dest_office) dest_ids = await dialog_service.find_id_office_by_name_office(dest_office) elif raw_destination_city: dest_code = origin_codes.get(raw_destination_city) route_ids = await dialog_service.search_all_route_ids(origin_code=origin_code, from_id=origin_id, orign_ids=origin_ids, dest_code=dest_code, to_id=dest_id, dest_ids=dest_ids) time_list: List[Dict[str, any]] = parameters.get("time_list", []) time_select = parameters.get("time-select", None) route_name = parameters.get("route_name", None) if time_select is not None: time_select = format_time(time_select) for time in time_list: if time_select == time["time"]: id = int(time["id"]) trip = await dialog_service.search_trip_by_id(id, from_time, to_time, route_ids, ticket_count) departure_date = trip["raw_departure_date"] parameters = { "is_valid_time": True, "departure_time": time_select, "departure_date": departure_date, "trip": trip, } text = [f' Quý khách chọn chuyến **{time_select}** | **{route_name}**'] return DialogFlowResponseAPI(text=text, parameters=parameters) parameters = { "is_valid_time": False } text = [] return DialogFlowResponseAPI(text=text, parameters=parameters) except Exception as e: print(e) return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/trip/seats') async def seats_trip(request: Request) -> Response: try: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip = parameters.get("trip", None) route_id: int = parameters.get("route_id", None) route_id = int(route_id) if route_id else None departure_date: str = parameters.get("departure_date", None) departure_time: str = parameters.get("departure_time", None) kind: str = parameters.get("kind", None) trip_id = int(trip["id"]) if trip else None seats = await dialog_service.seats_trip(route_id, trip_id, departure_date, departure_time, kind) seats_empty = [ seat for seat in seats if seat["bookStatus"] == 0 ] seats_empty.sort(key=lambda x: x["chair"]) text=["Vui lòng chọn ghế"] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": seat["chair"]} for seat in (seats_empty) ] } ] ] } parameters = { "seat_list": seats } return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters) except Exception as e: print(e) return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/trip/check-seat-select') async def is_valid_select_seat(request: Request) -> Response: try: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip = parameters.get("trip", None) route_id: int = parameters.get("route_id", None) route_id = int(route_id) if route_id else None departure_date: str = parameters.get("departure_date", None) departure_time: str = parameters.get("departure_time", None) kind: str = parameters.get("kind", None) seat: str = parameters.get("seat", None) trip_id = int(trip["id"]) if trip else None is_valid = await dialog_service.is_valid_select_seat(seat, route_id, trip_id, departure_date, departure_time, kind) if is_valid: parameters = { "is_valid_seat": True, "seat": seat } text = [f"Quý khách chọn ghế **{seat}**"] else: parameters = { "is_valid_seat": False, "seat": None } text = [f"Ghế **{seat}** đã được đặt. Quý khách vui lòng chọn ghế khác"] return DialogFlowResponseAPI(text=text, parameters=parameters) except Exception as e: return DialogFlowResponseAPI(text=["Hệ thống xảy ra lỗi. Quý khách vui lòng thử lại sau hoặc liên hệ Trung tâm tổng đài 1900 6067 để được hỗ trợ."]) @router.post('/trip/stop/pickup') async def pickup(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip: dict[str, any] = parameters.get("trip", None) route_id: int = trip["route_id"] route_id = int(route_id) if route_id else None way_id: int = trip["way_id"] way_id = int(way_id) if way_id else None pickup_list = await dialog_service.pickup_list(route_id, way_id) text=["Quý khách vui lòng chọn điểm đón"] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": pickup["name"]} for pickup in (pickup_list) ] } ] ] } parameters = { "pickup_list": pickup_list } return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters) @router.post('/trip/check-pickup-select') async def is_valid_select_pickup(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip = parameters.get("trip", None) route_id: int = trip["route_id"] route_id = int(route_id) if route_id else None way_id: int = trip["way_id"] way_id = int(way_id) if way_id else None raw_input = (body.get("text","")) pickup = raw_input.strip() is_valid = await dialog_service.is_valid_pickup(pickup, route_id, way_id) if is_valid: parameters = { "is_valid_pickup": True, "pick_up": pickup } text = [f"Quý khách chọn điểm đón **{pickup}**"] else: parameters = { "is_valid_pickup": False, } text = [f"Điểm đón không hợp lệ. Quý khách vui lòng chọn điểm đón khác"] return DialogFlowResponseAPI(text=text, parameters=parameters) @router.post('/trip/stop/dropoff') async def dropoff(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip = parameters.get("trip", None) route_id: int = trip["route_id"] route_id = int(route_id) if route_id else None way_id: int = trip["way_id"] way_id = int(way_id) if way_id else None dropoff_list = await dialog_service.dropoff_list(route_id, way_id) text=["Quý khách vui lòng chọn điểm trả khách"] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": dropoff["name"]} for dropoff in (dropoff_list) ] } ] ] } parameters = { "dropoff_list": dropoff_list } return DialogFlowResponseAPI(text=text, payload=payload, parameters=parameters) @router.post('/trip/check-dropoff-select') async def is_valid_select_dropoff(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") trip = parameters.get("trip", None) route_id: int = trip["route_id"] route_id = int(route_id) if route_id else None way_id: int = trip["way_id"] way_id = int(way_id) if way_id else None raw_input = (body.get("text","")) dropoff = raw_input.strip() is_valid = await dialog_service.is_valid_dropoff(dropoff, route_id, way_id) if is_valid: parameters = { "is_valid_dropoff": True, "drop_off": dropoff } text = [f"Quý khách chọn điểm trả khách **{dropoff}**"] else: parameters = { "is_valid_dropoff": False, } text = [f"Điểm trả khách không hợp lệ. Quý khách vui lòng chọn điểm trả khách khác"] return DialogFlowResponseAPI(text=text, parameters=parameters) @router.post('/trip/ticket/info') async def response_ticket_info(request: Request) -> Response: body = await request.json() session_info = body.get("sessionInfo", {}) parameters = session_info.get("parameters") user_name = parameters.get("user_name", None) phone_number = parameters.get("phone_number", None) email = parameters.get("email", None) seat = parameters.get("seat", None) pickup = parameters.get("pick_up", None) dropoff = parameters.get("drop_off", None) trip = parameters.get("trip", None) route_name = trip["route"]["name"] time = trip["raw_departure_time"] date = trip["raw_departure_date"] price = trip["price"] price = int(price) if price else None text = [ f" \ **Thông tin hành khách**\n\ **Họ và tên** {user_name} \n\ **Số điện thoại** {phone_number}\n\ **Email** {email} \n\ **Thông tin lượt đi**\n\ **Tuyến xe** {route_name} \n\ **Thời gian xuất bến** {time} {date} \n\ **Số ghế** {seat} \n\ **Điểm lên xe** {pickup} \n\ **Điểm trả khách** {dropoff} \n\ **Tổng tiền lượt đi** {price} VND \ " ] payload={ "richContent": [ [ { "type": "chips", "options": [ {"text": "Đặt vé"}, {"text": "Không, cảm ơn"} ] } ] ] } return DialogFlowResponseAPI(text=text, payload=payload) @router.get("/") def home(): return "Hello World!" @router.get('/chatbot', response_class=HTMLResponse) async def index(request: Request): return templates.TemplateResponse("index.html", {"request": request, "title": "Dawi Chatbot"})