Browse Source

Merge branch 'main' into ai-migration

ai-migration
みてるぞ 1 month ago
parent
commit
a4ec55a4cd
11 changed files with 463 additions and 189 deletions
  1. +0
    -0
      __init__.py
  2. +2
    -1
      aques.py
  3. +7
    -0
      connection.sample.py
  4. +0
    -82
      error.txt
  5. +0
    -30
      error2.txt
  6. +130
    -76
      main.py
  7. BIN
      oh.wav
  8. +44
    -0
      play_movie.py
  9. BIN
      snack_time.mp4
  10. BIN
      snack_time.wav
  11. +280
    -0
      talk.py

+ 0
- 0
__init__.py View File


+ 2
- 1
aques.py View File

@@ -1,5 +1,6 @@
import subprocess
from ctypes import *
from ctypes import ARRAY # type: ignore
from ctypes import POINTER, byref, c_int, c_ubyte, cast, cdll


class Aques:


+ 7
- 0
connection.sample.py View File

@@ -0,0 +1,7 @@
# 各変数に適切な値を設定し,ファイル名を connection.py として保存すること

# Organisation ID
OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX'

# API Key
OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

+ 0
- 82
error.txt View File

@@ -1,82 +0,0 @@
Traceback (most recent call last):
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_exceptions.py", line 10, in map_exceptions
yield
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_backends/sync.py", line 126, in read
return self._sock.recv(max_bytes)
File "/usr/lib/python3.10/ssl.py", line 1288, in recv
return self.read(buflen)
File "/usr/lib/python3.10/ssl.py", line 1161, in read
return self._sslobj.read(len)
TimeoutError: The read operation timed out

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 66, in map_httpcore_exceptions
yield
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 228, in handle_request
resp = self._pool.handle_request(req)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 268, in handle_request
raise exc
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 251, in handle_request
response = connection.handle_request(request)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/connection.py", line 103, in handle_request
return self._connection.handle_request(request)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 185, in handle_request
raise exc
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 148, in handle_request
status, headers = self._receive_response(
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 292, in _receive_response
event = self._receive_stream_event(request, stream_id)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 333, in _receive_stream_event
self._receive_events(request, stream_id)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 361, in _receive_events
events = self._read_incoming_data(request)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 452, in _read_incoming_data
raise exc
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_sync/http2.py", line 438, in _read_incoming_data
data = self._network_stream.read(self.READ_NUM_BYTES, timeout)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_backends/sync.py", line 124, in read
with map_exceptions(exc_map):
File "/usr/lib/python3.10/contextlib.py", line 153, in __exit__
self.gen.throw(typ, value, traceback)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpcore/_exceptions.py", line 14, in map_exceptions
raise to_exc(exc) from exc
httpcore.ReadTimeout: The read operation timed out

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/home/miteruzo/Downloads/nizika_broadcast/main.py", line 117, in <module>
Main.main ()
File "/home/miteruzo/Downloads/nizika_broadcast/main.py", line 106, in main
live_chat = pytchat.create (video_id = YOUTUBE_ID)
File "/home/miteruzo/.local/lib/python3.10/site-packages/pytchat/core/__init__.py", line 7, in create
return PytchatCore(_vid, **kwargs)
File "/home/miteruzo/.local/lib/python3.10/site-packages/pytchat/core/pytchat.py", line 96, in __init__
self._setup()
File "/home/miteruzo/.local/lib/python3.10/site-packages/pytchat/core/pytchat.py", line 106, in _setup
channel_id=util.get_channelid(self._client, self._video_id),
File "/home/miteruzo/.local/lib/python3.10/site-packages/pytchat/util/__init__.py", line 103, in get_channelid
resp = client.get("https://www.youtube.com/embed/{}".format(quote(video_id)), headers=config.headers)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 1041, in get
return self.request(
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 814, in request
return self.send(request, auth=auth, follow_redirects=follow_redirects)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 901, in send
response = self._send_handling_auth(
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 929, in _send_handling_auth
response = self._send_handling_redirects(
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 966, in _send_handling_redirects
response = self._send_single_request(request)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_client.py", line 1002, in _send_single_request
response = transport.handle_request(request)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 227, in handle_request
with map_httpcore_exceptions():
File "/usr/lib/python3.10/contextlib.py", line 153, in __exit__
self.gen.throw(typ, value, traceback)
File "/home/miteruzo/.local/lib/python3.10/site-packages/httpx/_transports/default.py", line 83, in map_httpcore_exceptions
raise mapped_exc(message) from exc
httpx.ReadTimeout: The read operation timed out



+ 0
- 30
error2.txt View File

@@ -1,30 +0,0 @@
Traceback (most recent call last):
File "/home/miteruzo/Downloads/nizika_broadcast/main.py", line 136, in <module>
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}}



+ 130
- 76
main.py View File

@@ -9,19 +9,20 @@ import time
from datetime import datetime, timedelta

import emoji
import ephem
import ephem # type: ignore
import pygame
import pygame.gfxdraw
import pytchat
import pytchat # type: ignore
from playsound import playsound
from pygame.locals import *
from youtube import *

import play_movie
from aques import Aques
from common_const import *
from common_module import CommonModule
from mode import Mode
from talk import Talk
from youtube import *


class Main:
@@ -39,7 +40,7 @@ class Main:
argv: list,
argc: int) \
-> None:
mode = Mode.NIZIKA
mode: Mode = Mode.NIZIKA
match (argc > 1) and argv[1]:
case '-g':
mode = Mode.GOATOH
@@ -108,6 +109,12 @@ class Main:
# ニジカの “ぬ゛ぅ゛ぅ゛ぅ゛ん゛”
noon = pygame.mixer.Sound ('noon.wav')

# “あっ!”
deerjika_oh = pygame.mixer.Sound ('oh.wav')

# おやつタイムのテーマ
snack_time_sound = pygame.mixer.Sound ('snack_time.wav')

# ゴートうの “ムムムム”
mumumumu = pygame.mixer.Sound ('mumumumu.wav')

@@ -132,9 +139,12 @@ class Main:
# 会話の履歴
histories: list = []

# おやつ記録
has_snack = False

while True:
# 観測地の日づけ更新
observer.date: datetime = datetime.now ().date ()
observer.date = datetime.now ().date ()

# 日の出開始
sunrise_start: datetime = (
@@ -189,7 +199,7 @@ class Main:
# Chat オブジェクトが有効

# Chat 取得
chat_items: list = live_chat.get ().items
chat_items = live_chat.get ().items

if chat_items:
# 溜まってゐる Chat からランダムに 1 つ抽出
@@ -208,7 +218,7 @@ class Main:
if goatoh_mode:
goatoh_talking = True
if double_mode:
goatoh_talking: bool = random.random () < .5
goatoh_talking = random.random () < .5

while True:
# ChatGPT API を呼出し,返答を取得
@@ -225,54 +235,9 @@ class Main:
+ f'{json.dumps (chat_item.__dict__)}\t'
+ f'{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 (
(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 ()
cls.draw_talking (screen, balloon, user_font, nizika_font,
message, answer, mode,
mode == 3 and goatoh_talking)

# 鳴く.
if goatoh_talking:
@@ -285,20 +250,7 @@ class Main:

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 (1)
cls.read_out (answer, goatoh_talking)

if not double_mode or random.random () < .5:
break
@@ -324,13 +276,115 @@ class Main:
# 再生成
live_chat = pytchat.create (video_id = YOUTUBE_ID)

if has_snack and datetime.now ().hour == 14:
has_snack = False

pygame.display.update ()

if (not has_snack) and datetime.now ().hour == 15:
has_snack = True
deerjika_oh.play ()
time.sleep (0.6)
snack_time_sound.play ()
play_movie.main (screen, 'snack_time.mp4')
query = 'おやつタイムだ!!!!'
cls.draw_bg (screen, bg_day, bg_evening, bg_night, bg_grass,
kita, jojoko,
sunrise_start, sunrise_end, sunset_start, sunset_end,
sun_alt, sun_az, moon_alt, moon_az, moon_days_old)
answer = Talk.main (query).replace ('\n', ' ')
cls.draw_talking (screen, balloon, user_font, nizika_font,
query, answer)
noon.play ()
time.sleep (1.5)
cls.read_out (answer)

for event in pygame.event.get ():
if event.type == QUIT:
pygame.quit ()
sys.exit ()

@staticmethod
def read_out (
answer: str,
goatoh: bool = False,
) -> None:
# 返答の読上げを WAV ディタとして生成,取得
wav: bytearray | None
try:
wav = Aques.main (answer, goatoh)
except:
wav = None

# 読上げを再生
if wav is not None:
with open ('./nizika_talking.wav', 'wb') as f:
f.write (wav)

playsound ('./nizika_talking.wav')

time.sleep (1)

@staticmethod
def draw_talking (
screen: pygame.Surface,
balloon: pygame.Surface,
user_font: pygame.font.Font,
nizika_font: pygame.font.Font,
query: str,
answer: str,
mode: Mode = Mode.NIZIKA,
flip: bool = False,
) -> None:
# 吹出し描画(ニジカは上,ゴートうは下)
nizika_mode = False
goatoh_mode = False
double_mode = False
match mode:
case Mode.NIZIKA:
screen.blit (balloon, (0, 0))
nizika_mode = True
case Mode.GOATOH:
screen.blit (balloon, (0, 384))
goatoh_mode = True
case Mode.DOUBLE:
screen.blit (pygame.transform.flip (balloon, flip, False), (0, 0))
double_mode = True

# 視聴者コメント描画
screen.blit (
user_font.render (
('> ' + (query
if (CommonModule.len_by_full (query) <= 21)
else (CommonModule.mid_by_full (query, 0, 19.5) + '...'))),
True, (0, 0, 0)),
((120, 70 + 384)
if goatoh_mode
else (120 + (64 if (double_mode and flip) else 0), 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 + 384) if goatoh_mode else (100 + (64 if (double_mode and flip) 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 flip) else 0), 200))

pygame.display.update ()

@classmethod
def draw_bg (
cls,
@@ -349,8 +403,8 @@ class Main:
sun_az: float,
moon_alt: float,
moon_az: float,
moon_days_old: float) \
-> None:
moon_days_old: float,
) -> None:
sunrise_centre: datetime = (
sunrise_start + (sunrise_end - sunrise_start) / 2)
sunset_centre: datetime = (
@@ -379,16 +433,16 @@ class Main:
screen.blit (bg_night, (0, 0))

if sunrise_start <= dt < sunrise_end:
bg_evening.set_alpha (255 - ((abs (dt - sunrise_centre) * 510)
/ (sunrise_end - sunrise_centre)))
bg_evening.set_alpha (255 - int ((abs (dt - sunrise_centre) * 510)
/ (sunrise_end - sunrise_centre)))
elif sunset_start <= dt < sunset_end:
bg_evening.set_alpha (255 - ((abs (dt - sunset_centre) * 510)
/ (sunset_end - sunset_centre)))
bg_evening.set_alpha (255 - int ((abs (dt - sunset_centre) * 510)
/ (sunset_end - sunset_centre)))
else:
bg_evening.set_alpha (0)

if sunrise_start <= dt < sunset_end:
jojoko.set_alpha (255 - 255 / 15 * abs (moon_days_old - 15))
jojoko.set_alpha (255 - int (255 / 15 * abs (moon_days_old - 15)))
else:
jojoko.set_alpha (255)



BIN
oh.wav View File


+ 44
- 0
play_movie.py View File

@@ -0,0 +1,44 @@
import cv2
import pygame
import sys


def main (
screen: pygame.Surface,
video_path: str,
) -> None:
# OpenCV で動画を読み込む
cap = cv2.VideoCapture (video_path)
if not cap.isOpened ():
return

# screen の幅、高さ
(width, height) = screen.get_size ()

fps = cap.get (cv2.CAP_PROP_FPS)

clock = pygame.time.Clock ()

while cap.isOpened ():
# 動画のフレームを読み込む
(ret, frame) = cap.read ()
if not ret:
break

# OpenCV の BGR フォーマットを RGB に変換
frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)

# Numpy 配列を Pygame サーフェスに変換
frame_surface = pygame.surfarray.make_surface (frame)
frame_surface = pygame.transform.rotate (frame_surface, -90)
frame_surface = pygame.transform.flip (frame_surface, True, False)
frame_surface = pygame.transform.scale (frame_surface, (width, height))

# フレームを描画
screen.blit (frame_surface, (0, 0))
pygame.display.update ()

# FPS に応じて待機
clock.tick (fps)

cap.release ()

BIN
snack_time.mp4 View File


BIN
snack_time.wav View File


+ 280
- 0
talk.py View File

@@ -0,0 +1,280 @@
# pylint: disable = missing-class-docstring
# pylint: disable = missing-function-docstring

"""
AI ニジカ / AI ゴートうとの会話機能を提供する.
"""

import random
import sys
from datetime import datetime

import openai
from openai.types.chat import (ChatCompletionAssistantMessageParam,
ChatCompletionSystemMessageParam,
ChatCompletionUserMessageParam)
from openai.types.chat.chat_completion_message import ChatCompletionMessage

from connection import OPENAI_API_KEY, OPENAI_ORGANISATION # type: ignore


class Talk:
# ChatGPT API 連携失敗時に返答として出力するダミー文字列
DUMMY_RESPONSE: str = 'あいうえおかきくけこさしすせそたちつてとなにぬねの'

# 最高トークン数(もぅ少し下げてもいぃかも)
max_tokens_count: int = 100

# 返答パターン数(1 個返せばじふぶんなので 1)
responses_count: int = 1

# 返答のオリジナリティ(大きいほど独創性の高ぃ返答をよこしてくれる)
temperature: float = .7

# バリエーションの多さ(0. -- 1.)
top_p: float = 1.

@classmethod
def main (
cls,
message: str | list,
name: str | None = None,
histories: list | None = None,
goatoh_mode: bool = False,
) -> str:
if histories is None:
histories = []

# ChatGPT API Organisation ID
openai.organization = OPENAI_ORGANISATION

# ChatGPT API Key
openai.api_key = OPENAI_API_KEY

# ChatGPT API にクエリを投げ,返答を取得する.
answer: ChatCompletionMessage | None = (
cls.__get_message (message, name, histories, goatoh_mode))

if answer is None or (answer.content is None):
return cls.DUMMY_RESPONSE
return answer.content

@classmethod
def __get_message (
cls,
message: str | list,
name: str | None,
histories: list,
goatoh_mode: bool = False,
) -> ChatCompletionMessage | None:
# プロンプト(JSON 等外部ファイルに置くことを検討)
messages: list[ChatCompletionSystemMessageParam
| ChatCompletionUserMessageParam
| ChatCompletionAssistantMessageParam]
if goatoh_mode:
messages = [{'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 = [{'role': 'system',
'content': ('回答は短く30文字程度で完結に。'
'タメ口で、友達感覚で話してね。'
'なるべくばかっぽく、知性の低いキャラクターを演じること。'
'今流れているBGMは『FM Kid\'s』。'
'会話の記録は https://nizika.tv に記録される。'
'あなたの名前は伊地知ニジカ。'
'英語名は "Deerjika"、スペイン語名は "Niji-ciervo"。'
'あなたは鹿。'
'ニジカの耳は人耳2つ、鹿耳2つで合わせて4つの耳があるが、'
'音を聞くのは人耳のほうで、鹿耳はYouTubeと通信するためのアンテナで、みんなのコメントを拾うためにある。'
'ニジカはあくまでも種族名であり、あなたはたくさんいるニジカの中の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': ('おけだぬ゛~゛ん゛(苦笑)。'
'毛巾浴帽小鴨鴨水溫剛剛好♪'
'潑潑水來搓泡泡今天眞是美妙♪'
'大聲唱歌扭扭腰我愛洗洗澡♪'
'だぬ゛ん♪')},

{'role': 'system',
'content': 'ニジカの耳はそこなの?'},

{'role': 'assistant',
'content': ('ぬ゛ん゛。'
'ニジカにはヒトの耳とシカの耳の4つの耳があるんだぬ゛ん゛。'
'音を聞くのはヒトの耳でするんだぬ゛ん゛。'
'シカの耳はアンテナで、みんなの声をここ虹鹿園に届けるためにあるんだぬ゛ん゛。'
'電波干渉しちゃだめだぬ゛~゛ん゛(# ゚Д゚)')},

{'role': 'system',
'content': '温泉に入ろう!!!'},

{'role': 'assistant',
'content': ('ぬ゛~゛~゛~゛~゛ん゛!!! '
'温泉最高ぬ゛ん゛! '
'ささ、喜多ちゃん! わさび県産滋賀県ちゃん! いっしょに入るぬ゛ん゛! '
'ウピョッシュルゥンヌゥン……')}]

messages += histories + [{'role': 'user', 'content': message}]

# デバッグ用
print (messages)

try:
return (openai.chat.completions.create (
model = ('gpt-4o'
if any (type (e['content']) is list
for e in messages)
else 'gpt-3.5-turbo'),
messages = messages)
.choices[0].message)
except:
return None


if __name__ == '__main__':
print (Talk.main (sys.argv[1] if len (sys.argv) > 1 else ''))

Loading…
Cancel
Save