| @@ -1,15 +1,17 @@ | |||
| from __future__ import annotations | |||
| import json | |||
| import math | |||
| import os | |||
| import random | |||
| import subprocess | |||
| import sys | |||
| import time | |||
| import wave | |||
| from datetime import datetime, timedelta | |||
| from enum import Enum, auto | |||
| from io import BytesIO | |||
| from typing import Callable, TypedDict | |||
| from typing import Callable, TypedDict, cast | |||
| import cv2 | |||
| import emoji | |||
| @@ -34,6 +36,8 @@ from nizika_ai.config import DB | |||
| from nizika_ai.consts import Character, GPTModel, Platform, QueryType | |||
| from nizika_ai.models import Answer, AnsweredFlag, Query, User | |||
| NIZIKA_NICO_DIR = os.environ.get ('NIZIKA_NICO_DIR') or '/root/nizika_nico' | |||
| pygame.init () | |||
| FPS = 30 | |||
| @@ -42,6 +46,8 @@ SYSTEM_FONT = pygame.font.SysFont ('notosanscjkjp', 11, bold = True) | |||
| USER_FONT = pygame.font.SysFont ('notosanscjkjp', 15, italic = True) | |||
| DEERJIKA_FONT = pygame.font.SysFont ('07nikumarufont', 23) | |||
| WATCHING_MSG = 'それじゃあ、さっそく見てみるぬ゛ん゛!' | |||
| def main ( | |||
| ) -> None: | |||
| @@ -61,7 +67,7 @@ def main ( | |||
| except KeyError: | |||
| broadcast = None | |||
| waiting_balloon = (False, '', '') | |||
| waiting_balloon: tuple[bool, str | None, str] = (False, '', '') | |||
| last_flags_poll: float = 0 | |||
| traced_af_ids: list[int] = [] | |||
| @@ -76,7 +82,10 @@ def main ( | |||
| if (not balloon.enabled) and (not snack_time.enabled): | |||
| if waiting_balloon[0]: | |||
| deerjika.talk (waiting_balloon[1], waiting_balloon[2]) | |||
| waiting_balloon = (False, '', '') | |||
| if waiting_balloon[2] == WATCHING_MSG: | |||
| ... | |||
| else: | |||
| waiting_balloon = (False, '', '') | |||
| if now_m - last_flags_poll >= 1: | |||
| last_flags_poll = now_m | |||
| @@ -104,6 +113,12 @@ def main ( | |||
| waiting_balloon = (True, query.content, answer.content) | |||
| answer_flag.answered = True | |||
| answer_flag.save () | |||
| case QueryType.KIRIBAN: | |||
| query = Query.find (answer.query_id) | |||
| deerjika.talk (None, answer.content) | |||
| waiting_balloon = (True, None, WATCHING_MSG) | |||
| answer_flag.answered = True | |||
| answer_flag.save () | |||
| case _: | |||
| traced_af_ids.append (answer_flag.id) | |||
| DB.commit () | |||
| @@ -490,7 +505,7 @@ class Deerjika (Creature): | |||
| def talk ( | |||
| self, | |||
| query: str, | |||
| query: str | None, | |||
| answer: str, | |||
| ) -> None: | |||
| self.bell () | |||
| @@ -560,7 +575,7 @@ class Balloon (GameObject): | |||
| answer (str): 回答テキスト | |||
| image_url (str, None): 画像 URL | |||
| length (int): 表示する時間 (frame) | |||
| query (str): 質問テキスト | |||
| query (str, None): 質問テキスト | |||
| surface (Surface): 吹出し Surface | |||
| x_flip (bool): 左右反転フラグ | |||
| y_flip (bool): 上下反転フラグ | |||
| @@ -569,7 +584,7 @@ class Balloon (GameObject): | |||
| answer: str = '' | |||
| image_url: str | None = None | |||
| length: int = 300 | |||
| query: str = '' | |||
| query: str | None = None | |||
| surface: Surface | |||
| x_flip: bool = False | |||
| y_flip: bool = False | |||
| @@ -595,8 +610,10 @@ class Balloon (GameObject): | |||
| self.game.last_answered_at = self.game.now | |||
| return | |||
| query = self.query | |||
| if CommonModule.len_by_full (query) > 21: | |||
| if query and CommonModule.len_by_full (query) > 21: | |||
| query = CommonModule.mid_by_full (query, 0, 19.5) + '...' | |||
| if query is not None: | |||
| query = '>' + query | |||
| answer = Surface ( | |||
| (375, int (((CommonModule.len_by_full (self.answer) - 1) // 16 + 1) * 23.4375)), | |||
| pygame.SRCALPHA) | |||
| @@ -605,7 +622,7 @@ class Balloon (GameObject): | |||
| CommonModule.mid_by_full (self.answer, 16 * i, 16), True, (192, 0, 0)), | |||
| (0, 23.4375 * i)) | |||
| surface = self.surface.copy () | |||
| surface.blit (USER_FONT.render ('>' + query, True, (0, 0, 0)), (56.25, 32.8125)) | |||
| surface.blit (USER_FONT.render (query, True, (0, 0, 0)), (56.25, 32.8125)) | |||
| y: float | |||
| if self.frame < 30: | |||
| y = 0 | |||
| @@ -619,7 +636,7 @@ class Balloon (GameObject): | |||
| def talk ( | |||
| self, | |||
| query: str, | |||
| query: str | None, | |||
| answer: str, | |||
| image_url: str | None = None, | |||
| length: int = 300, | |||
| @@ -815,7 +832,7 @@ class Broadcast: | |||
| def __init__ ( | |||
| self, | |||
| broadcast_code, | |||
| broadcast_code: str, | |||
| ): | |||
| self.code = broadcast_code | |||
| self.chat = pytchat.create (self.code) | |||
| @@ -955,7 +972,13 @@ class Video (GameObject): | |||
| class NicoVideo (Video): | |||
| ... | |||
| def __init__ ( | |||
| self, | |||
| game: Game, | |||
| video_code: str, | |||
| ): | |||
| comments = fetch_comments (video_code) | |||
| #super ().__init__ (game, ) | |||
| class SnackTime (Video): | |||
| @@ -977,6 +1000,17 @@ def fetch_bytes_from_url ( | |||
| return res.content | |||
| def fetch_comments ( | |||
| video_code: str, | |||
| ) -> list[CommentDict]: | |||
| result = subprocess.run ( | |||
| ['python3', 'get_kiriban_list.py', video_code], | |||
| cwd = NIZIKA_NICO_DIR, | |||
| env = os.environ, | |||
| capture_output = True, | |||
| text = True) | |||
| return cast(list[CommentDict], json.loads (result.stdout)) | |||
| def add_query ( | |||
| broadcast: Broadcast, | |||
| ) -> None: | |||
| @@ -1007,6 +1041,11 @@ def add_query ( | |||
| DB.commit () | |||
| class CommentDict (TypedDict): | |||
| content: str | |||
| vpos_ms: int | |||
| def log ( | |||
| msg: str, | |||
| ) -> None: | |||