伊地知ニジカ放送局だぬ゛ん゛. 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.

test.py 22 KiB

4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
4 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. from __future__ import annotations
  2. import math
  3. import os
  4. import random
  5. import sys
  6. import wave
  7. from datetime import datetime, timedelta
  8. from enum import Enum, auto
  9. from typing import Callable, TypedDict
  10. import cv2
  11. import emoji
  12. import ephem
  13. import pygame
  14. import pygame.gfxdraw
  15. import pytchat
  16. import requests
  17. from cv2 import VideoCapture
  18. from ephem import Moon, Observer, Sun
  19. from pygame import Rect, Surface
  20. from pygame.font import Font
  21. from pygame.mixer import Sound
  22. from pygame.time import Clock
  23. from pytchat.core.pytchat import PytchatCore
  24. from pytchat.processors.default.processor import Chat
  25. from aques import Aques
  26. from common_module import CommonModule
  27. from nizika_ai.config import DB
  28. from nizika_ai.consts import (AnswerType, Character, GPTModel, Platform,
  29. QueryType)
  30. from nizika_ai.models import Answer, AnsweredFlag, Query, User
  31. pygame.init ()
  32. FPS = 30
  33. SYSTEM_FONT = pygame.font.SysFont ('notosanscjkjp', 24, bold = True)
  34. USER_FONT = pygame.font.SysFont ('notosanscjkjp', 32, italic = True)
  35. DEERJIKA_FONT = pygame.font.SysFont ('07nikumarufont', 50)
  36. def main (
  37. ) -> None:
  38. game = Game ()
  39. Bg (game)
  40. balloon = Balloon (game)
  41. deerjika = Deerjika (game, DeerjikaPattern.RELAXED,
  42. x = CWindow.WIDTH * 3 / 4,
  43. y = CWindow.HEIGHT - 120,
  44. balloon = balloon)
  45. CurrentTime (game, SYSTEM_FONT)
  46. broadcast = Broadcast (os.environ['BROADCAST_CODE'])
  47. try:
  48. Sound ('assets/bgm.mp3').play (loops = -1)
  49. except Exception:
  50. pass
  51. while True:
  52. for event in pygame.event.get ():
  53. if event.type == pygame.QUIT:
  54. pygame.quit ()
  55. sys.exit ()
  56. if not balloon.enabled:
  57. DB.begin_transaction ()
  58. answer_flags = (AnsweredFlag.where ('platform', Platform.YOUTUBE.value)
  59. .where ('answered', False)
  60. .get ())
  61. if answer_flags:
  62. answer_flag = random.choice (answer_flags)
  63. answer = Answer.find (answer_flag.answer_id)
  64. if answer.answer_type == AnswerType.YOUTUBE_REPLY.value:
  65. query = Query.find (answer.query_id)
  66. deerjika.talk (query.content, answer.content)
  67. answer_flag.answered = True
  68. answer_flag.save ()
  69. DB.commit ()
  70. add_query (broadcast)
  71. game.redraw ()
  72. class Bg:
  73. """
  74. 背景オブゼクト管理用クラス
  75. Attributes:
  76. base (BgBase): 最背面
  77. grass (BgGrass): 草原部分
  78. jojoko (Jojoko): 大月ヨヨコ
  79. kita (KitaSun): き太く陽
  80. """
  81. base: BgBase
  82. grass: BgGrass
  83. jojoko: Jojoko
  84. kita: KitaSun
  85. def __init__ (
  86. self,
  87. game: Game,
  88. ):
  89. self.base = BgBase (game)
  90. self.jojoko = Jojoko (game)
  91. self.kita = KitaSun (game)
  92. self.grass = BgGrass (game)
  93. class DeerjikaPattern (Enum):
  94. """
  95. ニジカの状態
  96. Members:
  97. NORMAL: 通常
  98. RELAXED: 足パタパタ
  99. SLEEPING: 寝ニジカ
  100. DANCING: ダンシング・ニジカ
  101. """
  102. NORMAL = auto ()
  103. RELAXED = auto ()
  104. SLEEPING = auto ()
  105. DANCING = auto ()
  106. class Direction (Enum):
  107. """
  108. クリーチャの向き
  109. Members:
  110. LEFT: 左向き
  111. RIGHT: 右向き
  112. """
  113. LEFT = auto ()
  114. RIGHT = auto ()
  115. class Game:
  116. """
  117. ゲーム・クラス
  118. Attributes:
  119. clock (Clock): Clock オブゼクト
  120. frame (int): フレーム・カウンタ
  121. last_answered_at (datetime): 最後に回答した時刻
  122. now (datetime): 基準日時
  123. redrawers (list[Redrawer]): 再描画するクラスのリスト
  124. screen (Surface): 基底スクリーン
  125. sky (Sky): 天体情報
  126. """
  127. clock: Clock
  128. frame: int
  129. last_answered_at: datetime
  130. now: datetime
  131. redrawers: list[Redrawer]
  132. screen: Surface
  133. sky: Sky
  134. def __init__ (
  135. self,
  136. ):
  137. self.now = datetime.now ()
  138. self.screen = pygame.display.set_mode ((CWindow.WIDTH, CWindow.HEIGHT))
  139. self.clock = Clock ()
  140. self.frame = 0
  141. self.redrawers = []
  142. self._create_sky ()
  143. def redraw (
  144. self,
  145. ) -> None:
  146. self.now = datetime.now ()
  147. self.sky.observer.date = self.now - timedelta (hours = 9)
  148. for redrawer in sorted (self.redrawers, key = lambda x: x['layer']):
  149. if redrawer['obj'].enabled:
  150. redrawer['obj'].redraw ()
  151. pygame.display.update ()
  152. self.clock.tick (FPS)
  153. def _create_sky (
  154. self,
  155. ) -> None:
  156. self.sky = Sky ()
  157. self.sky.observer = Observer ()
  158. self.sky.observer.lat = '35'
  159. self.sky.observer.lon = '139'
  160. class GameObject:
  161. """
  162. 各ゲーム・オブゼクトの基底クラス
  163. Attributes:
  164. arg (float): 回転角度 (rad)
  165. ax (float): X 軸に対する加速度 (px/frame^2)
  166. ay (float): y 軸に対する加速度 (px/frame^2)
  167. enabled (bool): オブゼクトの表示可否
  168. frame (int): フレーム・カウンタ
  169. game (Game): ゲーム基盤
  170. height (int): 高さ (px)
  171. vx (float): x 軸に対する速度 (px/frame)
  172. vy (float): y 軸に対する速度 (px/frame)
  173. width (int): 幅 (px)
  174. x (float): X 座標 (px)
  175. y (float): Y 座標 (px)
  176. """
  177. arg: float = 0
  178. ax: float = 0
  179. ay: float = 0
  180. enabled: bool = True
  181. frame: int
  182. game: Game
  183. height: int
  184. vx: float = 0
  185. vy: float = 0
  186. width: int
  187. x: float
  188. y: float
  189. def __init__ (
  190. self,
  191. game: Game,
  192. layer: int | None = None,
  193. enabled: bool = True,
  194. x: float = 0,
  195. y: float = 0,
  196. ):
  197. self.game = game
  198. self.enabled = enabled
  199. self.frame = 0
  200. if layer is None:
  201. if self.game.redrawers:
  202. layer = max (r['layer'] for r in self.game.redrawers) + 10
  203. else:
  204. layer = 0
  205. self.game.redrawers.append ({ 'layer': layer, 'obj': self })
  206. self.x = x
  207. self.y = y
  208. def redraw (
  209. self,
  210. ) -> None:
  211. self.x += self.vx
  212. self.y += self.vy
  213. self.vx += self.ax
  214. self.vy += self.ay
  215. self.frame += 1
  216. class BgBase (GameObject):
  217. """
  218. 背景
  219. Attributes:
  220. surface (Surface): 背景 Surface
  221. """
  222. surface: Surface
  223. def __init__ (
  224. self,
  225. game: Game,
  226. ):
  227. super ().__init__ (game)
  228. self.surface = pygame.image.load ('assets/bg.jpg')
  229. self.surface = pygame.transform.scale (self.surface, (CWindow.WIDTH, CWindow.HEIGHT))
  230. def redraw (
  231. self,
  232. ) -> None:
  233. self.game.screen.blit (self.surface, (self.x, self.y))
  234. super ().redraw ()
  235. class BgGrass (GameObject):
  236. """
  237. 背景の草原部分
  238. Attributes:
  239. surface (Surface): 草原 Surface
  240. """
  241. surface: Surface
  242. def __init__ (
  243. self,
  244. game: Game,
  245. ):
  246. super ().__init__ (game)
  247. self.game = game
  248. self.surface = pygame.image.load ('assets/bg-grass.png')
  249. self.surface = pygame.transform.scale (self.surface, (CWindow.WIDTH, CWindow.HEIGHT))
  250. def redraw (
  251. self,
  252. ) -> None:
  253. self.game.screen.blit (self.surface, (self.x, self.y))
  254. super ().redraw ()
  255. class Creature (GameObject):
  256. sound: Sound
  257. def bell (
  258. self,
  259. ) -> None:
  260. self.sound.play ()
  261. class Deerjika (Creature):
  262. """
  263. 伊地知ニジカ
  264. Attributes:
  265. height (int): 高さ (px)
  266. scale (float): 拡大率
  267. surfaces (list[Surface]): ニジカの各フレームを Surface にしたリスト
  268. width (int): 幅 (px)
  269. """
  270. height: int
  271. scale: float = .8
  272. surfaces: list[Surface]
  273. width: int
  274. talking: bool = False
  275. wav: bytearray | None = None
  276. balloon: Balloon
  277. def __init__ (
  278. self,
  279. game: Game,
  280. pattern: DeerjikaPattern = DeerjikaPattern.NORMAL,
  281. direction: Direction = Direction.LEFT,
  282. layer: int | None = None,
  283. x: float = 0,
  284. y: float = 0,
  285. balloon: Balloon | None = None,
  286. ):
  287. if balloon is None:
  288. raise Exception
  289. super ().__init__ (game, layer, x = x, y = y)
  290. self.pattern = pattern
  291. self.direction = direction
  292. self.balloon = balloon
  293. match pattern:
  294. case DeerjikaPattern.NORMAL:
  295. ...
  296. case DeerjikaPattern.RELAXED:
  297. match direction:
  298. case Direction.LEFT:
  299. self.width = 1280
  300. self.height = 720
  301. surface = pygame.image.load ('assets/deerjika_relax_left.png')
  302. self.surfaces = []
  303. for x in range (0, surface.get_width (), self.width):
  304. self.surfaces.append (
  305. surface.subsurface (x, 0, self.width, self.height))
  306. case Direction.RIGHT:
  307. ...
  308. self.sound = Sound ('assets/noon.wav')
  309. def redraw (
  310. self,
  311. ) -> None:
  312. surface = pygame.transform.scale (self.surfaces[self.frame % len (self.surfaces)],
  313. (self.width * self.scale, self.height * self.scale))
  314. self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y)))
  315. super ().redraw ()
  316. if (not self.balloon.enabled) and self.talking:
  317. self.talking = False
  318. if (self.balloon.enabled and self.balloon.frame >= FPS * 3
  319. and not self.talking):
  320. self.read_out ()
  321. def talk (
  322. self,
  323. query: str,
  324. answer: str,
  325. ) -> None:
  326. self.bell ()
  327. self._create_wav (answer)
  328. length = 300
  329. if self.wav is not None:
  330. with wave.open ('./nizika_talking.wav', 'rb') as f:
  331. length = int (FPS * (f.getnframes () / f.getframerate () + 4))
  332. self.balloon.talk (query, answer, length = length)
  333. def read_out (
  334. self,
  335. ) -> None:
  336. Sound ('./nizika_talking.wav').play ()
  337. self.talking = True
  338. def _create_wav (
  339. self,
  340. message: str,
  341. ) -> None:
  342. try:
  343. self.wav = Aques.main (message, False)
  344. except:
  345. self.wav = None
  346. if self.wav is None:
  347. return
  348. with open ('./nizika_talking.wav', 'wb') as f:
  349. f.write (self.wav)
  350. class CurrentTime (GameObject):
  351. """
  352. 現在日時表示
  353. Attributes:
  354. font (Font): フォント
  355. """
  356. font: Font
  357. def __init__ (
  358. self,
  359. game: Game,
  360. font: Font,
  361. ):
  362. super ().__init__ (game)
  363. self.font = font
  364. def redraw (
  365. self,
  366. ) -> None:
  367. for i in range (4):
  368. self.game.screen.blit (
  369. self.font.render (str (self.game.now), True, (0, 0, 0)),
  370. (i % 2, i // 2 * 2))
  371. super ().redraw ()
  372. class Balloon (GameObject):
  373. """
  374. 吹出し
  375. Attributes:
  376. answer (str): 回答テキスト
  377. image_url (str, None): 画像 URL
  378. length (int): 表示する時間 (frame)
  379. query (str): 質問テキスト
  380. surface (Surface): 吹出し Surface
  381. x_flip (bool): 左右反転フラグ
  382. y_flip (bool): 上下反転フラグ
  383. """
  384. answer: str = ''
  385. image_url: str | None = None
  386. length: int = 300
  387. query: str = ''
  388. surface: Surface
  389. x_flip: bool = False
  390. y_flip: bool = False
  391. def __init__ (
  392. self,
  393. game: Game,
  394. x_flip: bool = False,
  395. y_flip: bool = False,
  396. ):
  397. super ().__init__ (game, enabled = False)
  398. self.x_flip = x_flip
  399. self.y_flip = y_flip
  400. self.surface = pygame.transform.scale (pygame.image.load ('assets/balloon.png'),
  401. (CWindow.WIDTH, CWindow.HEIGHT / 2))
  402. self.surface = pygame.transform.flip (self.surface, self.x_flip, self.y_flip)
  403. def redraw (
  404. self,
  405. ) -> None:
  406. if self.frame >= self.length:
  407. self.enabled = False
  408. self.game.last_answered_at = self.game.now
  409. return
  410. query = self.query
  411. if CommonModule.len_by_full (query) > 21:
  412. query = CommonModule.mid_by_full (query, 0, 19.5) + '...'
  413. answer = Surface ((800, ((CommonModule.len_by_full (self.answer) - 1) // 16 + 1) * 50),
  414. pygame.SRCALPHA)
  415. for i in range (int (CommonModule.len_by_full (self.answer) - 1) // 16 + 1):
  416. answer.blit (DEERJIKA_FONT.render (
  417. CommonModule.mid_by_full (self.answer, 16 * i, 16), True, (192, 0, 0)),
  418. (0, 50 * i))
  419. surface = self.surface.copy ()
  420. surface.blit (USER_FONT.render ('>' + query, True, (0, 0, 0)), (120, 70))
  421. y: int
  422. if self.frame < 30:
  423. y = 0
  424. elif self.frame >= self.length - 90:
  425. y = answer.get_height () - 100
  426. else:
  427. y = int ((answer.get_height () - 100) * (self.frame - 30) / (self.length - 120))
  428. surface.blit (answer, (100, 150), Rect (0, y, 800, 100))
  429. self.game.screen.blit (surface, (0, 0))
  430. super ().redraw ()
  431. def talk (
  432. self,
  433. query: str,
  434. answer: str,
  435. image_url: str | None = None,
  436. length: int = 300,
  437. ) -> None:
  438. self.query = query
  439. self.answer = answer
  440. self.image_url = image_url
  441. self.length = length
  442. self.frame = 0
  443. self.enabled = True
  444. class KitaSun (GameObject):
  445. """
  446. き太く陽
  447. Attributes:
  448. sun (Sun): ephem の太陽オブゼクト
  449. surface (Surface): き太く陽 Surface
  450. """
  451. alt: float
  452. az: float
  453. sun: Sun
  454. surface: Surface
  455. def __init__ (
  456. self,
  457. game: Game,
  458. ):
  459. super ().__init__ (game)
  460. self.surface = pygame.transform.scale (pygame.image.load ('assets/sun.png'), (200, 200))
  461. self.sun = Sun ()
  462. def redraw (
  463. self,
  464. ) -> None:
  465. surface = pygame.transform.rotate (self.surface, -(90 + math.degrees (self.arg)))
  466. self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y)))
  467. super ().redraw ()
  468. self.sun.compute (self.game.sky.observer)
  469. self.alt = self.sun.alt
  470. self.az = self.sun.az
  471. if abs (self.new_arg - self.arg) > math.radians (15):
  472. self.arg = self.new_arg
  473. self.x = self.new_x
  474. self.y = self.new_y
  475. @property
  476. def new_x (
  477. self,
  478. ) -> float:
  479. return CWindow.WIDTH * (math.degrees (self.az) - 80) / 120
  480. @property
  481. def new_y (
  482. self,
  483. ) -> float:
  484. return ((CWindow.HEIGHT / 2)
  485. - ((CWindow.HEIGHT / 2 + 100) * math.sin (self.alt)
  486. / math.sin (math.radians (60))))
  487. @property
  488. def new_arg (
  489. self,
  490. ) -> float:
  491. return math.atan2 (self.new_y - self.y, self.new_x - self.x)
  492. class Jojoko (GameObject):
  493. """
  494. 大月ヨヨコ
  495. Attributes:
  496. base (Surface): 満月ヨヨコ Surface
  497. moon (Moon): ephem の月オブゼクト
  498. surface (Surface): 缺けたヨヨコ
  499. """
  500. alt: float
  501. az: float
  502. base: Surface
  503. moon: Moon
  504. surface: Surface
  505. def __init__ (
  506. self,
  507. game: Game,
  508. ):
  509. super ().__init__ (game)
  510. self.base = pygame.transform.scale (pygame.image.load ('assets/moon.png'), (200, 200))
  511. self.moon = Moon ()
  512. self.surface = self._get_surface ()
  513. def redraw (
  514. self,
  515. ) -> None:
  516. if self.frame % (FPS * 3600) == 0:
  517. self.surface = self._get_surface ()
  518. surface = pygame.transform.rotate (self.surface, -(90 + math.degrees (self.arg)))
  519. surface.set_colorkey ((0, 255, 0))
  520. self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y)))
  521. super ().redraw ()
  522. self.moon.compute (self.game.sky.observer)
  523. self.alt = self.moon.alt
  524. self.az = self.moon.az
  525. if abs (self.new_arg - self.arg) > math.radians (15):
  526. self.arg = self.new_arg
  527. self.x = self.new_x
  528. self.y = self.new_y
  529. @property
  530. def phase (
  531. self,
  532. ) -> float:
  533. dt: datetime = ephem.localtime (ephem.previous_new_moon (self.game.sky.observer.date))
  534. return (self.game.now - dt).total_seconds () / 60 / 60 / 24
  535. def _get_surface (
  536. self,
  537. ) -> Surface:
  538. """
  539. ヨヨコを月齢に応じて缺かす.
  540. Returns:
  541. Surface: 缺けたヨヨコ
  542. """
  543. jojoko = self.base.copy ()
  544. for i in range (200):
  545. if 1 <= self.phase < 15:
  546. pygame.gfxdraw.bezier (jojoko, ((0, 100 + i), (100, 180 * self.phase / 7 - 80 + i), (200, 100 + i)), 3, (0, 255, 0))
  547. elif self.phase < 16:
  548. pass
  549. elif self.phase < 30:
  550. pygame.gfxdraw.bezier (jojoko, ((0, 100 - i), (100, 180 * (self.phase - 15) / 7 - 80 - i), (200, 100 - i)), 3, (0, 255, 0))
  551. else:
  552. jojoko.fill ((0, 255, 0))
  553. return jojoko
  554. @property
  555. def new_x (
  556. self,
  557. ) -> float:
  558. return CWindow.WIDTH * (math.degrees (self.az) - 80) / 120
  559. @property
  560. def new_y (
  561. self,
  562. ) -> float:
  563. return ((CWindow.HEIGHT / 2)
  564. - ((CWindow.HEIGHT / 2 + 100) * math.sin (self.alt)
  565. / math.sin (math.radians (60))))
  566. @property
  567. def new_arg (
  568. self,
  569. ) -> float:
  570. return math.atan2 (self.new_y - self.y, self.new_x - self.x)
  571. class Sky:
  572. """
  573. 天体に関する情報を保持するクラス
  574. Attributes:
  575. observer (Observer): 観測値
  576. """
  577. observer: Observer
  578. class CWindow:
  579. """
  580. ウィンドゥに関する定数クラス
  581. Attributes:
  582. WIDTH (int): ウィンドゥ幅
  583. HEIGHT (int): ウィンドゥ高さ
  584. """
  585. WIDTH = 1024
  586. HEIGHT = 768
  587. class Redrawer (TypedDict):
  588. """
  589. 再描画処理を行ふゲーム・オブゼクトとその優先順位のペア
  590. Attributes:
  591. layer (int): レイア
  592. obj (GameObject): ゲーム・オブゼクト
  593. """
  594. layer: int
  595. obj: GameObject
  596. def get_surfaces_from_video (
  597. video_path: str,
  598. ) -> list[Surface]:
  599. cap = VideoCapture (video_path)
  600. if not cap.isOpened ():
  601. return []
  602. fps = cap.get (cv2.CAP_PROP_FPS)
  603. surfaces: list[Surface] = []
  604. while cap.isOpened ():
  605. (ret, frame) = cap.read ()
  606. if not ret:
  607. break
  608. frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)
  609. frame_surface = pygame.surfarray.make_surface (frame)
  610. frame_surface = pygame.transform.rotate (frame_surface, -90)
  611. surfaces.append (pygame.transform.flip (frame_surface, True, False))
  612. cap.release ()
  613. return surfaces
  614. class Broadcast:
  615. chat: PytchatCore
  616. def __init__ (
  617. self,
  618. broadcast_code,
  619. ):
  620. self.chat = pytchat.create (broadcast_code)
  621. def fetch_chat (
  622. self,
  623. ) -> Chat | None:
  624. if not self.chat.is_alive ():
  625. return None
  626. chats = self.chat.get ().items
  627. print (chats)
  628. if not chats:
  629. return None
  630. return random.choice (chats)
  631. class Log:
  632. ...
  633. def fetch_bytes_from_url (
  634. url: str,
  635. ) -> bytes | None:
  636. res = requests.get (url, timeout = 60)
  637. if res.status_code != 200:
  638. return None
  639. return res.content
  640. def add_query (
  641. broadcast: Broadcast,
  642. ) -> None:
  643. chat = broadcast.fetch_chat ()
  644. if chat is None:
  645. return
  646. DB.begin_transaction ()
  647. chat.message = emoji.emojize (chat.message)
  648. message: str = chat.message
  649. user = (User.where ('platform', Platform.YOUTUBE.value)
  650. .where ('code', chat.author.channelId)
  651. .first ())
  652. if user is None:
  653. user = User ()
  654. user.platform = Platform.YOUTUBE.value
  655. user.code = chat.author.channelId
  656. user.name = chat.author.name
  657. user.icon = fetch_bytes_from_url (chat.author.imageUrl)
  658. user.save ()
  659. query = Query ()
  660. query.user_id = user.id
  661. query.target_character = Character.DEERJIKA.value
  662. query.content = chat.message
  663. query.query_type = QueryType.YOUTUBE_COMMENT.value
  664. query.model = GPTModel.GPT3_TURBO.value
  665. query.sent_at = datetime.now ()
  666. query.answerd = False
  667. query.save ()
  668. DB.commit ()
  669. if __name__ == '__main__':
  670. main ()