|
@@ -1,10 +1,14 @@ |
|
|
|
|
|
""" |
|
|
|
|
|
AI ニジカ常時稼動バッチ |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
from __future__ import annotations |
|
|
|
|
|
|
|
|
import asyncio |
|
|
import asyncio |
|
|
import random |
|
|
import random |
|
|
from asyncio import Lock |
|
|
from asyncio import Lock |
|
|
from datetime import date, datetime, time, timedelta |
|
|
from datetime import date, datetime, time, timedelta |
|
|
from typing import TypedDict, cast |
|
|
|
|
|
|
|
|
from typing import cast |
|
|
|
|
|
|
|
|
import nicolib |
|
|
import nicolib |
|
|
import queries_to_answers as q2a |
|
|
import queries_to_answers as q2a |
|
@@ -21,11 +25,18 @@ KIRIBAN_VIEWS_COUNTS: list[int] = sorted ({ *range (1_000, 10_000, 1_000), |
|
|
reverse = True) |
|
|
reverse = True) |
|
|
|
|
|
|
|
|
kiriban_list: list[tuple[int, VideoInfo, datetime]] |
|
|
kiriban_list: list[tuple[int, VideoInfo, datetime]] |
|
|
|
|
|
|
|
|
|
|
|
watched_videos: set[str] = set () |
|
|
|
|
|
|
|
|
lock = Lock () |
|
|
lock = Lock () |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def main ( |
|
|
async def main ( |
|
|
) -> None: |
|
|
) -> None: |
|
|
|
|
|
""" |
|
|
|
|
|
メーン処理 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
await asyncio.gather ( |
|
|
await asyncio.gather ( |
|
|
queries_to_answers (), |
|
|
queries_to_answers (), |
|
|
report_kiriban (), |
|
|
report_kiriban (), |
|
@@ -35,6 +46,10 @@ async def main ( |
|
|
|
|
|
|
|
|
async def queries_to_answers ( |
|
|
async def queries_to_answers ( |
|
|
) -> None: |
|
|
) -> None: |
|
|
|
|
|
""" |
|
|
|
|
|
クエリ処理 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
while True: |
|
|
while True: |
|
|
loop = asyncio.get_running_loop () |
|
|
loop = asyncio.get_running_loop () |
|
|
await loop.run_in_executor (None, q2a.main) |
|
|
await loop.run_in_executor (None, q2a.main) |
|
@@ -43,6 +58,10 @@ async def queries_to_answers ( |
|
|
|
|
|
|
|
|
async def report_kiriban ( |
|
|
async def report_kiriban ( |
|
|
) -> None: |
|
|
) -> None: |
|
|
|
|
|
""" |
|
|
|
|
|
キリ番祝ひ |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
while True: |
|
|
while True: |
|
|
if not kiriban_list: |
|
|
if not kiriban_list: |
|
|
await wait_until (time (15, 0)) |
|
|
await wait_until (time (15, 0)) |
|
@@ -53,15 +72,7 @@ async def report_kiriban ( |
|
|
(views_count, video_info, uploaded_at) = ( |
|
|
(views_count, video_info, uploaded_at) = ( |
|
|
kiriban_list.pop (random.randint (0, len (kiriban_list) - 1))) |
|
|
kiriban_list.pop (random.randint (0, len (kiriban_list) - 1))) |
|
|
|
|
|
|
|
|
since_posted = datetime.now () - uploaded_at |
|
|
|
|
|
_days = since_posted.days |
|
|
|
|
|
_seconds = since_posted.seconds |
|
|
|
|
|
(_hours, _seconds) = divmod (_seconds, 3600) |
|
|
|
|
|
(_mins, _seconds) = divmod (_seconds, 60) |
|
|
|
|
|
|
|
|
|
|
|
video_code = video_info['contentId'] |
|
|
video_code = video_info['contentId'] |
|
|
uri = f"https://www.nicovideo.jp/watch/{ video_code }" |
|
|
|
|
|
(title, description, _) = nicolib.fetch_embed_info (uri) |
|
|
|
|
|
comments = fetch_comments (video_code) |
|
|
comments = fetch_comments (video_code) |
|
|
popular_comments = sorted (comments, |
|
|
popular_comments = sorted (comments, |
|
|
key = lambda c: c.nico_count, |
|
|
key = lambda c: c.nico_count, |
|
@@ -69,8 +80,9 @@ async def report_kiriban ( |
|
|
latest_comments = sorted (comments, |
|
|
latest_comments = sorted (comments, |
|
|
key = lambda c: c.posted_at, |
|
|
key = lambda c: c.posted_at, |
|
|
reverse = True)[:10] |
|
|
reverse = True)[:10] |
|
|
prompt = f"{ _days }日{ _hours }時間{ _mins }分{ _seconds }秒前にニコニコに投稿された『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n" |
|
|
|
|
|
prompt += f"コメント数は{ len (comments) }件です。\n" |
|
|
|
|
|
|
|
|
prompt = (f"{ _format_elapsed (uploaded_at) }前にニコニコに投稿された" |
|
|
|
|
|
f"『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n" |
|
|
|
|
|
f"コメント数は{ len (comments) }件です。\n") |
|
|
if video_info['tags']: |
|
|
if video_info['tags']: |
|
|
prompt += f"つけられたタグは「{ '」、「'.join (video_info['tags']) }」です。\n" |
|
|
prompt += f"つけられたタグは「{ '」、「'.join (video_info['tags']) }」です。\n" |
|
|
if comments: |
|
|
if comments: |
|
@@ -111,6 +123,10 @@ async def report_kiriban ( |
|
|
|
|
|
|
|
|
async def update_kiriban_list ( |
|
|
async def update_kiriban_list ( |
|
|
) -> None: |
|
|
) -> None: |
|
|
|
|
|
""" |
|
|
|
|
|
キリ番リストの更新 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
while True: |
|
|
while True: |
|
|
await wait_until (time (15, 0)) |
|
|
await wait_until (time (15, 0)) |
|
|
|
|
|
|
|
@@ -129,6 +145,20 @@ async def update_kiriban_list ( |
|
|
def fetch_kiriban_list ( |
|
|
def fetch_kiriban_list ( |
|
|
base_date: date, |
|
|
base_date: date, |
|
|
) -> list[tuple[int, VideoInfo, datetime]]: |
|
|
) -> list[tuple[int, VideoInfo, datetime]]: |
|
|
|
|
|
""" |
|
|
|
|
|
キリ番を迎へた動画のリストを取得する. |
|
|
|
|
|
|
|
|
|
|
|
Parameters |
|
|
|
|
|
---------- |
|
|
|
|
|
base_date: date |
|
|
|
|
|
基準日 |
|
|
|
|
|
|
|
|
|
|
|
Return |
|
|
|
|
|
------ |
|
|
|
|
|
list[tuple[int, VideoInfo, datetime]] |
|
|
|
|
|
動画リスト(キリ番基準再生数、対象動画情報、投稿日時のタプル) |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
_kiriban_list: list[tuple[int, VideoInfo, datetime]] = [] |
|
|
_kiriban_list: list[tuple[int, VideoInfo, datetime]] = [] |
|
|
|
|
|
|
|
|
latest_fetched_at = cast (date, (VideoHistory |
|
|
latest_fetched_at = cast (date, (VideoHistory |
|
@@ -145,7 +175,7 @@ def fetch_kiriban_list ( |
|
|
continue |
|
|
continue |
|
|
previous_views_count: int | None = ( |
|
|
previous_views_count: int | None = ( |
|
|
VideoHistory |
|
|
VideoHistory |
|
|
.where_has ('video', lambda q: q.where ('code', code)) |
|
|
|
|
|
|
|
|
.where_has ('video', lambda q, code = code: q.where ('code', code)) |
|
|
.where ('fetched_at', '<', latest_fetched_at) |
|
|
.where ('fetched_at', '<', latest_fetched_at) |
|
|
.max ('views_count')) |
|
|
.max ('views_count')) |
|
|
if previous_views_count is None: |
|
|
if previous_views_count is None: |
|
@@ -155,7 +185,8 @@ def fetch_kiriban_list ( |
|
|
video_info = nicolib.fetch_video_info (code) |
|
|
video_info = nicolib.fetch_video_info (code) |
|
|
if video_info is not None: |
|
|
if video_info is not None: |
|
|
_kiriban_list.append ((kiriban_views_count, video_info, |
|
|
_kiriban_list.append ((kiriban_views_count, video_info, |
|
|
cast (Video, Video.where ('code', code).first ()).uploaded_at)) |
|
|
|
|
|
|
|
|
(cast (Video, Video.where ('code', code).first ()) |
|
|
|
|
|
.uploaded_at))) |
|
|
|
|
|
|
|
|
return _kiriban_list |
|
|
return _kiriban_list |
|
|
|
|
|
|
|
@@ -163,20 +194,87 @@ def fetch_kiriban_list ( |
|
|
def fetch_comments ( |
|
|
def fetch_comments ( |
|
|
video_code: str, |
|
|
video_code: str, |
|
|
) -> list[Comment]: |
|
|
) -> list[Comment]: |
|
|
|
|
|
""" |
|
|
|
|
|
動画のコメント・リストを取得する. |
|
|
|
|
|
|
|
|
|
|
|
Parameters |
|
|
|
|
|
---------- |
|
|
|
|
|
video_code: str |
|
|
|
|
|
ニコニコの動画コード |
|
|
|
|
|
|
|
|
|
|
|
Return |
|
|
|
|
|
------ |
|
|
|
|
|
list[Comment] |
|
|
|
|
|
コメント・リスト |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
video = Video.where ('code', video_code).first () |
|
|
video = Video.where ('code', video_code).first () |
|
|
if video is None: |
|
|
if video is None: |
|
|
return [] |
|
|
return [] |
|
|
return video.comments |
|
|
return video.comments |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_latest_deerjika ( |
|
|
|
|
|
) -> VideoInfo | None: |
|
|
|
|
|
""" |
|
|
|
|
|
最新のぼざクリ動画を取得する. |
|
|
|
|
|
|
|
|
|
|
|
Return |
|
|
|
|
|
------ |
|
|
|
|
|
VideoInfo | None |
|
|
|
|
|
動画情報 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
return nicolib.fetch_latest_video (['伊地知ニジカ', |
|
|
|
|
|
'ぼざろクリーチャーシリーズ', |
|
|
|
|
|
'ぼざろクリーチャーシリーズ外伝']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def report_nico ( |
|
|
async def report_nico ( |
|
|
) -> None: |
|
|
) -> None: |
|
|
... |
|
|
|
|
|
|
|
|
""" |
|
|
|
|
|
ニコニコから最新のぼざクリを取得し,まだ報知してゐなかったら報知する. |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
while True: |
|
|
|
|
|
latest_deerjika = fetch_latest_deerjika () |
|
|
|
|
|
if latest_deerjika and latest_deerjika['contentId'] not in watched_videos: |
|
|
|
|
|
video = latest_deerjika |
|
|
|
|
|
watched_videos.add (video['contentId']) |
|
|
|
|
|
|
|
|
|
|
|
prompt = f"""ニコニコに『{ video['title'] }』という動画がアップされました。 |
|
|
|
|
|
つけられたタグは「{ '」、「'.join (video['tags']) }」です。 |
|
|
|
|
|
概要には次のように書かれています: |
|
|
|
|
|
```html |
|
|
|
|
|
{ video['description'] } |
|
|
|
|
|
``` |
|
|
|
|
|
このことについて、みんなに告知するとともに、ニジカちゃんの感想を教えてください。""" |
|
|
|
|
|
query = Query () |
|
|
|
|
|
query.user_id = None |
|
|
|
|
|
query.target_character = Character.DEERJIKA.value |
|
|
|
|
|
query.content = prompt |
|
|
|
|
|
query.query_type = QueryType.NICO_REPORT.value |
|
|
|
|
|
query.model = GPTModel.GPT3_TURBO.value |
|
|
|
|
|
query.sent_at = datetime.now () |
|
|
|
|
|
query.answered = False |
|
|
|
|
|
query.transfer_data = { 'video_code': video['contentId'] } |
|
|
|
|
|
query.save () |
|
|
|
|
|
|
|
|
|
|
|
await asyncio.sleep (60) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def wait_until ( |
|
|
async def wait_until ( |
|
|
t: time, |
|
|
t: time, |
|
|
): |
|
|
|
|
|
|
|
|
) -> None: |
|
|
|
|
|
""" |
|
|
|
|
|
指定した時刻まで待つ. |
|
|
|
|
|
|
|
|
|
|
|
Parameters |
|
|
|
|
|
---------- |
|
|
|
|
|
t: time |
|
|
|
|
|
次に実行を続行するまでの時刻 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
dt = datetime.now () |
|
|
dt = datetime.now () |
|
|
d = dt.date () |
|
|
d = dt.date () |
|
|
if dt.time () >= t: |
|
|
if dt.time () >= t: |
|
@@ -184,9 +282,35 @@ async def wait_until ( |
|
|
await asyncio.sleep ((datetime.combine (d, t) - dt).total_seconds ()) |
|
|
await asyncio.sleep ((datetime.combine (d, t) - dt).total_seconds ()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _format_elapsed ( |
|
|
|
|
|
uploaded_at: datetime, |
|
|
|
|
|
) -> str: |
|
|
|
|
|
""" |
|
|
|
|
|
指定した時刻から現在までの時間を見やすぃ文字列に変換する. |
|
|
|
|
|
|
|
|
|
|
|
Parameters |
|
|
|
|
|
---------- |
|
|
|
|
|
uploaded_at: datetime |
|
|
|
|
|
基準日時 |
|
|
|
|
|
|
|
|
|
|
|
Return |
|
|
|
|
|
------ |
|
|
|
|
|
str |
|
|
|
|
|
変換後文字列 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
delta = datetime.now () - uploaded_at |
|
|
|
|
|
days = delta.days |
|
|
|
|
|
seconds = delta.seconds |
|
|
|
|
|
(hours, seconds) = divmod (seconds, 3600) |
|
|
|
|
|
(mins, seconds) = divmod (seconds, 60) |
|
|
|
|
|
|
|
|
|
|
|
return f"{ days }日{ hours }時間{ mins }分{ seconds }秒" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kiriban_list = ( |
|
|
kiriban_list = ( |
|
|
fetch_kiriban_list ((d := datetime.now ()).date () |
|
|
|
|
|
- timedelta (days = d.hour < 15))) |
|
|
|
|
|
|
|
|
fetch_kiriban_list ((now := datetime.now ()).date () |
|
|
|
|
|
- timedelta (days = now.hour < 15))) |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
if __name__ == '__main__': |
|
|
asyncio.run (main ()) |
|
|
asyncio.run (main ()) |