伊地知ニジカ放送局だぬ゛ん゛. https://www.youtube.com/@deerjika
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.py 6.7 KiB

5 months ago
9 months ago
9 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
9 months ago
5 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import json
  2. import random
  3. import subprocess
  4. import sys
  5. import time
  6. from datetime import datetime
  7. import emoji
  8. import pygame
  9. import pytchat
  10. from playsound import playsound
  11. from pygame.locals import *
  12. from aques import Aques
  13. from common_const import *
  14. from common_module import CommonModule
  15. from talk import Talk
  16. from youtube import *
  17. class Main:
  18. @classmethod
  19. def main (cls, goatoh_mode: bool = False) -> None:
  20. print (goatoh_mode)
  21. # ウィンドゥの初期化
  22. pygame.init ()
  23. screen: pygame.Surface = pygame.display.set_mode (
  24. (CWindow.WIDTH, CWindow.HEIGHT))
  25. # 吹き出し
  26. balloon = pygame.transform.scale (pygame.image.load ('talking.png'),
  27. (CWindow.WIDTH, 384))
  28. if goatoh_mode:
  29. balloon = pygame.transform.flip (balloon, False, True)
  30. # 音声再生器の初期化
  31. pygame.mixer.init (frequency = 44100)
  32. # ニジカの “ぬ゛ぅ゛ぅ゛ぅ゛ん゛”
  33. noon = pygame.mixer.Sound ('noon.wav')
  34. # ゴートうの “ムムムム”
  35. mumumumu = pygame.mixer.Sound ('mumumumu.wav')
  36. # ゴートうの “クサタベテル!!”
  37. kusa = pygame.mixer.Sound ('kusa.wav')
  38. # YouTube Chat オブジェクト
  39. live_chat = pytchat.create (video_id = YOUTUBE_ID)
  40. # デバッグ・メシジのフォント
  41. system_font = pygame.font.SysFont ('notosanscjkjp', 24, bold = True)
  42. # 視聴者コメントのフォント
  43. user_font = pygame.font.SysFont ('notosanscjkjp', 32,
  44. italic = True)
  45. # ニジカのフォント
  46. nizika_font = pygame.font.SysFont ('07nikumarufont', 50)
  47. # Youtube Chat から取得したコメントたち
  48. chat_items: list = []
  49. # 会話の履歴(3 件分保持)
  50. histories: list = []
  51. while (True):
  52. screen.fill ((0, 255, 0))
  53. # 左上に時刻表示
  54. for i in range (4):
  55. screen.blit (
  56. system_font.render (str (datetime.now ()), True, (0, 0, 0)),
  57. (i % 2, i // 2 * 2))
  58. if live_chat.is_alive ():
  59. # Chat オブジェクトが有効
  60. # Chat 取得
  61. chat_items: list = live_chat.get ().items
  62. if chat_items:
  63. # 溜まってゐる Chat からランダムに 1 つ抽出
  64. chat_item = random.choice (chat_items)
  65. # 投稿者情報を辞書化
  66. chat_item.author = chat_item.author.__dict__
  67. # 絵文字を復元
  68. chat_item.message = emoji.emojize (chat_item.message)
  69. message: str = chat_item.message
  70. # ChatGPT API を呼出し,返答を取得
  71. answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_mode).replace ('\n', ' ')
  72. # 履歴に追加
  73. histories = (histories
  74. + [{'role': 'user', 'content': message},
  75. {'role': 'assistant', 'content': answer}])[(-6):]
  76. # ログ書込み
  77. with open ('log.txt', 'a') as f:
  78. f.write (f'{datetime.now ()}\t{json.dumps (chat_item.__dict__)}\t{answer}\n')
  79. # 吹出し描画(ニジカは上,ゴートうは下)
  80. screen.blit (balloon, (0, 384) if goatoh_mode else (0, 0))
  81. # 視聴者コメント描画
  82. screen.blit (
  83. user_font.render (
  84. '> ' + (message
  85. if (CommonModule.len_by_full (message)
  86. <= 21)
  87. else (CommonModule.mid_by_full (
  88. message, 0, 19.5)
  89. + '...')),
  90. True,
  91. (0, 0, 0)),
  92. (120, 70 + 384) if goatoh_mode else (120, 70))
  93. # ニジカの返答描画
  94. screen.blit (
  95. nizika_font.render (
  96. (answer
  97. if CommonModule.len_by_full (answer) <= 16
  98. else CommonModule.mid_by_full (answer, 0, 16)),
  99. True,
  100. (192, 0, 0)),
  101. (100, 150 + 384) if goatoh_mode else (100, 150))
  102. if CommonModule.len_by_full (answer) > 16:
  103. screen.blit (
  104. nizika_font.render (
  105. (CommonModule.mid_by_full (answer, 16, 16)
  106. if CommonModule.len_by_full (answer) <= 32
  107. else (CommonModule.mid_by_full (
  108. answer, 16, 14.5)
  109. + '...')),
  110. True,
  111. (192, 0, 0)),
  112. (100, 200 + 384) if goatoh_mode else (100, 200))
  113. pygame.display.update ()
  114. # 鳴く.
  115. if goatoh_mode:
  116. if random.random () < 0.1:
  117. kusa.play ()
  118. else:
  119. mumumumu.play ()
  120. else:
  121. noon.play ()
  122. time.sleep (1.5)
  123. # 返答の読上げを WAV ディタとして生成,取得
  124. try:
  125. wav: bytearray | None = Aques.main (answer, goatoh_mode)
  126. except:
  127. wav: None = None
  128. # 再生
  129. if wav is not None:
  130. with open ('./nizika_talking.wav', 'wb') as f:
  131. f.write (wav)
  132. playsound ('./nizika_talking.wav')
  133. time.sleep (10)
  134. else:
  135. # Chat オブジェクトが無効
  136. # 再生成
  137. live_chat = pytchat.create (video_id = YOUTUBE_ID)
  138. pygame.display.update ()
  139. for event in pygame.event.get ():
  140. if event.type == QUIT:
  141. pygame.quit ()
  142. sys.exit ()
  143. if __name__ == '__main__':
  144. Main.main ((len (sys.argv) > 1) and (sys.argv[1] == '-g'))