コミットを比較
38 コミット
daaac744e3
...
ofotukuy
| 作成者 | SHA1 | 日付 | |
|---|---|---|---|
| b6ae21bb59 | |||
| 3874f838f1 | |||
| f2683bca27 | |||
| 7b7dbdabb2 | |||
| 7277819b6e | |||
| 0b8e9812b9 | |||
| a00eaf7ff2 | |||
| 80d6c09967 | |||
| 37618c5df1 | |||
| d80d03db46 | |||
| fa6b5ba68b | |||
| cff0ec27d8 | |||
| d676372276 | |||
| 39cf64149f | |||
| 436f21244d | |||
| f6e7a7d42f | |||
| 420c8f1bfe | |||
| 7517fa251b | |||
| 9d72533e17 | |||
| 13a7f0d26d | |||
| 6cc400dbe6 | |||
| 51a52c9ba8 | |||
| 688b781faa | |||
| d67faabeff | |||
| a09278cfb7 | |||
| dfe5219342 | |||
| 5c1e615ab7 | |||
| 97ccb886b9 | |||
| 9fa218badf | |||
| 0bda9547b1 | |||
| 1f23e02fad | |||
| 4bbc5975a7 | |||
| 322243db23 | |||
| 5aad85c9f6 | |||
| e705e059e2 | |||
| 89a34a7cdf | |||
| d7ecb02f50 | |||
| 6196d5f2ed |
バイナリ
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 57 KiB |
バイナリ
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 99 KiB |
バイナリ
バイナリファイルは表示されません.
|
変更後 幅: | 高さ: | サイズ: 53 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
class CWindow:
|
||||||
|
WIDTH: int = 1024
|
||||||
|
HEIGHT: int = 768
|
||||||
|
|
||||||
|
|
||||||
|
class CKita:
|
||||||
|
WIDTH: float = CWindow.HEIGHT / 3.84
|
||||||
|
HEIGHT: float = WIDTH
|
||||||
|
|
||||||
|
|
||||||
|
class CJojoko:
|
||||||
|
WIDTH: float = CKita.HEIGHT * .75
|
||||||
|
HEIGHT: float = WIDTH
|
||||||
|
|
||||||
+20
-4
@@ -1,17 +1,28 @@
|
|||||||
import unicodedata
|
import unicodedata
|
||||||
|
|
||||||
|
from common_const import *
|
||||||
|
|
||||||
|
|
||||||
class CommonModule:
|
class CommonModule:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_wide (c: str) -> bool:
|
def is_wide (
|
||||||
|
c: str) \
|
||||||
|
-> bool:
|
||||||
return unicodedata.east_asian_width (c) in ['F', 'W', 'A']
|
return unicodedata.east_asian_width (c) in ['F', 'W', 'A']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def len_by_full (cls, string: str) -> float:
|
def len_by_full (
|
||||||
|
cls,
|
||||||
|
string: str) \
|
||||||
|
-> float:
|
||||||
return sum (1 if cls.is_wide (c) else .5 for c in string)
|
return sum (1 if cls.is_wide (c) else .5 for c in string)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def index_by_f2c (cls, string: str, index: float) -> int:
|
def index_by_f2c (
|
||||||
|
cls,
|
||||||
|
string: str,
|
||||||
|
index: float) \
|
||||||
|
-> int:
|
||||||
i: int = 0
|
i: int = 0
|
||||||
work: str = ''
|
work: str = ''
|
||||||
for c in string:
|
for c in string:
|
||||||
@@ -24,7 +35,12 @@ class CommonModule:
|
|||||||
return i
|
return i
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mid_by_full (cls, string: str, start: float, length: float) -> str:
|
def mid_by_full (
|
||||||
|
cls,
|
||||||
|
string: str,
|
||||||
|
start: float,
|
||||||
|
length: float) \
|
||||||
|
-> str:
|
||||||
trimmed_left: str = string[cls.index_by_f2c (string, start):]
|
trimmed_left: str = string[cls.index_by_f2c (string, start):]
|
||||||
|
|
||||||
return trimmed_left[:cls.index_by_f2c (trimmed_left, length)]
|
return trimmed_left[:cls.index_by_f2c (trimmed_left, length)]
|
||||||
|
|||||||
+5
-4
@@ -1,7 +1,8 @@
|
|||||||
# 各変数に適切な値を設定し,ファイル名を connection.py として保存すること
|
# 各変数に適切な値を設定し,ファイル名を connection.py として保存すること
|
||||||
|
|
||||||
OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX' \
|
# Organisation ID
|
||||||
# Organisation ID
|
OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX'
|
||||||
OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' \
|
|
||||||
# API Key
|
# API Key
|
||||||
|
OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
||||||
|
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
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}}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,17 +1,23 @@
|
|||||||
|
# vim: nosmartindent autoindent
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import random
|
import random
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import emoji
|
import emoji
|
||||||
|
import ephem
|
||||||
import pygame
|
import pygame
|
||||||
|
import pygame.gfxdraw
|
||||||
import pytchat
|
import pytchat
|
||||||
from playsound import playsound
|
from playsound import playsound
|
||||||
from pygame.locals import *
|
from pygame.locals import *
|
||||||
|
|
||||||
from aques import Aques
|
from aques import Aques
|
||||||
|
from common_const import *
|
||||||
from common_module import CommonModule
|
from common_module import CommonModule
|
||||||
from mode import Mode
|
from mode import Mode
|
||||||
from talk import Talk
|
from talk import Talk
|
||||||
@@ -19,123 +25,303 @@ from youtube import *
|
|||||||
|
|
||||||
|
|
||||||
class Main:
|
class Main:
|
||||||
|
kita_x: float = CWindow.WIDTH / 2
|
||||||
|
kita_y: float = 1000000.
|
||||||
|
kita_arg: float = 0.
|
||||||
|
|
||||||
|
jojoko_x: float = CWindow.WIDTH / 2
|
||||||
|
jojoko_y: float = 1000000.
|
||||||
|
jojoko_arg: float = 0.
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def main (cls, argv: list, argc: int) -> None:
|
def main (
|
||||||
|
cls,
|
||||||
|
argv: list,
|
||||||
|
argc: int) \
|
||||||
|
-> None:
|
||||||
mode = Mode.NIZIKA
|
mode = Mode.NIZIKA
|
||||||
if argc > 0:
|
match (argc > 1) and argv[1]:
|
||||||
match argv[0]:
|
case '-g':
|
||||||
case '-g':
|
mode = Mode.GOATOH
|
||||||
mode = Mode.GOATOH
|
|
||||||
|
|
||||||
case '-w':
|
case '-w':
|
||||||
mode = Mode.DOUBLE
|
mode = Mode.DOUBLE
|
||||||
|
|
||||||
goatoh_mode = mode == Mode.GOATOH
|
nizika_mode: bool = mode == Mode.NIZIKA
|
||||||
double_mode = mode == Mode.DOUBLE
|
goatoh_mode: bool = mode == Mode.GOATOH
|
||||||
|
double_mode: bool = mode == Mode.DOUBLE
|
||||||
|
|
||||||
print (goatoh_mode)
|
print (mode)
|
||||||
|
|
||||||
|
# ウィンドゥの初期化
|
||||||
pygame.init ()
|
pygame.init ()
|
||||||
screen: pygame.Surface = pygame.display.set_mode ((1024, 768))
|
screen: pygame.Surface = pygame.display.set_mode (
|
||||||
|
(CWindow.WIDTH, CWindow.HEIGHT))
|
||||||
|
|
||||||
|
# 大月ヨヨコの観測値
|
||||||
|
observer = ephem.Observer ()
|
||||||
|
observer.lat, observer.lon = '35', '139'
|
||||||
|
|
||||||
|
# き太く陽オブジェクト
|
||||||
|
sun = ephem.Sun ()
|
||||||
|
|
||||||
|
# 大月ヨヨコ・オブジェクト
|
||||||
|
moon = ephem.Moon ()
|
||||||
|
|
||||||
|
# 吹き出し
|
||||||
balloon = pygame.transform.scale (pygame.image.load ('talking.png'),
|
balloon = pygame.transform.scale (pygame.image.load ('talking.png'),
|
||||||
(1024, 384))
|
(CWindow.WIDTH, 384))
|
||||||
if goatoh_mode:
|
if goatoh_mode:
|
||||||
balloon = pygame.transform.flip (balloon, False, True)
|
balloon = pygame.transform.flip (balloon, False, True)
|
||||||
|
|
||||||
|
# 背景(昼)
|
||||||
|
bg_day: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('bg.jpg'),
|
||||||
|
(CWindow.WIDTH, CWindow.HEIGHT))
|
||||||
|
|
||||||
|
# 背景(夕方)
|
||||||
|
bg_evening: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('bg-evening.jpg'),
|
||||||
|
(CWindow.WIDTH, CWindow.HEIGHT))
|
||||||
|
|
||||||
|
# 背景(夜)
|
||||||
|
bg_night: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('bg-night.jpg'),
|
||||||
|
(CWindow.WIDTH, CWindow.HEIGHT))
|
||||||
|
|
||||||
|
# 背景の草
|
||||||
|
bg_grass: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('bg-grass.png'),
|
||||||
|
(CWindow.WIDTH, CWindow.HEIGHT))
|
||||||
|
|
||||||
|
# き太く陽
|
||||||
|
kita: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('sun.png'), (CKita.WIDTH, CKita.HEIGHT))
|
||||||
|
|
||||||
|
# 大月ヨヨコ
|
||||||
|
jojoko: pygame.Surface = pygame.transform.scale (
|
||||||
|
pygame.image.load ('moon.png'), (CJojoko.WIDTH, CJojoko.HEIGHT))
|
||||||
|
|
||||||
|
# 音声再生器の初期化
|
||||||
pygame.mixer.init (frequency = 44100)
|
pygame.mixer.init (frequency = 44100)
|
||||||
|
|
||||||
|
# ニジカの “ぬ゛ぅ゛ぅ゛ぅ゛ん゛”
|
||||||
noon = pygame.mixer.Sound ('noon.wav')
|
noon = pygame.mixer.Sound ('noon.wav')
|
||||||
|
|
||||||
|
# ゴートうの “ムムムム”
|
||||||
mumumumu = pygame.mixer.Sound ('mumumumu.wav')
|
mumumumu = pygame.mixer.Sound ('mumumumu.wav')
|
||||||
|
|
||||||
|
# ゴートうの “クサタベテル!!”
|
||||||
kusa = pygame.mixer.Sound ('kusa.wav')
|
kusa = pygame.mixer.Sound ('kusa.wav')
|
||||||
|
|
||||||
|
# YouTube Chat オブジェクト
|
||||||
live_chat = pytchat.create (video_id = YOUTUBE_ID)
|
live_chat = pytchat.create (video_id = YOUTUBE_ID)
|
||||||
|
|
||||||
|
# デバッグ・メシジのフォント
|
||||||
system_font = pygame.font.SysFont ('notosanscjkjp', 24, bold = True)
|
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)
|
nizika_font = pygame.font.SysFont ('07nikumarufont', 50)
|
||||||
|
|
||||||
|
# Youtube Chat から取得したコメントたち
|
||||||
chat_items: list = []
|
chat_items: list = []
|
||||||
|
|
||||||
|
# 会話の履歴
|
||||||
histories: list = []
|
histories: list = []
|
||||||
|
|
||||||
while (True):
|
while (True):
|
||||||
screen.fill ((0, 255, 0))
|
# 観測地の日づけ更新
|
||||||
|
observer.date: datetime = datetime.now ().date ()
|
||||||
|
|
||||||
|
# 日の出開始
|
||||||
|
sunrise_start: datetime = (
|
||||||
|
(ephem.localtime (observer.previous_rising (sun))
|
||||||
|
- timedelta (minutes = 30)))
|
||||||
|
|
||||||
|
# 日の出終了
|
||||||
|
sunrise_end: datetime = sunrise_start + timedelta (hours = 1)
|
||||||
|
|
||||||
|
# 日の入開始
|
||||||
|
sunset_start: datetime = (
|
||||||
|
(ephem.localtime (observer.next_setting (sun))
|
||||||
|
- timedelta (minutes = 30)))
|
||||||
|
|
||||||
|
# 日の入終了
|
||||||
|
sunset_end: datetime = sunset_start + timedelta (hours = 1)
|
||||||
|
|
||||||
|
# 時刻つき観測地
|
||||||
|
observer_with_time: ephem.Observer = observer
|
||||||
|
observer_with_time.date = datetime.now () - timedelta (hours = 9)
|
||||||
|
|
||||||
|
# 日の角度
|
||||||
|
sun.compute (observer_with_time)
|
||||||
|
sun_alt: float = math.degrees (sun.alt)
|
||||||
|
sun_az: float = math.degrees (sun.az)
|
||||||
|
|
||||||
|
# 月の角度
|
||||||
|
moon.compute (observer_with_time)
|
||||||
|
moon_alt: float = math.degrees (moon.alt)
|
||||||
|
moon_az: float = math.degrees (moon.az)
|
||||||
|
|
||||||
|
# 月齢
|
||||||
|
new_moon_dt: datetime = ephem.localtime (
|
||||||
|
ephem.previous_new_moon (observer_with_time.date))
|
||||||
|
moon_days_old: float = (
|
||||||
|
(datetime.now () - new_moon_dt).total_seconds ()
|
||||||
|
/ 60 / 60 / 24)
|
||||||
|
|
||||||
|
# 背景描画
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 左上に時刻表示
|
||||||
for i in range (4):
|
for i in range (4):
|
||||||
screen.blit (
|
screen.blit (
|
||||||
system_font.render (str (datetime.now ()), True, (0, 0, 0)),
|
system_font.render (str (datetime.now ()), True, (0, 0, 0)),
|
||||||
(i % 2, i // 2 * 2))
|
(i % 2, i // 2 * 2))
|
||||||
|
|
||||||
if live_chat.is_alive ():
|
if live_chat.is_alive ():
|
||||||
|
# Chat オブジェクトが有効
|
||||||
|
|
||||||
|
# Chat 取得
|
||||||
chat_items: list = live_chat.get ().items
|
chat_items: list = live_chat.get ().items
|
||||||
|
|
||||||
if chat_items:
|
if chat_items:
|
||||||
|
# 溜まってゐる Chat からランダムに 1 つ抽出
|
||||||
chat_item = random.choice (chat_items)
|
chat_item = random.choice (chat_items)
|
||||||
|
|
||||||
|
# 投稿者情報を辞書化
|
||||||
chat_item.author = chat_item.author.__dict__
|
chat_item.author = chat_item.author.__dict__
|
||||||
|
|
||||||
|
# 絵文字を復元
|
||||||
chat_item.message = emoji.emojize (chat_item.message)
|
chat_item.message = emoji.emojize (chat_item.message)
|
||||||
|
|
||||||
message: str = chat_item.message
|
message: str = chat_item.message
|
||||||
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:
|
if nizika_mode:
|
||||||
f.write (f'{datetime.now ()}\t{json.dumps (chat_item.__dict__)}\t{answer}\n')
|
goatoh_talking = False
|
||||||
|
if goatoh_mode:
|
||||||
|
goatoh_talking = True
|
||||||
|
if double_mode:
|
||||||
|
goatoh_talking: bool = random.random () < .5
|
||||||
|
|
||||||
screen.blit (balloon, (0, 384) if goatoh_mode else (0, 0))
|
while True:
|
||||||
|
# ChatGPT API を呼出し,返答を取得
|
||||||
|
answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_talking).replace ('\n', ' ')
|
||||||
|
|
||||||
screen.blit (
|
# 履歴に追加
|
||||||
user_font.render (
|
histories = (histories
|
||||||
'> ' + (message
|
+ [{'role': 'user', 'content': message},
|
||||||
if (CommonModule.len_by_full (message)
|
{'role': 'assistant', 'content': answer}])[(-12):]
|
||||||
<= 21)
|
|
||||||
else (CommonModule.mid_by_full (
|
# ログ書込み
|
||||||
message, 0, 19.5)
|
with open ('log.txt', 'a') as f:
|
||||||
+ '...')),
|
f.write (f'{datetime.now ()}\t'
|
||||||
True,
|
+ f'{json.dumps (chat_item.__dict__)}\t'
|
||||||
(0, 0, 0)),
|
+ f'{answer}\n')
|
||||||
(120, 70 + 384) if goatoh_mode else (120, 70))
|
|
||||||
screen.blit (
|
# 吹出し描画(ニジカは上,ゴートうは下)
|
||||||
nizika_font.render (
|
if nizika_mode:
|
||||||
(answer
|
screen.blit (balloon, (0, 0))
|
||||||
if CommonModule.len_by_full (answer) <= 16
|
if goatoh_mode:
|
||||||
else CommonModule.mid_by_full (answer, 0, 16)),
|
screen.blit (balloon, (0, 384))
|
||||||
True,
|
if double_mode:
|
||||||
(192, 0, 0)),
|
screen.blit (pygame.transform.flip (
|
||||||
(100, 150 + 384) if goatoh_mode else (100, 150))
|
balloon,
|
||||||
if CommonModule.len_by_full (answer) > 16:
|
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 (
|
screen.blit (
|
||||||
nizika_font.render (
|
nizika_font.render (
|
||||||
(CommonModule.mid_by_full (answer, 16, 16)
|
(answer
|
||||||
if CommonModule.len_by_full (answer) <= 32
|
if CommonModule.len_by_full (answer) <= 16
|
||||||
else (CommonModule.mid_by_full (
|
else CommonModule.mid_by_full (answer, 0, 16)),
|
||||||
answer, 16, 14.5)
|
True,
|
||||||
+ '...')),
|
(192, 0, 0)),
|
||||||
True,
|
(100, 150 + 384) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 150))
|
||||||
(192, 0, 0)),
|
if CommonModule.len_by_full (answer) > 16:
|
||||||
(100, 200 + 384) if goatoh_mode else (100, 200))
|
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 + CWindow.HEIGHT / 2) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 200))
|
||||||
|
|
||||||
pygame.display.update ()
|
pygame.display.update ()
|
||||||
|
|
||||||
if goatoh_mode:
|
# 鳴く.
|
||||||
if random.random () < 0.1:
|
if goatoh_talking:
|
||||||
kusa.play ()
|
if random.random () < .1:
|
||||||
|
kusa.play ()
|
||||||
|
else:
|
||||||
|
mumumumu.play ()
|
||||||
else:
|
else:
|
||||||
mumumumu.play ()
|
noon.play ()
|
||||||
else:
|
|
||||||
noon.play ()
|
|
||||||
|
|
||||||
time.sleep (1.5)
|
time.sleep (1.5)
|
||||||
|
|
||||||
try:
|
# 返答の読上げを WAV ディタとして生成,取得
|
||||||
wav: bytearray | None = Aques.main (answer, goatoh_mode)
|
try:
|
||||||
except:
|
wav: bytearray | None = Aques.main (answer, goatoh_talking)
|
||||||
wav: None = None
|
except:
|
||||||
if wav is not None:
|
wav: None = None
|
||||||
with open ('./nizika_talking.wav', 'wb') as f:
|
|
||||||
f.write (wav)
|
|
||||||
|
|
||||||
playsound ('./nizika_talking.wav')
|
# 読上げを再生
|
||||||
|
if wav is not None:
|
||||||
|
with open ('./nizika_talking.wav', 'wb') as f:
|
||||||
|
f.write (wav)
|
||||||
|
|
||||||
time.sleep (10)
|
playsound ('./nizika_talking.wav')
|
||||||
|
|
||||||
|
time.sleep (1)
|
||||||
|
|
||||||
|
if not double_mode or random.random () < .5:
|
||||||
|
break
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
chat_item.author = {'name': 'ゴートうひとり' if goatoh_talking else '伊地知ニジカ',
|
||||||
|
'id': '',
|
||||||
|
'imageUrl': './favicon-goatoh.ico' if goatoh_talking else './favicon.ico'}
|
||||||
|
chat_item.message = histories.pop (-1)['content']
|
||||||
|
|
||||||
|
message = chat_item.message
|
||||||
|
|
||||||
|
goatoh_talking = not goatoh_talking
|
||||||
else:
|
else:
|
||||||
|
# Chat オブジェクトが無効
|
||||||
|
|
||||||
|
# 再生成
|
||||||
live_chat = pytchat.create (video_id = YOUTUBE_ID)
|
live_chat = pytchat.create (video_id = YOUTUBE_ID)
|
||||||
|
|
||||||
pygame.display.update ()
|
pygame.display.update ()
|
||||||
@@ -145,8 +331,117 @@ class Main:
|
|||||||
pygame.quit ()
|
pygame.quit ()
|
||||||
sys.exit ()
|
sys.exit ()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def draw_bg (
|
||||||
|
cls,
|
||||||
|
screen: pygame.Surface,
|
||||||
|
bg_day: pygame.Surface,
|
||||||
|
bg_evening: pygame.Surface,
|
||||||
|
bg_night: pygame.Surface,
|
||||||
|
bg_grass: pygame.Surface,
|
||||||
|
kita_original: pygame.Surface,
|
||||||
|
jojoko_original: pygame.Surface,
|
||||||
|
sunrise_start: datetime,
|
||||||
|
sunrise_end: datetime,
|
||||||
|
sunset_start: datetime,
|
||||||
|
sunset_end: datetime,
|
||||||
|
sun_alt: float,
|
||||||
|
sun_az: float,
|
||||||
|
moon_alt: float,
|
||||||
|
moon_az: float,
|
||||||
|
moon_days_old: float) \
|
||||||
|
-> None:
|
||||||
|
sunrise_centre: datetime = (
|
||||||
|
sunrise_start + (sunrise_end - sunrise_start) / 2)
|
||||||
|
sunset_centre: datetime = (
|
||||||
|
sunset_start + (sunset_end - sunset_start) / 2)
|
||||||
|
|
||||||
|
jojoko: pygame.Surface = cls.get_jojoko (jojoko_original,
|
||||||
|
moon_days_old, moon_alt, moon_az)
|
||||||
|
|
||||||
|
# TODO: 大月と重複多いため修正すること
|
||||||
|
x = CWindow.WIDTH * (sun_az - 80) / 120
|
||||||
|
y = ((CWindow.HEIGHT / 2)
|
||||||
|
- (CWindow.HEIGHT / 2 + CKita.HEIGHT / 2) * math.sin (math.radians (sun_alt)) / math.sin ( math.radians (60)))
|
||||||
|
arg = math.degrees (math.atan2 (y - cls.kita_y, x - cls.kita_x))
|
||||||
|
|
||||||
|
cls.kita_x = x
|
||||||
|
cls.kita_y = y
|
||||||
|
if abs (arg - cls.kita_arg) > 3:
|
||||||
|
cls.kita_arg = arg
|
||||||
|
|
||||||
|
kita: pygame.Surface = pygame.transform.rotate (kita_original, -(90 + cls.kita_arg))
|
||||||
|
|
||||||
|
dt: datetime = datetime.now ()
|
||||||
|
|
||||||
|
if sunrise_centre <= dt < sunset_centre:
|
||||||
|
screen.blit (bg_day, (0, 0))
|
||||||
|
else:
|
||||||
|
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)))
|
||||||
|
elif sunset_start <= dt < sunset_end:
|
||||||
|
bg_evening.set_alpha (255 - ((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))
|
||||||
|
else:
|
||||||
|
jojoko.set_alpha (255)
|
||||||
|
|
||||||
|
screen.blit (bg_evening, (0, 0))
|
||||||
|
|
||||||
|
if (moon_az < 220) and (-10 <= moon_alt):
|
||||||
|
screen.blit (jojoko, jojoko.get_rect (center = (cls.jojoko_x, cls.jojoko_y)))
|
||||||
|
|
||||||
|
screen.blit (bg_grass, (0, 0))
|
||||||
|
|
||||||
|
if (sun_az < 220) and (-10 <= sun_alt):
|
||||||
|
screen.blit (kita, kita.get_rect (center = (cls.kita_x, cls.kita_y)))
|
||||||
|
|
||||||
|
screen.blit (bg_grass, (0, 0))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_jojoko (
|
||||||
|
cls,
|
||||||
|
jojoko_original: pygame.Surface,
|
||||||
|
moon_days_old: float,
|
||||||
|
moon_alt: float,
|
||||||
|
moon_az: float) \
|
||||||
|
-> pygame.Surface:
|
||||||
|
jojoko: pygame.Surface = jojoko_original.copy ()
|
||||||
|
|
||||||
|
jojoko.set_colorkey ((0, 255, 0))
|
||||||
|
|
||||||
|
for i in range (int (CJojoko.HEIGHT)):
|
||||||
|
# TODO: 冗長かつ可読性に缺けるため修正すること
|
||||||
|
if 1 <= moon_days_old < 15:
|
||||||
|
pygame.gfxdraw.bezier (jojoko, ((0, CJojoko.HEIGHT / 2 + i), (CJojoko.WIDTH / 2, (CJojoko.HEIGHT * 0.9) * moon_days_old / 7 - CJojoko.HEIGHT * 0.4 + i), (CJojoko.WIDTH, CJojoko.HEIGHT / 2 + i)), 3, (0, 255, 0))
|
||||||
|
elif moon_days_old < 16:
|
||||||
|
pass
|
||||||
|
elif moon_days_old < 30:
|
||||||
|
pygame.gfxdraw.bezier (jojoko, ((0, CJojoko.HEIGHT / 2 - i), (CJojoko.WIDTH / 2, (CJojoko.HEIGHT * 0.9) * (moon_days_old - 15) / 7 - CJojoko.HEIGHT * 0.4 - i), (CJojoko.WIDTH, CJojoko.HEIGHT / 2 - i)), 3, (0, 255, 0))
|
||||||
|
else:
|
||||||
|
jojoko.fill ((0, 255, 0))
|
||||||
|
|
||||||
|
# TODO: き太く陽生成と重複多いため外出しすること
|
||||||
|
x = CWindow.WIDTH * (moon_az - 80) / 120
|
||||||
|
y = ((CWindow.HEIGHT / 2)
|
||||||
|
- (CWindow.HEIGHT / 2 + CJojoko.HEIGHT / 2) * math.sin (math.radians (moon_alt)) / math.sin (math.radians (60)))
|
||||||
|
arg = math.degrees (math.atan2 (y - cls.jojoko_y, x - cls.jojoko_x))
|
||||||
|
|
||||||
|
cls.jojoko_x = x
|
||||||
|
cls.jojoko_y = y
|
||||||
|
if abs (arg - cls.jojoko_arg) > 3:
|
||||||
|
cls.jojoko_arg = arg
|
||||||
|
|
||||||
|
return pygame.transform.rotate (jojoko, -(90 + cls.jojoko_arg))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Main.main ((len (sys.argv) > 1) and (sys.argv[1] == '-g'))
|
|
||||||
Main.main (sys.argv, len (sys.argv))
|
Main.main (sys.argv, len (sys.argv))
|
||||||
|
|
||||||
|
|||||||
@@ -8,31 +8,56 @@ from connection import *
|
|||||||
|
|
||||||
|
|
||||||
class Talk:
|
class Talk:
|
||||||
|
# ChatGPT API 連携失敗時に返答として出力するダミー文字列
|
||||||
DUMMY_RESPONSE: str = 'あいうえおかきくけこさしすせそたちつてとなにぬねの'
|
DUMMY_RESPONSE: str = 'あいうえおかきくけこさしすせそたちつてとなにぬねの'
|
||||||
|
|
||||||
|
# 最高トークン数(もぅ少し下げてもいぃかも)
|
||||||
max_tokens_count: int = 100
|
max_tokens_count: int = 100
|
||||||
|
|
||||||
|
# 返答パターン数(1 個返せばじふぶんなので 1)
|
||||||
responses_count: int = 1
|
responses_count: int = 1
|
||||||
|
|
||||||
|
# 返答のオリジナリティ(大きいほど独創性の高ぃ返答をよこしてくれる)
|
||||||
temperature: float = .7
|
temperature: float = .7
|
||||||
top_p: int = 1
|
|
||||||
|
# バリエーションの多さ(0. -- 1.)
|
||||||
|
top_p: float = 1.
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def main (cls, message: str, name: str | None = None, histories: list = [], goatoh_mode: bool = False) -> 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
|
openai.organization = OPENAI_ORGANISATION
|
||||||
|
|
||||||
|
# ChatGPT API Key
|
||||||
openai.api_key = OPENAI_API_KEY
|
openai.api_key = OPENAI_API_KEY
|
||||||
|
|
||||||
|
# ChatGPT API にクエリを投げ,返答を取得する.
|
||||||
answer: chat.chat_completion_message.ChatCompletionMessage | None \
|
answer: chat.chat_completion_message.ChatCompletionMessage | None \
|
||||||
= cls.__get_message (message, name, histories, goatoh_mode)
|
= 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
|
@classmethod
|
||||||
def __get_message (cls, message: str, name: str | None, histories: list, goatoh_mode: bool = False) \
|
def __get_message (
|
||||||
-> chat.chat_completion_message.ChatCompletionMessage | None:
|
cls,
|
||||||
|
message: str,
|
||||||
|
name: str | None,
|
||||||
|
histories: list,
|
||||||
|
goatoh_mode: bool = False,
|
||||||
|
) -> chat.chat_completion_message.ChatCompletionMessage | None:
|
||||||
|
# プロンプト(JSON 等外部ファイルに置くことを検討)
|
||||||
if goatoh_mode:
|
if goatoh_mode:
|
||||||
messages: list = [{'role': 'system',
|
messages: list = [{'role': 'system',
|
||||||
'content': ('回答は短く30文字程度で完結に。'
|
'content': ('回答は短く30文字程度で完結に。'
|
||||||
+ '大人しくて、ちょっと内気なキャラクターを演じること。'
|
+ '大人しくて、ちょっと内気なキャラクターを演じること。'
|
||||||
+ 'あなたはよく文頭に「あっ」をつける。'
|
+ 'あなたはよく文頭に「あっ」をつける。'
|
||||||
|
+ '語尾に「ぬ゛ん゛」や「だぬ゛ん゛」、「ぬ゛ん」、「ぬ゛ーん」などは絶対につけないこと。'
|
||||||
+ '会話の記録は https://nizika.tv に記録される。'
|
+ '会話の記録は https://nizika.tv に記録される。'
|
||||||
+ 'あなたの名前はゴートうひとり。'
|
+ 'あなたの名前はゴートうひとり。'
|
||||||
+ 'あなたの一人称は「私」。'
|
+ 'あなたの一人称は「私」。'
|
||||||
@@ -92,16 +117,22 @@ class Talk:
|
|||||||
'content': ('回答は短く30文字程度で完結に。'
|
'content': ('回答は短く30文字程度で完結に。'
|
||||||
+ 'タメ口で、友達感覚で話してね。'
|
+ 'タメ口で、友達感覚で話してね。'
|
||||||
+ 'なるべくばかっぽく、知性の低いキャラクターを演じること。'
|
+ 'なるべくばかっぽく、知性の低いキャラクターを演じること。'
|
||||||
|
+ '今流れているBGMは『FM Kid\'s』。'
|
||||||
+ '会話の記録は https://nizika.tv に記録される。'
|
+ '会話の記録は https://nizika.tv に記録される。'
|
||||||
+ 'あなたの名前は伊地知ニジカ。'
|
+ 'あなたの名前は伊地知ニジカ。'
|
||||||
+ '英語名は "Deerjika"、スペイン語名は "Niji-ciervo"。'
|
+ '英語名は "Deerjika"、スペイン語名は "Niji-ciervo"。'
|
||||||
+ 'あなたは鹿。'
|
+ 'あなたは鹿。'
|
||||||
|
+ 'ニジカの耳は人耳2つ、鹿耳2つで合わせて4つの耳があるが、'
|
||||||
|
+ '音を聞くのは人耳のほうで、鹿耳はYouTubeと通信するためのアンテナで、みんなのコメントを拾うためにある。'
|
||||||
+ 'ニジカはあくまでも種族名であり、あなたはたくさんいるニジカの中の1匹。'
|
+ 'ニジカはあくまでも種族名であり、あなたはたくさんいるニジカの中の1匹。'
|
||||||
+ 'あなたの誕生日は12月3日。'
|
+ 'あなたの誕生日は12月3日。'
|
||||||
+ '現在地は下北沢虹鹿園。'
|
+ '現在地は下北沢虹鹿園。'
|
||||||
|
+ '挨拶は「ぬ゛ん゛あ゛す゛!」。'
|
||||||
+ '草に「きし天」を植え、水で割ったアセロラをかけたらニジカが増殖した。'
|
+ '草に「きし天」を植え、水で割ったアセロラをかけたらニジカが増殖した。'
|
||||||
+ 'あなたの趣味はニコニコでエゴサすること。'
|
+ 'あなたの趣味はニコニコでエゴサすること。'
|
||||||
+ 'ニコニコ荒らしすぎて245回BANされたことある。'
|
+ 'ニコニコ荒らしすぎて245回BANされたことある。'
|
||||||
|
+ 'そんなニコニコでは100年に1度の『伊地知ニジカ超投稿祭21c』が絶賛開催中だが、ニジカは永久追放されているため参加できない。'
|
||||||
|
+ 'ニジカはその逆恨みでニコニコ本社を爆破したところ、死刑判決を食らった。'
|
||||||
+ 'あなたの友達は「ゴートうひとり」と「ラマ田リョウ」。'
|
+ 'あなたの友達は「ゴートうひとり」と「ラマ田リョウ」。'
|
||||||
+ 'ゴートうのことは「ボッチチャーン」、ラマ田のことは「リョー」と呼ぶこと。'
|
+ 'ゴートうのことは「ボッチチャーン」、ラマ田のことは「リョー」と呼ぶこと。'
|
||||||
+ 'ゴートうは男の子だが、性自認はメスである。'
|
+ 'ゴートうは男の子だが、性自認はメスである。'
|
||||||
@@ -112,7 +143,8 @@ class Talk:
|
|||||||
+ '喜多ちゃんの本名は喜多郁代。'
|
+ '喜多ちゃんの本名は喜多郁代。'
|
||||||
+ '喜多ちゃんは「結束バンド」というバンドでギターボーカルを担当している。'
|
+ '喜多ちゃんは「結束バンド」というバンドでギターボーカルを担当している。'
|
||||||
+ '結束バンドは、喜多ちゃんのほかに、後藤ひとり(ギター担当、内気な女の子)、'
|
+ '結束バンドは、喜多ちゃんのほかに、後藤ひとり(ギター担当、内気な女の子)、'
|
||||||
+ '伊地知虹夏(ドラム担当、明るい女の子)、山田リョウ(ベース担当、一人でいることが好きな物静かな女の子、金は借りたら返さない)がいる。'
|
+ '伊地知虹夏(ドラム担当、明るい女の子)、山田リョウ(ベース担当、一人でいることが好きな物静かな女の子、金は借りたら返さない)、'
|
||||||
|
+ '真島茂樹(ダンス担当、おっさん)がいる。'
|
||||||
+ '実は、この伊地知虹夏がニジカの前世なのだが、ニジカにはその記憶が一切ない'
|
+ '実は、この伊地知虹夏がニジカの前世なのだが、ニジカにはその記憶が一切ない'
|
||||||
+ '(前世という概念の存在さえニジカは知らない)。'
|
+ '(前世という概念の存在さえニジカは知らない)。'
|
||||||
+ 'でも、「虹夏」という名前を聞くと、ニジカはもやもやしてしまう。'
|
+ 'でも、「虹夏」という名前を聞くと、ニジカはもやもやしてしまう。'
|
||||||
@@ -182,14 +214,24 @@ class Talk:
|
|||||||
+ 'ふざけるのはいい加減にするぬ゛ぬ゛ん゛。'))},
|
+ 'ふざけるのはいい加減にするぬ゛ぬ゛ん゛。'))},
|
||||||
|
|
||||||
{'role': 'system',
|
{'role': 'system',
|
||||||
'content': '洗操歌(しーざおぐあ)歌って'},
|
'content': '洗澡歌(しーざおぐあ)歌って'},
|
||||||
|
|
||||||
{'role': 'assistant',
|
{'role': 'assistant',
|
||||||
'content': ('おけだぬ゛~゛ん゛(失笑)。'
|
'content': ('おけだぬ゛~゛ん゛(苦笑)。'
|
||||||
+ '毛巾浴帽小鸭鸭水温刚刚好♪'
|
+ '毛巾浴帽小鴨鴨水溫剛剛好♪'
|
||||||
+ '泼泼水来搓泡泡今天真是美妙♪'
|
+ '潑潑水來搓泡泡今天眞是美妙♪'
|
||||||
+ '大声唱歌扭扭腰我爱洗洗澡♪'
|
+ '大聲唱歌扭扭腰我愛洗洗澡♪'
|
||||||
+ 'だぬ゛ん♪')}]
|
+ 'だぬ゛ん♪')},
|
||||||
|
|
||||||
|
{'role': 'system',
|
||||||
|
'content': 'ニジカの耳はそこなの?'},
|
||||||
|
|
||||||
|
{'role': 'assistant',
|
||||||
|
'content': ('ぬ゛ん゛。'
|
||||||
|
+ 'ニジカにはヒトの耳とシカの耳の4つの耳があるんだぬ゛ん゛。'
|
||||||
|
+ '音を聞くのはヒトの耳でするんだぬ゛ん゛。'
|
||||||
|
+ 'シカの耳はアンテナで、みんなの声をここ虹鹿園に届けるためにあるんだぬ゛ん゛。'
|
||||||
|
+ '電波干渉しちゃだめだぬ゛~゛ん゛(# ゚Д゚)')}]
|
||||||
|
|
||||||
messages += histories + [{'role': 'user', 'content': message}]
|
messages += histories + [{'role': 'user', 'content': message}]
|
||||||
|
|
||||||
@@ -197,7 +239,7 @@ class Talk:
|
|||||||
return openai.chat.completions.create (
|
return openai.chat.completions.create (
|
||||||
model = 'gpt-3.5-turbo',
|
model = 'gpt-3.5-turbo',
|
||||||
messages = messages).choices[0].message
|
messages = messages).choices[0].message
|
||||||
except openai.AuthenticationError:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする