ㅁ 개요
O 프로그램 소개
- 이번 글은 이전글([프로젝트] 파이썬 음성으로 구글 캘린더에 자동으로 일정 등록하는 chatGPT plugin 만들기 - 3.구글 캘린더에 일정 추가/삭제하기)에 이은 5번째 마지막 글로 여기서는 챗GPT와 음성으로 대화하고, 구글 일정 등록을 위해 (가상의)플러그인스토어에서 구글일정등록 플러그인 설치 후 이용자가 몇가지 정보를 음성으로 알려주면 나의 구글 캘린더에 일정을 등록해주는 방법을 알아보겠습니다.
O 아이디어 구상 정리
1.이번 chatGPT 플러그인의 기능
> 기본적으로 chatGPT와 대화할 수 있고, 특정 키워드('일정', '에약', '결제' 등)가 나오면 플러그인 기능을 활용할 수 있도록 함
1.1 기본 기능 : chatGPT와 대화
1.2 플러그인 기능 : 대화속에 "일정" 키워드가 나오면 구글캘린더에 일정을 추가할 수 있도록 안내
2. 평상(대기)시 모드와 대화 모드의 구분
>기본적으로 chatGPT가 명령을 기다리고 있다가 특정 단어("지피티")가 나오면 그때부터 대화 및 플러그인 기능을 이용할 수 있게 함
>아래와 같이 2가지 모드로 구분하여 프로그램 운용
2.1 대기(리슨) 모드 : 아무런 행위("챗GPT를 부르는 등의 행위")가 없으면 계속 리슨하고 있는 상태(예: 1초 간격으로 지피티가 자신을 부르는지를 확인하기 위하여 리슨->대화(없음)->리슨->대화(없음) 모드를 반복하는 행위)
2.2 지피티(대화) 모드 : 대기모드에서 '지피티'라고 말하면 지피티모드로 진입하게 되며, 여기서 chatGPT와 대화 및 특정 키워드로 플러그인을 사용할 수 있게 됨
O 완성된 프로그램 실행 화면
- 최종 완성된 프로그램의 결과화면은 아래와 같습니다.
1.프로그램을 실행하면 아래와 같이 실행됩니다.
사용자가 음성으로 빨간색으로 질문/답변하고,
챗GPT가 음성으로 노란색으로 질문/답변합니다.
2 최종적으로 구글캘린더에서 확인결과 내가 음성으로 말한 내용이 구글 캘린더에 잘 등록된 것을 확인할 수 있습니다.
ㅁ 세부 내용
O 완성된 소스
소스 : 4.voice_add_cal.py
import datetime
from datetime import datetime, timedelta
import time
from google.oauth2.credentials import Credentials #pip install --upgrade google-api-python-client
# pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
from googleapiclient.errors import HttpError
from googleapiclient.discovery import build
import speech_recognition as sr
from gtts import gTTS
import os
import playsound
from translate import Translator
import openai # pip install openai
from config import *
openai.api_key = OPENAI_API_KEY
def textToVoice(text):
tts = gTTS(text=text, lang='ko')
filename='voice.mp3'
tts.save(filename) # 파일을 만들고,
playsound.playsound(filename) # 해당 음성파일을 실행(즉, 음성을 말함)
os.remove(filename)
def voiceToText():
r = sr.Recognizer()
with sr.Microphone() as source:
print("say something: ")
audio = r.listen(source)
said = " "
try:
said = r.recognize_google(audio, language="ko-KR")
print("Your speech thinks like: ", said)
except Exception as e:
print("Exception: " + str(e))
return said
# API 인증 정보
creds = Credentials.from_authorized_user_file('token.json', ['https://www.googleapis.com/auth/calendar'])
# 구글 캘린더 API 클라이언트 생성
service = build('calendar', 'v3', credentials=creds)
# 일정 등록 함수
def create_event(start_time, end_time, summary, location=None, description=None):
event = {
'summary': summary,
'location': location,
'description': description,
'start': {
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'Asia/Seoul',
},
'end': {
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'Asia/Seoul',
},
}
event = service.events().insert(calendarId='primary', body=event).execute()
print(f'Event created: {event.get("htmlLink")}')
def register_schedule():
my_res2 = "yes"
if my_res2 == "yes": #---------------------------------(1)
# print("구글캘린더 플러그인을 설치하였습니다.")
# textToVoice("구글캘린더 플러그인을 설치하였습니다.")
print("일정등록을 위한 몇가지 질문을 드리겠습니다.")
textToVoice("일정등록을 위한 몇가지 질문을 드리겠습니다.")
print("등록일자를 알려주세요. 예를 들어 '2023년 4월 20일 오후 7시'인 경우 '2023 0420 19' 라고만 말씀해 주세요.")
textToVoice("등록일자를 알려주세요. 예를 들어 '2023년 4월 20일 오후 7시'인 경우 '이공이삼 공사이공 일구' 라고만 말씀해 주세요.")
my_res_date = voiceToText()
print("등록할 일정의 내용을 말씀해 주세요.")
textToVoice("등록할 일정의 내용을 말씀해 주세요.")
my_res_cont = voiceToText() # 생일
print(my_res_date, "과", my_res_cont, " 으로 등록하겠습니다. 내용이 맞으면 yes, 틀리면 no라고 말씀해 주세요.") ##########################
textToVoice(str(my_res_date)+"과"+ str(my_res_cont)+" 으로 등록하겠습니다. 내용이 맞으면 yes, 틀리면 no라고 말씀해 주세요.") ##########################
my_res_yesno = voiceToText()
if my_res_yesno == "yes":
try:
# 일정 등록 예시
# my_res_date = "2 0 2 3 0 4 2 0 1 9"
my_res_date = my_res_date.replace(" ","").strip()
# print(my_res_date)
year = my_res_date[:4]
# print(year)
month = my_res_date[4:6]
day = my_res_date[6:8]
hour = my_res_date[8:10]
time_obj = datetime.strptime(my_res_date, "%Y%m%d%H")
time_obj += timedelta(hours=1)
time_str_formatted = time_obj.strftime("%Y%m%d%H%M%S")
# print(time_str_formatted) # 출력 결과: "01:00"
hour2 = str(time_str_formatted)[8:10]
# print(hour2)
minute = "0"
seconed = "0"
start_time = datetime(int(year), int(month), int(day), int(hour), int(minute), int(seconed))
end_time = datetime(int(year), int(month), int(day), int(hour2), int(minute), int(seconed))
summary = my_res_cont
location = ' '
description = my_res_cont
create_event(start_time, end_time, summary, location, description)
print("구글캘린더에 홍길동과 점심약속으로 2023년 4월 20일 오후 7시로 등록되었습니다.") ##########################
textToVoice("구글캘린더에 홍길동과 점심약속으로 2023년 4월 20일 오후 7시로 등록되었습니다.") ##########################
print("지피티모드를 종료하고 대기모드로 돌아갑니다. 제가 필요하면 대기모드에서 '지피티'라고 불러주세요.")
textToVoice("지피티모드를 종료하고 대기모드로 돌아갑니다. 제가 필요하면 대기모드에서 '지피티'라고 불러주세요.")
# break
except HttpError as error:
print(f'An error occurred: {error}')
textToVoice("에러가 발생하였습니다. 지피티 모드를 종료합니다.")
# break
else:
print("내용이 잘못 입력되었습니다. 다시 등록하려면 yes, 여기서 나가시려면 no라고 대답해 주세요.")
textToVoice("내용이 잘못 입력되었습니다. 다시 등록하려면 yes, 여기서 나가시려면 no라고 대답해 주세요.")
my_res_yesno = voiceToText()
if my_res_yesno == "yes":
register_schedule() # -------------------------------- 위의 (1)번으로 돌아감
pass
else:
# break
pass
content = ''
for_break = True
while True:
if for_break == False: # 이중 루프를 빠져나가기 위한 변수 설정임
break
print("챗지피티와 대화하려면 '지피티'라고 불러주시고, 대화를 종료하려면 '굿바이'라고 말씀해 주세요.")
textToVoice("챗지피티와 대화하려면 '지피티'라고 불러주시고, 대화를 종료하려면 '굿바이'라고 말씀해 주세요.")
my_res = voiceToText()
if '굿바이' in my_res:
textToVoice("chatGPT를 종료합니다.")
print("chatGPT를 종료합니다.")
break
if "GPT" in my_res:
print("지피티 모드 입니다.")
textToVoice("지피티 모드 입니다.")
while True:
#현재 시점 이후 30초 경과 알아내기
now = datetime.now()
delta = timedelta(seconds=5)
after_30sec = now + delta
print("네. 주인님. 말씀하세요.")
textToVoice("네. 주인님. 말씀하세요.")
my_res1 = voiceToText() # 일정 등록 좀 해줘 (키워드: 일정)
if '굿바이' in my_res1:
textToVoice("chatGPT를 종료합니다.")
print("chatGPT를 종료합니다.")
for_break = False
break
now = datetime.now()
if my_res1 == " " and (now > after_30sec):
print("5초 동안 아무 움직임이 없어 지피티모드를 종료합니다.(대기(리슨)모드로 돌아갑니다.)")
textToVoice("5초 동안 아무 움직임이 없어 지피티모드를 종료합니다.(대기모드로 돌아 갑니다.)")
time.sleep(1)
break
elif "일정" in my_res1:
print("구글캘린더에 일정을 추가할 수 있습니다. 일정을 등록하기 위해서는 구글 캘린더 플러그인을 설치해야 합니다. \
설치할까요? yes, no로 답변해 주세요.")
textToVoice("구글캘린더에 일정을 추가할 수 있습니다. 일정을 등록하기 위해서는 구글 캘린더 플러그인을 설치해야 합니다. \
설치할까요? yes, no로 답변해 주세요.")
my_res2 = voiceToText()
if my_res2 == "yes" or my_res2 == "예스": #---------------------------------(1)
print("구글캘린더 플러그인을 설치하였습니다.")
textToVoice("구글캘린더 플러그인을 설치하였습니다.")
print("일정등록을 위한 몇가지 질문을 드리겠습니다.")
textToVoice("일정등록을 위한 몇가지 질문을 드리겠습니다.")
print("등록일자를 알려주세요. 예를 들어 '2023년 4월 20일 오후 7시'인 경우 '2023 0420 19' 라고만 말씀해 주세요.")
textToVoice("등록일자를 알려주세요. 예를 들어 '2023년 4월 20일 오후 7시'인 경우 '이공이삼 공사이공 일구' 라고만 말씀해 주세요.")
my_res_date = voiceToText()
# print(my_res_date, type(my_res_date)) ##########################
# time.sleep(10)
print("등록할 일정의 내용을 말씀해 주세요.")
textToVoice("등록할 일정의 내용을 말씀해 주세요.")
my_res_cont = voiceToText() # 생일
print(my_res_date, "과", my_res_cont, " 으로 등록하겠습니다. 내용이 맞으면 yes, 틀리면 no라고 말씀해 주세요.") ##########################
textToVoice(str(my_res_date)+"과"+ str(my_res_cont)+" 으로 등록하겠습니다. 내용이 맞으면 yes, 틀리면 no라고 말씀해 주세요.") ##########################
my_res_yesno = voiceToText()
if my_res_yesno == "yes" or my_res_yesno == "예스":
try:
# 일정 등록 예시
# my_res_date = "2 0 2 3 0 4 2 0 1 9"
my_res_date = my_res_date.replace(" ","").strip()
# print(my_res_date)
year = my_res_date[:4]
# print(year)
month = my_res_date[4:6]
day = my_res_date[6:8]
hour = my_res_date[8:10]
time_obj = datetime.strptime(my_res_date, "%Y%m%d%H")
time_obj += timedelta(hours=1)
time_str_formatted = time_obj.strftime("%Y%m%d%H%M%S")
# print(time_str_formatted) # 출력 결과: "01:00"
hour2 = str(time_str_formatted)[8:10]
# print(hour2)
minute = "0"
seconed = "0"
start_time = datetime(int(year), int(month), int(day), int(hour), int(minute), int(seconed))
end_time = datetime(int(year), int(month), int(day), int(hour2), int(minute), int(seconed))
summary = my_res_cont
location = ' '
description = my_res_cont
create_event(start_time, end_time, summary, location, description)
print("구글캘린더에 "+year+" 년 " + month+" 월 "+ day+" 일 "+ hour+" 에서 "+ hour2 + " 시 사이로 " +summary+" 의 내용으로 등록되었습니다.") ##########################
textToVoice("구글캘린더에 "+year+" 년 " + month+" 월 "+ day+" 일 "+ hour+" 에서 "+ hour2 + " 시 사이로 " +summary+" 의 내용으로 등록되었습니다.") ##########################
print("지피티모드를 종료하고 대기모드로 돌아갑니다. 제가 필요하면 대기모드에서 '지피티'라고 불러주세요.")
textToVoice("지피티모드를 종료하고 대기모드로 돌아갑니다. 제가 필요하면 대기모드에서 '지피티'라고 불러주세요.")
break
except HttpError as error:
print(f'에러가 발생하였습니다. 등록일자가 잘못된것 같습니다. 지피티 모드를 종료합니다.: {error}')
textToVoice("에러가 발생하였습니다. 등록일자가 잘못된것 같습니다. 지피티 모드를 종료합니다.")
break
else:
print("내용이 잘못 입력되었습니다. 다시 등록하려면 yes, 여기서 나가시려면 no라고 대답해 주세요.")
textToVoice("내용이 잘못 입력되었습니다. 다시 등록하려면 yes, 여기서 나가시려면 no라고 대답해 주세요.")
my_res_yesno = voiceToText()
if my_res_yesno == "yes":
register_schedule() # -------------------------------- 위의 (1)번으로 돌아감
pass
else:
break
else:
print("구글달려 플러그인을 설치하지 않았습니다.")
textToVoice("구글달려 플러그인을 설치하지 않았습니다.")
print("지피티모드를 종료하고 대기모드로 돌아갑니다.")
textToVoice("지피티모드를 종료하고 대기모드로 돌아갑니다.")
break
else:
prompt = my_res1
try:
messages = [
{'role': 'system', 'content': 'You are a helpful assistant.'},
{'role': 'user', 'content': content},
]
messages.append({'role': 'assistant', 'content': msg })
messages.append({'role': 'user', 'content': prompt })
except:
messages = [
{'role': 'system', 'content': 'You are a helpful assistant.'},
{'role': 'user', 'content': prompt},
]
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=messages
)
# print(response)
print("--------------------------------")
print(str(response['choices'][0]['message']['content']).strip())
msg = str(response['choices'][0]['message']['content']).strip()
print("--------------------------------")
textToVoice(msg)
# time.sleep(1)
content = content + msg
else:
print("대기모드 입니다.")
textToVoice("대기모드 입니다.")
time.sleep(1)
O 주요 내용
1. 아래 소스코드에 대해서 간략히 설명합니다.
(대부분 이전 글에서 설명한 내용이며, 코드내 주석을 달았으므로 참고하시기 바랍니다.)
1. 관련 모듈을 임포트 합니다.
2. 안내를 위해 텍스트를 음성으로 변환해주는 함수와 chatGPT에게 음성명령을 내릴때 사용하는 voiceToText()함수를 정의합니다.
3. 구글 캘린터 API 사용을 위해 인증/권한 설정으로 해주고, 일정 추가 함수를 만들어 줍니다.
4.여기서는 음성으로 캘린더에 등록해야 하므로 음성을 입력받고 처리해 주는 반복적인 기능을 처리하기 위해 아래와 같이 함수로 만들었습니다.
5.사용자의 음성명령 입력값이 옳지 않거나, 이상한 경우 지피티 모드를 종료하거나 다시 입력 받도록 합니다.
6. 아래와 같이 다시 입력 받도록 본인(재귀함수) 함수를 다시 호출 하고 있습니다.
7. while 문 내에 while문이 하나 더 있습니다. 따라서 2번째 while문을 빠져 나가기 위해서는 break 가 두번 나와야 하므로 꼼수로 for_break = False가 되는 경우 다시 루프를 빠져 나갈 수 있도록 해주었습니다.(이중루프 탈출)
그리고 첫번재 while문에서는 대기모드와 지피티모드를 선택할 수 있게 하였고,
지피티모드로 진입하면 두번재 while문에서는 chatGPT와 대화하거나, '일정' 키워드가 입력되면 플러그인을 사용할 수 있게 하였습니다.
추가로 지피티모드에서 5초 동안 아무런 말을 하지 않으면 지피티모드를 빠져나가도록 설정해 주었습니다.
8. 위에서 설명드린 지피티 모드에서 '굿바이'라고 말하면 대기모드로 들어가게 되는데, 즉 종료되지 않는데 여기서 for_break = False로 설정하여 if조건(한번 더 break)에 의하여 대기모드에서도 빠져나가 종료되도록 설정하였습니다.
대화내용 속에 '일정' 키워드가 존재하면 구글 캘린더에 일정을 추가할 수 있도록 안내해 줍니다.
일정 추가를 위해 사용자로부터 등록일시와 내용을 확인합니다.
9. 음성으로 입력받은 등록일시를 형식(년,월,일, 시,분,초)에 맞게 가공해 줍니다.
10. 입력값에 에러가 없으면 정상적으로 등록 후 안내 메시지를 들려줍니다.
만일 에러가 발생한 경우 지피티 모드를 종료하거나, 다시 음성입력을 받을 수 있도록 안내해 줍니다.
11. 특정 키워드가 아닌 일반적인 질문에는 chatGPT3.5가 대답을 할 수 있도록 아래와 같이 코딩해 줍니다.
(chatGPT3.5 사용 시 적지만 과금이 되니 주의 바랍니다.)
ㅁ 정리
O 우리가 배운 내용
1. 기본적으로 chatGPT와 대화하면서, 특정 키워드('일정', '에약', '결제' 등)가 나오면 플러그인 기능을 활용할 수 있도록 해주었습니다.
1.1 기본 기능 : chatGPT와 대화
1.2 플러그인 기능 : 대화속에 "일정" 키워드가 나오면 구글캘린더에 일정을 추가할 수 있도록 안내
2. 대기 모드와 지피티(대화) 모드로 구분하였습니다.
>기본적으로 chatGPT가 명령을 기다리고 있다가 특정 단어("지피티")가 나오면 그때부터 대화 및 플러그인 기능을 이용할 수 있게 하였습니다.
2.1 대기(리슨) 모드 : 아무런 행위("챗GPT를 부르는 등의 행위")가 없으면 계속 리슨하고 있는 상태(예: 1초 간격으로 지피티가 자신을 부르는지를 확인하기 위하여 리슨->대화(없음)->리슨->대화(없음) 모드를 반복하는 행위)
2.2 지피티(대화) 모드 : 대기모드에서 '지피티'라고 말하면 지피티모드로 진입하게 되며, 여기서 chatGPT와 대화 및 특정 키워드로 플러그인을 사용할 수 있게 됨
이것을 '파이썬 음성으로 구글 캘린더에 자동으로 일정 등록하는 chatGPT plugin 만들기' 프로젝트를 모두 마칩니다.
오늘은 여기까지이며, 댓글과 하트는 제가 이글을 지속할 수 있게 해주는 힘이 됩니다.
위의 내용이 유익하셨다면, 댓글과 하트 부탁드립니다.
감사합니다.
※ 추가적인 정보는 아래 유튜브 영상에서 해당 내용을 더욱 자세히 보실 수 있습니다.