| 
				
				
					
				
				
				 | 
			
			 | 
			@@ -5,25 +5,20 @@ AI ニジカ常時稼動バッチ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from __future__ import annotations | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import asyncio | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import json | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import os | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import random | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import subprocess | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from asyncio import Lock | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from datetime import date, datetime, time, timedelta | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from typing import cast | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from typing import Any, cast | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import nicolib | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			import queries_to_answers as q2a | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from config import DB | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from db.models import Comment, Video, VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from nicolib import VideoInfo | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from nizika_ai.consts import Character, GPTModel, QueryType | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			from nizika_ai.models import Query | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			# DB 設定 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			DB | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			for m in (Comment, Video, VideoHistory): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    m.__connection__ = 'nico' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			Query.__connection__ = 'ai' | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			KIRIBAN_VIEWS_COUNTS: list[int] = sorted ({ *range (1_000, 10_000, 1_000), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                            *range (10_000, 1_000_001, 10_000), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                            114_514, 1_940, 2_450, 5_100, | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -84,10 +79,10 @@ async def report_kiriban ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        video_code = video_info['contentId'] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        comments = fetch_comments (video_code) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        popular_comments = sorted (comments, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                   key      = lambda c: c.nico_count, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                   key      = lambda c: c['nico_count'], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                   reverse  = True)[:10] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        latest_comments = sorted (comments, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                  key       = lambda c: c.posted_at, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                  key       = lambda c: c['posted_at'], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                  reverse   = True)[:10] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        prompt = (f"{ _format_elapsed (uploaded_at) }前にニコニコに投稿された" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                  f"『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n" | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -95,7 +90,7 @@ async def report_kiriban ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if video_info['tags']: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            prompt += f"つけられたタグは「{ '」、「'.join (video_info['tags']) }」です。\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        if comments: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            prompt += f"人気のコメントは次の通りです:「{ '」、「'.join (c.content for c in popular_comments) }」\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            prompt += f"人気のコメントは次の通りです:「{ '」、「'.join (c['content'] for c in popular_comments) }」\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if latest_comments != popular_comments: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                prompt += f"最新のコメントは次の通りです:「{ '」、「'.join (c.content for c in latest_comments) }」\n" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        prompt += f""" | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -160,41 +155,22 @@ def fetch_kiriban_list ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        動画リスト(キリ番基準再生数、対象動画情報、投稿日時のタプル) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    _kiriban_list: list[tuple[int, VideoInfo, datetime]] = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    latest_fetched_at = cast (date, (VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                     .where ('fetched_at', '<=', base_date) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                     .max ('fetched_at'))) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    for kiriban_views_count in KIRIBAN_VIEWS_COUNTS: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        targets = { vh.video.code for vh in (VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                             .where ('fetched_at', latest_fetched_at) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                             .where ('views_count', '>=', kiriban_views_count) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                             .get ()) } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        for code in targets: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if code in [kiriban[1]['contentId'] for kiriban in _kiriban_list]: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            previous_views_count: int | None = ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    VideoHistory | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    .where_has ('video', lambda q, code = code: q.where ('code', code)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    .where ('fetched_at', '<', latest_fetched_at) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                    .max ('views_count')) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if previous_views_count is None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                previous_views_count = 0 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if previous_views_count >= kiriban_views_count: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                continue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            video_info = nicolib.fetch_video_info (code) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            if video_info is not None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                _kiriban_list.append ((kiriban_views_count, video_info, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                       (cast (Video, Video.where ('code', code).first ()) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                                        .uploaded_at))) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return _kiriban_list | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    result = subprocess.run ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            ['python3', str (base_date), *map (str, KIRABAN_VIEWS_COUNTS)], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            cwd             = '/root/nizika_nico', | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            env             = os.environ, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            capture_output  = True, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            text            = True) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    kl: list[list[int | str]] = json.loads (result.stdout) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return map (lambda k: (cast (int, k[0]), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                           nicolib.fetch_video_info (cast (str, k[1])), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                           datetime.strptime (cast (str, k[2]), '%Y-%m-%d %H:%M:%S.%f')), kl) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			def fetch_comments ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        video_code: str, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			) -> list[Comment]: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			) -> list[CommentDict]: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    動画のコメント・リストを取得する. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -205,14 +181,23 @@ def fetch_comments ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    Return | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    ------ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    list[Comment] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    list[CommentDict] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        コメント・リスト | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    """ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    video = Video.where ('code', video_code).first () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    if video is None: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        return [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return video.comments | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    result = subprocess.run ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            ['python3', video_code], | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            cwd             = '/root/nizika_nico', | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            env             = os.environ, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            capture_output  = True, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			            text            = True) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    rows: list[dict[str, Any]] = json.loads (result.stdout) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    comments: list[CommentDict] = [] | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    for row in comments: | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        row['posted_at'] = datetime.strptime (row['posted_at'], '%Y-%m-%d %H:%M:%S.%f') | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        comments.append (cast (CommentDict, row)) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return comments | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			def fetch_latest_deerjika ( | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -339,6 +324,17 @@ def _format_elapsed ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return f"{ days }日{ hours }時間{ mins }分{ seconds }秒" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			class CommentDict (TypedDict): | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    id:         int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    video_id:   int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    comment_no: int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    user_id:    int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    content:    str | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    posted_at:  datetime | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    nico_count: int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    vpos_ms:    int | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			kiriban_list = ( | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			        fetch_kiriban_list ((now := datetime.now ()).date () | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			                            - timedelta (days = 1 if now.hour < 15 else 0))) | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |