diff --git a/connection.sample.py b/connection.sample.py index fe41a36..3911e64 100644 --- a/connection.sample.py +++ b/connection.sample.py @@ -1,7 +1,8 @@ # 各変数に適切な値を設定し,ファイル名を connection.py として保存すること -OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX' \ - # Organisation ID -OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' \ - # API Key +# Organisation ID +OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX' + +# API Key +OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' diff --git a/main.py b/main.py index e17fd3a..2833e38 100644 --- a/main.py +++ b/main.py @@ -22,53 +22,90 @@ class Main: def main (cls, goatoh_mode: bool = False) -> None: print (goatoh_mode) + # ウィンドゥの初期化 pygame.init () screen: pygame.Surface = pygame.display.set_mode ((1024, 768)) + + # 吹き出し balloon = pygame.transform.scale (pygame.image.load ('talking.png'), (1024, 384)) if goatoh_mode: balloon = pygame.transform.flip (balloon, False, True) + + # 音声再生器の初期化 pygame.mixer.init (frequency = 44100) + + # ニジカの “ぬ゛ぅ゛ぅ゛ぅ゛ん゛” noon = pygame.mixer.Sound ('noon.wav') + + # ゴートうの “ムムムム” mumumumu = pygame.mixer.Sound ('mumumumu.wav') + + # ゴートうの “クサタベテル!!” kusa = pygame.mixer.Sound ('kusa.wav') + # YouTube Chat オブジェクト live_chat = pytchat.create (video_id = YOUTUBE_ID) + # デバッグ・メシジのフォント system_font = pygame.font.SysFont ('notosanscjkjp', 24, bold = True) + + # 視聴者コメントのフォント user_font = pygame.font.SysFont ('notosanscjkjp', 32, italic = True) + + # ニジカのフォント nizika_font = pygame.font.SysFont ('07nikumarufont', 50) + # Youtube Chat から取得したコメントたち chat_items: list = [] + + # 会話の履歴(3 件分保持) histories: list = [] while (True): screen.fill ((0, 255, 0)) + # 左上に時刻表示 for i in range (4): screen.blit ( system_font.render (str (datetime.now ()), True, (0, 0, 0)), (i % 2, i // 2 * 2)) if live_chat.is_alive (): + # Chat オブジェクトが有効 + + # Chat 取得 chat_items: list = live_chat.get ().items if chat_items: + # 溜まってゐる Chat からランダムに 1 つ抽出 chat_item = random.choice (chat_items) + + # 投稿者情報を辞書化 chat_item.author = chat_item.author.__dict__ + + # 絵文字を復元 chat_item.message = emoji.emojize (chat_item.message) + message: str = chat_item.message + + # ChatGPT API を呼出し,返答を取得 answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_mode).replace ('\n', ' ') + + # 履歴に追加 histories = (histories + [{'role': 'user', 'content': message}, {'role': 'assistant', 'content': answer}])[(-6):] + # ログ書込み with open ('log.txt', 'a') as f: f.write (f'{datetime.now ()}\t{json.dumps (chat_item.__dict__)}\t{answer}\n') + # 吹出し描画(ニジカは上,ゴートうは下) screen.blit (balloon, (0, 384) if goatoh_mode else (0, 0)) + # 視聴者コメント描画 screen.blit ( user_font.render ( '> ' + (message @@ -80,6 +117,8 @@ class Main: True, (0, 0, 0)), (120, 70 + 384) if goatoh_mode else (120, 70)) + + # ニジカの返答描画 screen.blit ( nizika_font.render ( (answer @@ -102,6 +141,7 @@ class Main: pygame.display.update () + # 鳴く. if goatoh_mode: if random.random () < 0.1: kusa.play () @@ -112,10 +152,13 @@ class Main: time.sleep (1.5) + # 返答の読上げを WAV ディタとして生成,取得 try: wav: bytearray | None = Aques.main (answer, goatoh_mode) except: wav: None = None + + # 再生 if wav is not None: with open ('./nizika_talking.wav', 'wb') as f: f.write (wav) @@ -124,6 +167,9 @@ class Main: time.sleep (10) else: + # Chat オブジェクトが無効 + + # 再生成 live_chat = pytchat.create (video_id = YOUTUBE_ID) pygame.display.update () diff --git a/talk.py b/talk.py index a8b17af..57da221 100644 --- a/talk.py +++ b/talk.py @@ -8,26 +8,39 @@ from connection import * class Talk: + # ChatGPT API 連携失敗時に返答として出力するダミー文字列 DUMMY_RESPONSE: str = 'あいうえおかきくけこさしすせそたちつてとなにぬねの' + # 最高トークン数(もぅ少し下げてもいぃかも) max_tokens_count: int = 100 + + # 返答パターン数(1 個返せばじふぶんなので 1) responses_count: int = 1 + + # 返答のオリジナリティ(大きいほど独創性の高ぃ返答をよこしてくれる) temperature: float = .7 - top_p: int = 1 + + # バリエーションの多さ(0. -- 1.) + top_p: float = 1. @classmethod def main (cls, message: str, name: str | None = None, histories: list = [], goatoh_mode: bool = False) -> str: + # ChatGPT API Organisation ID openai.organization = OPENAI_ORGANISATION + + # ChatGPT API Key openai.api_key = OPENAI_API_KEY + # ChatGPT API にクエリを投げ,返答を取得する. answer: chat.chat_completion_message.ChatCompletionMessage | None \ = cls.__get_message (message, name, histories, goatoh_mode) - return cls.DUMMY_RESPONSE if answer is None else answer.content + return answer.content if answer is not None else cls.DUMMY_RESPONSE @classmethod def __get_message (cls, message: str, name: str | None, histories: list, goatoh_mode: bool = False) \ -> chat.chat_completion_message.ChatCompletionMessage | None: + # プロンプト(JSON 等外部ファイルに置くことを検討) if goatoh_mode: messages: list = [{'role': 'system', 'content': ('回答は短く30文字程度で完結に。' @@ -185,7 +198,7 @@ class Talk: 'content': '洗操歌(しーざおぐあ)歌って'}, {'role': 'assistant', - 'content': ('おけだぬ゛~゛ん゛(失笑)。' + 'content': ('おけだぬ゛~゛ん゛(苦笑)。' + '毛巾浴帽小鸭鸭水温刚刚好♪' + '泼泼水来搓泡泡今天真是美妙♪' + '大声唱歌扭扭腰我爱洗洗澡♪'