Browse Source

#30 おやつタイム対応(ブルスカ連携はまだ)

main
みてるぞ 1 month ago
parent
commit
719ae99273
11 changed files with 157 additions and 179 deletions
  1. +0
    -0
      __init__.py
  2. +2
    -1
      aques.py
  3. +0
    -1
      connection.sample.py
  4. +0
    -82
      error.txt
  5. +0
    -30
      error2.txt
  6. +110
    -64
      main.py
  7. BIN
      oh.wav
  8. +44
    -0
      play_movie.py
  9. BIN
      snack_time.mp4
  10. BIN
      snack_time.wav
  11. +1
    -1
      talk.py

+ 0
- 0
__init__.py View File


+ 2
- 1
aques.py View File

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




class Aques: class Aques:


+ 0
- 1
connection.sample.py View File

@@ -5,4 +5,3 @@ OPENAI_ORGANISATION: str = 'org-XXXXXXXXXXXXXXXXXXXXXXXX'


# API Key # API Key
OPENAI_API_KEY: str = 'sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' 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}}



+ 110
- 64
main.py View File

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


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


import play_movie
from aques import Aques from aques import Aques
from common_const import * 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
from youtube import *




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


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


# おやつ記録
has_snack = False

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


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


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


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


while True: while True:
# ChatGPT API を呼出し,返答を取得 # ChatGPT API を呼出し,返答を取得
@@ -225,54 +235,9 @@ class Main:
+ f'{json.dumps (chat_item.__dict__)}\t' + f'{json.dumps (chat_item.__dict__)}\t'
+ f'{answer}\n') + 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: if goatoh_talking:
@@ -286,10 +251,11 @@ class Main:
time.sleep (1.5) time.sleep (1.5)


# 返答の読上げを WAV ディタとして生成,取得 # 返答の読上げを WAV ディタとして生成,取得
wav: bytearray | None
try: try:
wav: bytearray | None = Aques.main (answer, goatoh_talking)
wav = Aques.main (answer, goatoh_talking)
except: except:
wav: None = None
wav = None


# 読上げを再生 # 読上げを再生
if wav is not None: if wav is not None:
@@ -324,13 +290,93 @@ class Main:
# 再生成 # 再生成
live_chat = pytchat.create (video_id = YOUTUBE_ID) live_chat = pytchat.create (video_id = YOUTUBE_ID)


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

pygame.display.update () 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)
cls.draw_talking (screen, balloon, user_font, nizika_font,
query, Talk.main (query).replace ('\n', ' '))
noon.play ()
time.sleep (1.5)

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


@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 @classmethod
def draw_bg ( def draw_bg (
cls, cls,
@@ -349,8 +395,8 @@ class Main:
sun_az: float, sun_az: float,
moon_alt: float, moon_alt: float,
moon_az: float, moon_az: float,
moon_days_old: float) \
-> None:
moon_days_old: float,
) -> None:
sunrise_centre: datetime = ( sunrise_centre: datetime = (
sunrise_start + (sunrise_end - sunrise_start) / 2) sunrise_start + (sunrise_end - sunrise_start) / 2)
sunset_centre: datetime = ( sunset_centre: datetime = (
@@ -379,16 +425,16 @@ class Main:
screen.blit (bg_night, (0, 0)) screen.blit (bg_night, (0, 0))


if sunrise_start <= dt < sunrise_end: 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: 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: else:
bg_evening.set_alpha (0) bg_evening.set_alpha (0)


if sunrise_start <= dt < sunset_end: 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: else:
jojoko.set_alpha (255) 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


+ 1
- 1
talk.py View File

@@ -15,7 +15,7 @@ from openai.types.chat import (ChatCompletionAssistantMessageParam,
ChatCompletionUserMessageParam) ChatCompletionUserMessageParam)
from openai.types.chat.chat_completion_message import ChatCompletionMessage from openai.types.chat.chat_completion_message import ChatCompletionMessage


from .connection import OPENAI_API_KEY, OPENAI_ORGANISATION # type: ignore
from connection import OPENAI_API_KEY, OPENAI_ORGANISATION # type: ignore




class Talk: class Talk:


Loading…
Cancel
Save