| 
				
				
				
				 | 
			
			 | 
			@@ -1,6 +1,7 @@ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from __future__ import annotations | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import asyncio | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import random | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from datetime import date, datetime, time, timedelta | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from typing import TypedDict, cast | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -9,7 +10,9 @@ from bs4 import BeautifulSoup | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from requests.exceptions import Timeout | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import queries_to_answers as q2a | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from db.models import Video, VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from db.models import Comment, Video, VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from nizika_ai.consts import Character, GPTModel, QueryType | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from nizika_ai.models import Query | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			KIRIBAN_VIEWS_COUNTS: list[int] = sorted ({ *range (1_000, 10_000, 1_000), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                            *range (10_000, 1_000_001, 10_000), | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -44,28 +47,16 @@ async def report_kiriban ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        (views_count, video_info, uploaded_at) = ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                kiriban_list.pop (random.randint (0, len (kiriban_list) - 1))) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        since_posted = datetime.now () - uploaded_at | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uri = f"https://www.nicovideo.jp/watch/{ video_info['contentId'] }" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        (title, description, thumbnail) = fetch_embed_info (uri) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            upload = client.com.atproto.repo.upload_blob ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    io.BytesIO (requests.get (thumbnail, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                              timeout = 60).content)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            thumb = upload.blob | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Timeout: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            thumb = None | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        comments = nico.get_comments (video_info['contentId']) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        video_code = video_info['contentId'] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        uri = f"https://www.nicovideo.jp/watch/{ video_code }" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        (title, description, _) = fetch_embed_info (uri) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        comments = fetch_comments (video_code) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        popular_comments = sorted (comments, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                   key      = lambda c: c.nico_count, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                   reverse  = True)[:10] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        latest_comments = sorted (comments, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                  key       = lambda c: c.posted_at, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                  reverse   = True)[:10] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        embed_external = models.AppBskyEmbedExternal.Main ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                external = models.AppBskyEmbedExternal.External ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    title       = title, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    description = description, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    thumb       = thumb, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    uri         = uri)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        prompt = f"{ since_posted.days }日と{ since_posted.seconds }秒前にニコニコに投稿された『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        prompt += f"コメント数は{ len (comments) }件です。\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if video_info['tags']: | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -78,10 +69,19 @@ async def report_kiriban ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			```html | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			{ video_info['description'] } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			``` | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			このことについて、ニジカちゃんからのお祝いメッセージを下さい。 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			ただし、そのメッセージ内には再生数の数値とその多さに応じたリアクションを添えてください。 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			また、ぜひ投稿からこの再生数に至るまでにかかった時間や、つけられたタグ、コメントに対して思いを馳せてください。 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			好きなコメントがあったら教えてね。""" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			このことについて、何かお祝いメッセージを下さい。 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			ただし、そのメッセージ内には再生数の数値を添えてください。 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			また、つけられたタグ、コメントからどのような動画か想像し、説明してください。""" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query = Query () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.user_id = None | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.target_character = Character.DEERJIKA.value | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.content = prompt | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.query_type = QueryType.KIRIBAN.value | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.model = GPTModel.GPT3_TURBO.value | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.sent_at = datetime.now () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.answered = False | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.transfer_data = { 'video_code': video_code } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        query.save () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        # 待ち時間計算 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        dt = datetime.now () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        d = dt.date () | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -195,6 +195,53 @@ def create_bs_from_url ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return BeautifulSoup (req.text, 'hecoml.parser') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			def fetch_embed_info ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        url:    str, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			) -> tuple[str, str, str]: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    title:          str = '' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    description:    str = '' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    thumbnail:      str = '' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        res = requests.get (url, timeout = 60) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    except Timeout: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return ('', '', '') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if res.status_code != 200: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return ('', '', '') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    soup = BeautifulSoup (res.text, 'html.parser') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    tmp = soup.find ('title') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if tmp is not None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        title = tmp.text | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    tmp = soup.find ('meta', attrs = { 'name': 'description' }) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if tmp is not None and hasattr (tmp, 'get'): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            description = cast (str, tmp.get ('content')) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Exception: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    tmp = soup.find ('meta', attrs = { 'name': 'thumbnail' }) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if tmp is not None and hasattr (tmp, 'get'): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        try: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            thumbnail = cast (str, tmp.get ('content')) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        except Exception: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            pass | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return (title, description, thumbnail) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			def fetch_comments ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        video_code: str, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			) -> list[Comment]: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    video = Video.where ('code', video_code).first () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if video is None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return video.comments | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			async def report_nico ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			) -> None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    ... | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |