임의로만든 AI 구글 캘린더

이주원·2025년 4월 15일

컴퓨터언어

목록 보기
22/50

OLLAMA - exaone3.5:2.4b모델 사용

  from googleapiclient.discovery import build # Google API 클라이언트 생성 도구
  from google_auth_oauthlib.flow import InstalledAppFlow # OAuth 인증 흐름을 다루는 도구
  from google.auth.transport.requests import Request # 토큰 갱신 시 필요한 요청 객체
  from datetime import datetime #날짜 다룰 때 사용
  # 파일 저장 및 불러오기용 (토큰 저장)
  import os
  import pickle
  import requests
  import json
  import re

  #  사용할 모델 이름
  model_name = "exaone3.5:2.4b"

  # JSON 블록 추출 함수
  def extract_json(text):
      # ```json 블록 내부만 추출
      pattern = r"```json\s*([\s\S]*?)\s*```"
      match = re.search(pattern, text)
      if not match:
          print("❌ JSON 블록을 찾지 못했습니다.")
          return None

      json_str = match.group(1)

      try:
          data = json.loads(json_str)
          return data
      except json.JSONDecodeError as e:
          print("❌ JSON 파싱 실패:", e)
          return None


  ################################################ 구글 켈린더 엑세스
  # 인증 범위 지정
  SCOPES = ['https://www.googleapis.com/auth/calendar']

  # 사용자 인증 + access_token 관리
  def get_credentials():
      creds = None

      if os.path.exists('token.pickle'):
          with open('token.pickle','rb') as token : 
              creds = pickle.load(token)

      if not creds or not creds.valid:
          if creds and creds.expired and creds.refresh_token:
              creds.refresh(Request())
          else:
              flow = InstalledAppFlow.from_client_secrets_file(
                  'client_secret.json', SCOPES )
              creds = flow.run_local_server(port=8080)

          # 새 토큰 저장
          with open('token.pickle', 'wb') as token:
              pickle.dump(creds, token)

      return creds  # ✅ 함수 끝에서 항상 반환
  ################################################ 구글 켈린더 엑세스

  ################################################ user input 분류
  classify_set = [
      # ✅ ADD
      {"input": "오늘 오후에 영화 보자", "output": "add"},
      {"input": "5월 3일에 생일 파티 일정 추가해줘", "output": "add"},
      {"input": "이번 주 금요일에 미용실 예약 좀 넣어줘", "output": "add"},
      {"input": "4월 20일에 친구랑 저녁 약속 있어", "output": "add"},
      {"input": "모레 점심 먹자", "output": "add"},
      {"input": "내일 오전 9시에 회의 있어", "output": "add"},
      {"input": "주말에 등산 일정 잡아줘", "output": "add"},
      {"input": "다음주 화요일에 프로젝트 발표 있어", "output": "add"},
      {"input": "오늘 밤에 헬스장 갈 거야", "output": "add"},
      {"input": "7시에 엄마랑 전화하기 일정 넣어줘", "output": "add"},

      # ✅ DELETE
      {"input": "오늘 저녁 약속 취소해줘", "output": "delete"},
      {"input": "5시 회의 일정 없애줘", "output": "delete"},
      {"input": "내일 생일 파티 취소됐어", "output": "delete"},
      {"input": "친구 만나는 일정 지워줘", "output": "delete"},
      {"input": "방금 넣은 일정 삭제해줘", "output": "delete"},
      {"input": "이번 주말 일정 취소할래", "output": "delete"},
      {"input": "3시에 예약한 거 없애줘", "output": "delete"},
      {"input": "다음주 월요일 약속 취소해줘", "output": "delete"},
      {"input": "쇼핑 일정 삭제해줘", "output": "delete"},
      {"input": "헬스장 안 가기로 했어", "output": "delete"},

      # ✅ EDIT
      {"input": "내일 회의 시간 바꿔줘", "output": "edit"},
      {"input": "오늘 약속 3시로 변경해줘", "output": "edit"},
      {"input": "저녁 6시 약속 7시로 옮겨줘", "output": "edit"},
      {"input": "생일 파티 장소 바뀌었어", "output": "edit"},
      {"input": "오후 회의 Zoom 링크로 수정해줘", "output": "edit"},
      {"input": "영화 시간 2시로 바꿔줘", "output": "edit"},
      {"input": "점심 시간 다시 조정해줘", "output": "edit"},
      {"input": "오늘 일정 제목 바꿔줘", "output": "edit"},
      {"input": "내일 약속 위치 바뀜", "output": "edit"},
      {"input": "저녁 약속 시간 변경해줘", "output": "edit"}
  ]

  def Input_analysis(user_input):
      user_prompt=user_input
      system_prompt=f'''
      1. Please analyze the sentence : {user_input}
      2. Please classify the sentences into 3 categories. [ Add schedule, delete schedule, edit schedule ]
      3. If you are adding a schedule, print Add.
      4. If the schedule is deleted, output Delete.
      5. If the schedule is modified, print Edit.
      default. Please print it out as in the example : {classify_set}
      '''
      final_prompt=f"{system_prompt} \n\n User:{user_prompt}"
      # AI에게 일정 폼으로 변환 요청
      response = requests.post(
          "http://localhost:11434/api/generate",
          json={
              "model": "exaone3.5:2.4b",  # 사용 중인 모델 이름 (예: ollama 로컬 서버)
              "prompt": final_prompt   
              ,
              "stream": False
          }
      )
      # 응답 파싱
      result = response.json()
      return result["response"]

  def extract_output(text):
      # JSON 배열 추출 시 ```json ``` 포함 여부와 관계없이 처리
      pattern = r"\[.*?\]"
      match = re.search(pattern, text, re.DOTALL)

      if not match:
          print("❌ JSON 배열을 찾을 수 없습니다.")
          return "re"

      json_str = match.group(0)

      try:
          # 작은따옴표 → 큰따옴표로 변경 후 파싱
          data = json.loads(json_str.replace("'", '"'))
          for item in data:
              print("item['output']",item['output'])
          return item['output']
      except json.JSONDecodeError as e:
          print("❌ JSON 파싱 실패:", e)
          return "re"
  ################################################ user input 분류

  ################################################ 일정 추가
  # 현재 날짜와 시간
  now = datetime.now()
  def MakeSchedule(user_input):
      # 사용자로부터 자연어 일정 입력 받기
      user_prompt = user_input #input("🗓️ 일정을 입력하세요: ")
      event_data=None
      while event_data==None :
          system_prompt = f'''
              0. Always remember the date : {now} 
              1. Please analyze the next schedule : {user_prompt} 
              2. Please write as in the example:
              {{
                  "summary": "event_data['summary']",
                  "location": "event_data['location']",
                  "description": "event_data['description']",
                  "start": {{
                      "dateTime": "event_data['start'] + '+09:00'",
                      "timeZone": "Asia/Seoul"
                  }},
                  "end": {{
                      "dateTime": "event_data['end'] + '+09:00'",
                      "timeZone": "Asia/Seoul"
                  }},
                  "reminders": {{
                      "useDefault": false,
                      "overrides": [
                      {{"method": "popup", "minutes": 10}}
                      ]
                  }}
              }}
              "3. If there is no data, write it randomly.\n"
              "4. Please enter the date and time exactly in the format '2023-10-28T13:00:00+09:00'. Do not use values ​​such as 'XX:XX'. \n"
              "default : Return in JSON format"
              )
              '''
          final_prompt=f"{system_prompt}\n\n User: {user_prompt}"

          # AI에게 일정 폼으로 변환 요청
          response = requests.post(
              "http://localhost:11434/api/generate",
              json={
                  "model": "exaone3.5:2.4b",  # 사용 중인 모델 이름 (예: ollama 로컬 서버)
                  "prompt": final_prompt   
                  ,
                  "stream": False
              }
          )

          # 응답 파싱
          result = response.json()
          # AI 응답 문자열에서 유효한 JSON만 추출해 dict로 반환
          event_data = extract_json(result["response"])

          if event_data is None:
              print("❌ 응답을 JSON으로 해석할 수 없습니다.")
              return None

      # Google Calendar API에 맞는 폼으로 변환
      event = {
          "summary": event_data["summary"],   
          "location": event_data["location"],
          "description": event_data["description"],
          "start": {
              "dateTime": event_data["start"]["dateTime"],
              "timeZone": event_data["start"]["timeZone"]
          },
          "end": {
              "dateTime": event_data["end"]["dateTime"],
              "timeZone": event_data["end"]["timeZone"]
          },
          "reminders": {
              "useDefault": event_data.get("reminders", {}).get("useDefault", False),
              "overrides": event_data.get("reminders", {}).get("overrides", [])
          }
      }

      # 디버깅용 출력
      print("\n📋 생성된 일정:")
      print(json.dumps(event, indent=4, ensure_ascii=False))

      return event

  # 실제 이벤트 등록 함수
  # 위에서 얻은 인증 정보로 API를 사용할 수 있는 service 객체 생성
  def add_event(make_event):
      try:
          creds = get_credentials()
          service = build('calendar', 'v3', credentials=creds)
          event = make_event
          print("\n📤 보내는 이벤트 JSON:")
          print(json.dumps(event, indent=4, ensure_ascii=False))
          event_result = service.events().insert(
              calendarId='primary',
              body=event
          ).execute()

          print(f"\n✅ 일정이 추가되었습니다: {event_result.get('htmlLink')}")
      except Exception as e:
          print("❌ 일정 추가 중 오류 발생:", e)
  ################################################ 일정 추가

  ################################################ 일정 삭제

  delete_set = [
      {
          "list": '''[
              {"id": "1", "summary": "회의", "start": "2025-04-10T10:00:00+09:00", "end": "2025-04-10T11:00:00+09:00"},
              {"id": "2", "summary": "치과 예약", "start": "2025-04-11T15:00:00+09:00", "end": "2025-04-11T15:30:00+09:00"},
              {"id": "3", "summary": "친구와 저녁", "start": "2025-04-12T19:00:00+09:00", "end": "2025-04-12T21:00:00+09:00"}
          ]''',
          "input": "치과 일정 취소해줘",
          "output": '{"id": "2", "summary": "치과 예약", "start": "2025-04-11T15:00:00+09:00", "end": "2025-04-11T15:30:00+09:00"}'
      },
      {
          "list": '''[
              {"id": "4", "summary": "프로젝트 미팅", "start": "2025-04-14T14:00:00+09:00", "end": "2025-04-14T15:30:00+09:00"},
              {"id": "5", "summary": "헬스장", "start": "2025-04-14T18:00:00+09:00", "end": "2025-04-14T19:00:00+09:00"}
          ]''',
          "input": "오늘 헬스장 삭제해줘",
          "output": '{"id": "5", "summary": "헬스장", "start": "2025-04-14T18:00:00+09:00", "end": "2025-04-14T19:00:00+09:00"}'
      },
      {
          "list": '''[
              {"id": "6", "summary": "엄마 생신 파티", "start": "2025-04-16T17:00:00+09:00", "end": "2025-04-16T20:00:00+09:00"},
              {"id": "7", "summary": "개발자 밋업", "start": "2025-04-17T13:00:00+09:00", "end": "2025-04-17T15:00:00+09:00"}
          ]''',
          "input": "개발자 모임 일정 지워줘",
          "output": '{"id": "7", "summary": "개발자 밋업", "start": "2025-04-17T13:00:00+09:00", "end": "2025-04-17T15:00:00+09:00"}'
      },
      {
          "list": '''[
              {"id": "8", "summary": "출장 - 부산", "start": "2025-04-20T07:00:00+09:00", "end": "2025-04-20T20:00:00+09:00"},
              {"id": "9", "summary": "가족 여행", "start": "2025-04-21T08:00:00+09:00", "end": "2025-04-23T20:00:00+09:00"}
          ]''',
          "input": "부산 출장 취소해줘",
          "output": '{"id": "8", "summary": "출장 - 부산", "start": "2025-04-20T07:00:00+09:00", "end": "2025-04-20T20:00:00+09:00"}'
      },
      {
          "list": '''[
              {"id": "10", "summary": "스터디 모임", "start": "2025-04-22T10:00:00+09:00", "end": "2025-04-22T12:00:00+09:00"},
              {"id": "11", "summary": "점심약속", "start": "2025-04-22T12:30:00+09:00", "end": "2025-04-22T14:00:00+09:00"}
          ]''',
          "input": "22일 점심약속 빼줘",
          "output": '{"id": "11", "summary": "점심약속", "start": "2025-04-22T12:30:00+09:00", "end": "2025-04-22T14:00:00+09:00"}'
      }
  ]


  def delete_event(user_input):
      creds = get_credentials()
      service = build('calendar', 'v3', credentials=creds)

      # 일정 목록 가져오기
      events_result = service.events().list(
          calendarId='primary',
          maxResults=2500,
          singleEvents=True,
          orderBy='startTime'
      ).execute()
      events = events_result.get('items', [])

      if not events:
          print("📭 등록된 일정이 없습니다.")
          return

      # 요약용 이벤트 리스트 (AI가 분석할 수 있도록 최소 정보만 포함)
      simplified_events = [
          {
              "id": event["id"],
              "summary": event.get("summary", "(제목 없음)"),
              "start": event['start'].get('dateTime', event['start'].get('date')),
              "end": event['end'].get('dateTime', event['end'].get('date'))
          }
          for event in events
      ]

      print("simplified_events : \n\n\n",simplified_events)

      # AI에게 삭제할 일정 찾도록 요청
      system_prompt = f"""
      1. I'll give you a list
      2. I will provide input
      3. Please answer as in the following example : {delete_set}
      4. only You just need to create that "output": '...'

      list : {simplified_events}
      User input: {user_input}

      """

      response = requests.post(
          "http://localhost:11434/api/generate",
          json={
              "model": "exaone3.5:2.4b",
              "prompt": system_prompt,
              "stream": False
          }
      )

      result = response.json()
      print("result!!!!!",result["response"])

      # # 응답에서 JSON 추출
      # try:
      #     selected_event = json.loads(result["response"])
      #     print("selected_eventselected_eventselected_event",selected_event)
      #     if not selected_event or "id" not in selected_event:
      #         print("❌ 삭제할 일정을 정확히 찾을 수 없습니다.")
      #         return

      #     # 일정 삭제
      #     service.events().delete(calendarId='primary', eventId=selected_event["id"]).execute()
      #     print(f"🗑️ 삭제 완료: {selected_event['summary']}")

      # except Exception as e:
      #     print("❌ 응답 처리 중 오류:", e)
      #     print("응답 내용:", result["response"])

  ################################################ 일정 삭제

  ################################################ 일정 수정
  def edit_event(user_input):
      print("일정 수정")
      system_prompt = ""
      user_prompt = ""
      final_prompt = f"{system_prompt}\n\n User : {user_prompt}"

      response = requests.post(
          "http://localhost:11434/api/generate",
          json={
              "model": "exaone3.5:2.4b",
              "prompt": system_prompt,
              "stream": False
          }
      )

      return 

  ################################################ 일정 수정

  # 실행 진입전
  if __name__ == '__main__':

      user_input = input("🗓️ 일정을 입력하세요: ")

      classification = "re"
      while classification == "re" : 
          classification = Input_analysis(user_input)
          print("classification",classification)
          classification = extract_output(classification)


      if classification == "add" :
          print("일정 추가")
          ## 이벤트 생성
          make_event = MakeSchedule(user_input)
          print("make_event!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",make_event)
          add_event( make_event )
      elif classification == "edit" : 
          print("일정 수정")
          edit_event(user_input)    
      elif classification == "delete" : 
          print("일정 삭제")
          ## 모델 이슈
          delete_event(user_input)
      else : 
          print('알 수 없는 명령입니다.')
profile
뭐가될지 모름

0개의 댓글