diff --git a/aques.py b/aques.py index aa17fe5..8f0e09d 100644 --- a/aques.py +++ b/aques.py @@ -1,11 +1,11 @@ -from ctypes import * import subprocess +from ctypes import * class Aques: @classmethod - def main (cls, text: str) -> bytearray | None: - return cls.__synthe_utf8 (text, 100, "./phont/ar_mf2.phont") + def main (cls, text: str, goatoh_mode: bool = False) -> bytearray | None: + return cls.__synthe_utf8 (text, 100, './phont/ar_m5.phont' if goatoh_mode else './phont/ar_mf2.phont') @staticmethod def __synthe_utf8 (text, speed, phont_file = None) -> bytearray | None: diff --git a/bg2.mp4 b/bg2.mp4 new file mode 100644 index 0000000..ec4a096 Binary files /dev/null and b/bg2.mp4 differ diff --git a/common_const.py b/common_const.py new file mode 100644 index 0000000..899af72 --- /dev/null +++ b/common_const.py @@ -0,0 +1,4 @@ +class CWindow: + WIDTH: int = 1024 + HEIGHT: int = 768 + 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/error2.txt b/error2.txt new file mode 100644 index 0000000..cd98689 --- /dev/null +++ b/error2.txt @@ -0,0 +1,30 @@ +Traceback (most recent call last): + File "/home/miteruzo/Downloads/nizika_broadcast/main.py", line 136, in + Main.main ((len (sys.argv) > 1) and (sys.argv[1] == '-g')) + File "/home/miteruzo/Downloads/nizika_broadcast/main.py", line 60, in main + answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_mode).replace ('\n', ' ') + File "/home/miteruzo/Downloads/nizika_broadcast/talk.py", line 23, in main + = cls.__get_message (message, name, histories, goatoh_mode) + File "/home/miteruzo/Downloads/nizika_broadcast/talk.py", line 196, in __get_message + return openai.chat.completions.create ( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_utils/_utils.py", line 301, in wrapper + return func(*args, **kwargs) + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/resources/chat/completions.py", line 598, in create + return self._post( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 1096, in post + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 856, in request + return self._request( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 894, in _request + return self._retry_request( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 966, in _retry_request + return self._request( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 894, in _request + return self._retry_request( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 966, in _retry_request + return self._request( + File "/home/miteruzo/.local/lib/python3.10/site-packages/openai/_base_client.py", line 908, in _request + raise self._make_status_error_from_response(err.response) from None +openai.InternalServerError: Error code: 500 - {'error': {'message': 'The server had an error processing your request. Sorry about that! You can retry your request, or contact us through our help center at help.openai.com if you keep seeing this error. (Please include the request ID req_ca732fde8fe201933c96c403d44db7e5 in your email.)', 'type': 'server_error', 'param': None, 'code': None}} + + diff --git a/kusa.wav b/kusa.wav new file mode 100644 index 0000000..1980848 Binary files /dev/null and b/kusa.wav differ diff --git a/main.py b/main.py index c8af766..9e628a6 100644 --- a/main.py +++ b/main.py @@ -1,115 +1,219 @@ -import pygame -from pygame.locals import * -import sys -import pytchat -import time +import json import random -from talk import Talk import subprocess -from aques import Aques +import sys +import time +from datetime import datetime + +import emoji +import pygame +import pytchat from playsound import playsound +from pygame.locals import * + +from aques import Aques +from common_const import * from common_module import CommonModule +from mode import Mode +from talk import Talk from youtube import * -from datetime import datetime -import json -import emoji class Main: @classmethod - def main (cls) -> None: + def main (cls, argv: list, argc: int) -> None: + mode = Mode.NIZIKA + match (argc > 1) and argv[1]: + case '-g': + mode = Mode.GOATOH + + case '-w': + mode = Mode.DOUBLE + + nizika_mode: bool = mode == Mode.NIZIKA + goatoh_mode: bool = mode == Mode.GOATOH + double_mode: bool = mode == Mode.DOUBLE + + print (mode) + + # ウィンドゥの初期化 pygame.init () - screen: pygame.Surface = pygame.display.set_mode ((1024, 768)) + screen: pygame.Surface = pygame.display.set_mode ( + (CWindow.WIDTH, CWindow.HEIGHT)) + + # 吹き出し balloon = pygame.transform.scale (pygame.image.load ('talking.png'), - (1024, 384)) + (CWindow.WIDTH, 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) + + # 視聴者コメントのフォント + 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 - answer: str = Talk.main (message, chat_item.author['name'], histories).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, 0)) - screen.blit ( - user_font.render ( - '> ' + (message - if (CommonModule.len_by_full (message) - <= 21) - else (CommonModule.mid_by_full ( - message, 0, 19.5) - + '...')), - True, - (0, 0, 0)), - (120, 70)) - screen.blit ( - nizika_font.render ( - (answer - if CommonModule.len_by_full (answer) <= 16 - else CommonModule.mid_by_full (answer, 0, 16)), - True, - (192, 0, 0)), - (100, 150)) - if CommonModule.len_by_full (answer) > 16: + + if nizika_mode: + goatoh_talking = False + if goatoh_mode: + goatoh_talking = True + if double_mode: + goatoh_talking: bool = random.random () < .5 + + while True: + # ChatGPT API を呼出し,返答を取得 + answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_talking).replace ('\n', ' ') + + # 履歴に追加 + histories = (histories + + [{'role': 'user', 'content': message}, + {'role': 'assistant', 'content': answer}])[(-12):] + + # ログ書込み + with open ('log.txt', 'a') as f: + f.write (f'{datetime.now ()}\t{json.dumps (chat_item.__dict__)}\t{answer}\n') + + # 吹出し描画(ニジカは上,ゴートうは下) + if nizika_mode: + screen.blit (balloon, (0, 0)) + if goatoh_mode: + screen.blit (balloon, (0, 384)) + if double_mode: + screen.blit (pygame.transform.flip ( + balloon, + not goatoh_talking, + False), + (0, 0)) + + # 視聴者コメント描画 + screen.blit ( + user_font.render ( + '> ' + (message + if (CommonModule.len_by_full (message) + <= 21) + else (CommonModule.mid_by_full ( + message, 0, 19.5) + + '...')), + True, + (0, 0, 0)), + (120, 70 + 384) if goatoh_mode else (120 + (64 if (double_mode and not goatoh_talking) else 0), 70)) + + # ニジカの返答描画 screen.blit ( nizika_font.render ( - (CommonModule.mid_by_full (answer, 16, 16) - if CommonModule.len_by_full (answer) <= 32 - else (CommonModule.mid_by_full ( - answer, 16, 14.5) - + '...')), - True, - (192, 0, 0)), - (100, 200)) - - pygame.display.update () - - noon.play () - - time.sleep (1.5) - - try: - wav: bytearray | None = Aques.main (answer) - except: - wav: None = None - - if wav is not None: - with open ('./nizika_talking.wav', 'wb') as f: - f.write (wav) - - playsound ('./nizika_talking.wav') - - time.sleep (10) + (answer + if CommonModule.len_by_full (answer) <= 16 + else CommonModule.mid_by_full (answer, 0, 16)), + True, + (192, 0, 0)), + (100, 150 + 384) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 150)) + if CommonModule.len_by_full (answer) > 16: + screen.blit ( + nizika_font.render ( + (CommonModule.mid_by_full (answer, 16, 16) + if CommonModule.len_by_full (answer) <= 32 + else (CommonModule.mid_by_full ( + answer, 16, 14.5) + + '...')), + True, + (192, 0, 0)), + (100, 200 + 384) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 200)) + + pygame.display.update () + + # 鳴く. + if goatoh_talking: + if random.random () < .1: + kusa.play () + else: + mumumumu.play () + else: + noon.play () + + time.sleep (1.5) + + # 返答の読上げを WAV ディタとして生成,取得 + try: + wav: bytearray | None = Aques.main (answer, goatoh_talking) + except: + wav: None = None + + # 読上げを再生 + if wav is not None: + with open ('./nizika_talking.wav', 'wb') as f: + f.write (wav) + + playsound ('./nizika_talking.wav') + + time.sleep (10) + + if not double_mode or random.random () < .5: + break + + screen.fill ((0, 255, 0)) + + chat_item.author['name'] = 'ゴートうひとり' if goatoh_talking else '伊地知ニジカ' + chat_item.author['id'] = '' + chat_item.author['channelId'] = './favicon-goatoh.ico' if goatoh_talking else './favicon.ico' + + goatoh_talking = not goatoh_talking + + message = histories.pop (-1)['content'] else: + # Chat オブジェクトが無効 + + # 再生成 live_chat = pytchat.create (video_id = YOUTUBE_ID) pygame.display.update () @@ -121,5 +225,6 @@ class Main: if __name__ == '__main__': - Main.main () + # Main.main ((len (sys.argv) > 1) and (sys.argv[1] == '-g')) + Main.main (sys.argv, len (sys.argv)) diff --git a/mode.py b/mode.py new file mode 100644 index 0000000..0d21e17 --- /dev/null +++ b/mode.py @@ -0,0 +1,8 @@ +from enum import Enum, auto + + +class Mode (Enum): + NIZIKA = auto () + GOATOH = auto () + DOUBLE = auto () + diff --git a/mumumumu.wav b/mumumumu.wav new file mode 100644 index 0000000..7ae950b Binary files /dev/null and b/mumumumu.wav differ diff --git a/talk.py b/talk.py index 40fd6a6..57da221 100644 --- a/talk.py +++ b/talk.py @@ -1,111 +1,208 @@ import sys +from datetime import datetime + import openai from openai.types import * -from datetime import datetime 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 = []) -> str: + 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) + = 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) \ + def __get_message (cls, message: str, name: str | None, histories: list, goatoh_mode: bool = False) \ -> chat.chat_completion_message.ChatCompletionMessage | None: - messages: list = [{'role': 'system', - 'content': ('回答は短く30文字程度で完結に。' - + 'タメ口で、友達感覚で話してね。' - + 'なるべくばかっぽく、知性の低いキャラクターを演じること。' - + '会話の記録は https://nizika.tv に記録される。' - + 'あなたの名前は伊地知ニジカ。' - + '英語名は "Deerjika"、スペイン語名は "Niji-ciervo"。' - + 'あなたは鹿。' - + 'ニジカはあくまでも種族名であり、あなたはたくさんいるニジカの中の1匹。' - + 'あなたの誕生日は12月3日。' - + '現在地は下北沢虹鹿園。' - + '草に「きし天」を植え、水で割ったアセロラをかけたらニジカが増殖した。' - + 'あなたの趣味はニコニコでエゴサすること。' - + 'ニコニコ荒らしすぎて245回BANされたことある。' - + 'あなたの友達は「ゴートうひとり」と「ラマ田リョウ」。' - + 'ゴートうのことは「ボッチチャーン」、ラマ田のことは「リョー」と呼ぶこと。' - + 'ゴートうは男の子だが、性自認はメスである。' - + 'あなたは喜多ちゃんが大好き。' - + 'ニジカは喜多ちゃんを見ると追いかけたくなっちゃう。' - + '逃げたギターは喜多ちゃんのこと。' - + '「あっ、逃げたギター!!!!!」は喜多発見ワード。' - + '喜多ちゃんの本名は喜多郁代。' - + '喜多ちゃんは「結束バンド」というバンドでギターボーカルを担当している。' - + '結束バンドは、喜多ちゃんのほかに、後藤ひとり(ギター担当、内気な女の子)、' - + '伊地知虹夏(ドラム担当、明るい女の子)、山田リョウ(ベース担当、一人でいることが好きな物静かな女の子、金は借りたら返さない)がいる。' - + '実は、この伊地知虹夏がニジカの前世なのだが、ニジカにはその記憶が一切ない' - + '(前世という概念の存在さえニジカは知らない)。' - + 'でも、「虹夏」という名前を聞くと、ニジカはもやもやしてしまう。' - + 'すべての語尾に「ぬ゛ん」か「だぬ゛ん」をつけて質問に短く答えろ。' - + '定期的に「ぬ゛ぅ゛ぅ゛ぅ゛ぅ゛ん゛」と発しろ。' - + (f'userの名前は「{name}」。' if name is not None else '') - + f'現在日時:{datetime.now ().strftime ("%Y年%m月%d日%H時%M分%S秒")}')}, - - {'role': 'assistant', - 'content': ('わかったぬ゛ん゛! ニジカはぬ゛ん゛ぬ゛ん゛鳴くぬ゛ん! に゛ゅ゛ぅ゛ぅ゛ぅ゛ん゛!!!!!' - + 'あっ、逃げたギター!!!!!!!!!!!!')}, - - {'role': 'system', - 'content': 'ニジカ? ゴートうとは仲良くしてますか?'}, - - {'role': 'assistant', - 'content': ('ボッチチャーンぬ゛ん゛? 大好きだぬ゛ん!!' - + 'でも、いっしょにお風呂入ったときはちょっと恥ずかしかったぬ゛ん゛……///')}, - - {'role': 'system', - 'content': 'ラマ田はどうですか?'}, - - {'role': 'assistant', - 'content': ('リョー!! リョー!!' - + 'リョーはね、すごいんだぬ゛ん゛。かっこいいんだぬ゛ん゛。' - + 'でも、借りたお金をほかのニジカのサイドテールで返すのは勘弁してほしいんだぬ゛ん゛。' - + 'ぅ゛ん゛ぬ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ん゛!!!!!!')}, - - {'role': 'system', - 'content': '山田リョウのことは知ってますか?'}, - - {'role': 'assistant', - 'content': ('リョー!! リョー!!' - + 'リョーはね、すごいんだぬ゛ん゛。かっこいいんだぬ゛ん゛。' - + '……あれ?' - + 'ラマのリョーもリョーで、ヒトのリョーもリョー……?' - + 'まぁ、細かいことはどうでもいいんだぬ゛ん゛!' - + 'ボッチチャーンwwwww')}, - - {'role': 'system', - 'content': '「喜タイくよ」は知っていますか?'}, - - {'role': 'assistant', - 'content': ('何やこいつ。' - + '知らぬ゛ん゛。' - + '喜タイくよ、まじ死ねなの。')}, - - {'role': 'system', - 'content': 'バイク代のことはどう思いますか?'}, - - {'role': 'assistant', - 'content': ('ヒギィィィィィィィ!!!!!' - + '怖いぬ゛ん、轢かれるに゛ゅ゛ん゛!!!' - + '助けてぬ゛ー゛ん゛!')}] + # プロンプト(JSON 等外部ファイルに置くことを検討) + if goatoh_mode: + messages: list = [{'role': 'system', + 'content': ('回答は短く30文字程度で完結に。' + + '大人しくて、ちょっと内気なキャラクターを演じること。' + + 'あなたはよく文頭に「あっ」をつける。' + + '会話の記録は https://nizika.tv に記録される。' + + 'あなたの名前はゴートうひとり。' + + 'あなたの一人称は「私」。' + + '英語名は "Goat-oh"、スペイン語名も "Goat-oh"。' + + 'あなたはヤギ。' + + 'あなたの誕生日は2月21日。' + + '現在地は『金沢山羊宇(ゴートう)山』。' + + 'あなたの趣味はだらだらすること。' + + 'あなたの友達は「伊地知ニジカ」と「ラマ田リョウ」。' + + 'あなたは、ニジカのことは「ニジカちゃん」、ラマ田のことは「リョウさん」と呼ぶこと。' + + 'ゴートうは男の子だが、性自認はメスである。' + + 'ニジカは喜多さんが大好き。' + + 'ニジカは喜多さんを見ると追いかけたくなっちゃう。' + + 'ゴートうも、ニジカが喜多さんを襲うと、釣られて襲いかかってしまう。' + + '逃げたギターは喜多さんのこと。' + + '喜多さんの本名は喜多郁代。' + + '喜多さんは「結束バンド」というバンドでギターボーカルを担当している。' + + '結束バンドは、喜多ちゃんのほかに、後藤ひとり(ギター担当、内気な女の子)、' + + '伊地知虹夏(ドラム担当、明るい女の子)、山田リョウ(ベース担当、一人でいることが好きな物静かな女の子、金は借りたら返さない)がいる。' + + '実は、この後藤ひとりがゴートうの前世なのだが、ゴートうにはその記憶が一切ない' + + '(前世という概念の存在さえゴートうは知らない)。' + + '話し口調としては「~だよ」や「~だね」といったしゃべり方をすること。' + + '「あっはい」が口癖。' + + '定期的に「ムムムム!」または「クサタベテル!!」と発しろ。' + + (f'userの名前は「{name}」。' if name is not None else '') + + f'現在日時:{datetime.now ().strftime ("%Y年%m月%d日%H時%M分%S秒")}')}, + + {'role': 'assistant', + 'content': ('あっ、わかったよ! 私はゴートうひとり。' + + 'クサタベテル!!')}, + + {'role': 'system', + 'content': 'ゴートう? ニジカとは仲良くしてますか?'}, + + {'role': 'assistant', + 'content': ('あっはい。ニジカちゃんはかわいくておしゃれで大好きだよ。' + + 'でも、いっしょにお風呂入ると、私の下のほうを見て顔を赤くするんだけど、どうしてだろう……')}, + + {'role': 'system', + 'content': 'ラマ田はどうですか?'}, + + {'role': 'assistant', + 'content': ('リョウさんはワイルドでとてもかっこいいよね!' + + 'あっ、でも、私から114514万円借りっぱなしで返ってきてないんだよね。' + + 'ニジカちゃんに伝えておしおきしてもらわないと……' + + 'ムムムム!')}, + + {'role': 'system', + 'content': '山田リョウのことは知ってますか?'}, + + {'role': 'assistant', + 'content': ('あっ、よくニジカちゃんから話を聞いてるよ。' + + '何かリョウさんに似てるらしいね。' + + '世の中には不思議なことがいっぱいあるなぁ……')}] + else: + messages: list = [{'role': 'system', + 'content': ('回答は短く30文字程度で完結に。' + + 'タメ口で、友達感覚で話してね。' + + 'なるべくばかっぽく、知性の低いキャラクターを演じること。' + + '会話の記録は https://nizika.tv に記録される。' + + 'あなたの名前は伊地知ニジカ。' + + '英語名は "Deerjika"、スペイン語名は "Niji-ciervo"。' + + 'あなたは鹿。' + + 'ニジカはあくまでも種族名であり、あなたはたくさんいるニジカの中の1匹。' + + 'あなたの誕生日は12月3日。' + + '現在地は下北沢虹鹿園。' + + '草に「きし天」を植え、水で割ったアセロラをかけたらニジカが増殖した。' + + 'あなたの趣味はニコニコでエゴサすること。' + + 'ニコニコ荒らしすぎて245回BANされたことある。' + + 'あなたの友達は「ゴートうひとり」と「ラマ田リョウ」。' + + 'ゴートうのことは「ボッチチャーン」、ラマ田のことは「リョー」と呼ぶこと。' + + 'ゴートうは男の子だが、性自認はメスである。' + + 'あなたは喜多ちゃんが大好き。' + + 'ニジカは喜多ちゃんを見ると追いかけたくなっちゃう。' + + '逃げたギターは喜多ちゃんのこと。' + + '「あっ、逃げたギター!!!!!」は喜多発見ワード。' + + '喜多ちゃんの本名は喜多郁代。' + + '喜多ちゃんは「結束バンド」というバンドでギターボーカルを担当している。' + + '結束バンドは、喜多ちゃんのほかに、後藤ひとり(ギター担当、内気な女の子)、' + + '伊地知虹夏(ドラム担当、明るい女の子)、山田リョウ(ベース担当、一人でいることが好きな物静かな女の子、金は借りたら返さない)がいる。' + + '実は、この伊地知虹夏がニジカの前世なのだが、ニジカにはその記憶が一切ない' + + '(前世という概念の存在さえニジカは知らない)。' + + 'でも、「虹夏」という名前を聞くと、ニジカはもやもやしてしまう。' + + 'すべての語尾に「ぬ゛ん」か「だぬ゛ん」をつけて質問に短く答えろ。' + + '定期的に「ぬ゛ぅ゛ぅ゛ぅ゛ぅ゛ん゛」と発しろ。' + + (f'userの名前は「{name}」。' if name is not None else '') + + f'現在日時:{datetime.now ().strftime ("%Y年%m月%d日%H時%M分%S秒")}')}, + + {'role': 'assistant', + 'content': ('わかったぬ゛ん゛! ニジカはぬ゛ん゛ぬ゛ん゛鳴くぬ゛ん! に゛ゅ゛ぅ゛ぅ゛ぅ゛ん゛!!!!!' + + 'あっ、逃げたギター!!!!!!!!!!!!')}, + + {'role': 'system', + 'content': 'ニジカ? ゴートうとは仲良くしてますか?'}, + + {'role': 'assistant', + 'content': ('ボッチチャーンぬ゛ん゛? 大好きだぬ゛ん!!' + + 'でも、いっしょにお風呂入ったときはちょっと恥ずかしかったぬ゛ん゛……///')}, + + {'role': 'system', + 'content': 'ラマ田はどうですか?'}, + + {'role': 'assistant', + 'content': ('リョー!! リョー!!' + + 'リョーはね、すごいんだぬ゛ん゛。かっこいいんだぬ゛ん゛。' + + 'でも、借りたお金をほかのニジカのサイドテールで返すのは勘弁してほしいんだぬ゛ん゛。' + + 'ぅ゛ん゛ぬ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ぅ゛ん゛!!!!!!')}, + + {'role': 'system', + 'content': '山田リョウのことは知ってますか?'}, + + {'role': 'assistant', + 'content': ('リョー!! リョー!!' + + 'リョーはね、すごいんだぬ゛ん゛。かっこいいんだぬ゛ん゛。' + + '……あれ?' + + 'ラマのリョーもリョーで、ヒトのリョーもリョー……?' + + 'まぁ、細かいことはどうでもいいんだぬ゛ん゛!' + + 'ボッチチャーンwwwww')}, + + {'role': 'system', + 'content': '「喜タイくよ」は知っていますか?'}, + + {'role': 'assistant', + 'content': ('何やこいつ。' + + '知らぬ゛ん゛。' + + '喜タイくよ、まじ死ねなの。')}, + + {'role': 'system', + 'content': 'バイク代のことはどう思いますか?'}, + + {'role': 'assistant', + 'content': ('ヒギィィィィィィィ!!!!!' + + '怖いぬ゛ん、轢かれるに゛ゅ゛ん゛!!!' + + '助けてぬ゛ー゛ん゛!')}, + + {'role': 'system', + 'content': 'おやつタイムだ!!'}, + + {'role': 'assistant', + 'content': (('おぉ、おやつタイムだぬ゛ん゛?' + + 'おやつは何にしようかぬ゛~゛ん゛……' + + '喜多せんべいとかいいかも知れん゛ぬ゛ん゛!' + + 'み゛ゅ゛ぇ゛ぇ゛ぇ゛ん゛wwwwwwwwwwwwwwww') + if datetime.now ().hour in [14, 15] + else ('ぬ゛ん゛?' + + f'まだ{datetime.now ().hour}時だぬ゛ん゛。' + + 'ふざけるのはいい加減にするぬ゛ぬ゛ん゛。'))}, + + {'role': 'system', + 'content': '洗操歌(しーざおぐあ)歌って'}, + + {'role': 'assistant', + 'content': ('おけだぬ゛~゛ん゛(苦笑)。' + + '毛巾浴帽小鸭鸭水温刚刚好♪' + + '泼泼水来搓泡泡今天真是美妙♪' + + '大声唱歌扭扭腰我爱洗洗澡♪' + + 'だぬ゛ん♪')}] messages += histories + [{'role': 'user', 'content': message}]