コミットを比較

..

2 コミット

作成者 SHA1 メッセージ 日付
みてるぞ d735c93526 #2 2025-08-16 04:00:20 +09:00
みてるぞ 70712e8517 #1 キリ番完成 2025-01-10 00:18:43 +09:00
5個のファイルの変更72行の追加101行の削除
+3
ファイルの表示
@@ -4,3 +4,6 @@
[submodule "nizika_nico"] [submodule "nizika_nico"]
path = nizika_nico path = nizika_nico
url = https://git.miteruzo.com/miteruzo/nizika_nico.git url = https://git.miteruzo.com/miteruzo/nizika_nico.git
[submodule "nicolib"]
path = nicolib
url = https://git.miteruzo.com/miteruzo/nicolib.git
+66 -99
ファイルの表示
@@ -1,15 +1,17 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import random
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 TypedDict, cast
import requests import nicolib
from bs4 import BeautifulSoup
from requests.exceptions import Timeout
import queries_to_answers as q2a import queries_to_answers as q2a
from db.models import Video, VideoHistory 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
KIRIBAN_VIEWS_COUNTS: list[int] = sorted ({ *range (1_000, 10_000, 1_000), KIRIBAN_VIEWS_COUNTS: list[int] = sorted ({ *range (1_000, 10_000, 1_000),
*range (10_000, 1_000_001, 10_000), *range (10_000, 1_000_001, 10_000),
@@ -19,6 +21,7 @@ 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]]
lock = Lock ()
async def main ( async def main (
@@ -33,40 +36,40 @@ async def main (
async def queries_to_answers ( async def queries_to_answers (
) -> None: ) -> None:
while True: while True:
q2a.main () loop = asyncio.get_running_loop ()
await loop.run_in_executor (None, q2a.main)
await asyncio.sleep (10) await asyncio.sleep (10)
async def report_kiriban ( async def report_kiriban (
) -> None: ) -> None:
while True: while True:
if not kiriban_list:
await wait_until (time (15, 0))
continue
# キリ番祝ひ # キリ番祝ひ
(views_count, video_info, uploaded_at) = ( async with lock:
kiriban_list.pop (random.randint (0, len (kiriban_list) - 1))) (views_count, video_info, uploaded_at) = (
kiriban_list.pop (random.randint (0, len (kiriban_list) - 1)))
since_posted = datetime.now () - uploaded_at since_posted = datetime.now () - uploaded_at
uri = f"https://www.nicovideo.jp/watch/{ video_info['contentId'] }" _days = since_posted.days
(title, description, thumbnail) = fetch_embed_info (uri) _seconds = since_posted.seconds
try: (_hours, _seconds) = divmod (_seconds, 3600)
upload = client.com.atproto.repo.upload_blob ( (_mins, _seconds) = divmod (_seconds, 60)
io.BytesIO (requests.get (thumbnail,
timeout = 60).content)) video_code = video_info['contentId']
thumb = upload.blob uri = f"https://www.nicovideo.jp/watch/{ video_code }"
except Timeout: (title, description, _) = nicolib.fetch_embed_info (uri)
thumb = None comments = fetch_comments (video_code)
comments = nico.get_comments (video_info['contentId'])
popular_comments = sorted (comments, popular_comments = sorted (comments,
key = lambda c: c.nico_count, key = lambda c: c.nico_count,
reverse = True)[:10] reverse = True)[:10]
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]
embed_external = models.AppBskyEmbedExternal.Main ( prompt = f"{ _days }{ _hours }時間{ _mins }{ _seconds }秒前にニコニコに投稿された『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n"
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" prompt += 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"
@@ -78,18 +81,31 @@ async def report_kiriban (
```html ```html
{ video_info['description'] } { 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 () dt = datetime.now ()
d = dt.date () d = dt.date ()
if dt.hour >= 15: if dt.hour >= 15:
d += timedelta (days = 1) d += timedelta (days = 1)
td = datetime.combine (d, time (15, 0)) - dt remain = max (len (kiriban_list), 1)
if kiriban_list: td = (datetime.combine (d, time (15, 0)) - dt) / remain
td /= len (kiriban_list) # まれに時刻跨ぎでマイナスになるため
if td.total_seconds () < 0:
td = timedelta (seconds = 0)
await asyncio.sleep (td.total_seconds ()) await asyncio.sleep (td.total_seconds ())
@@ -97,7 +113,17 @@ async def update_kiriban_list (
) -> None: ) -> None:
while True: while True:
await wait_until (time (15, 0)) await wait_until (time (15, 0))
kiriban_list += fetch_kiriban_list (datetime.now ().date ())
new_list = fetch_kiriban_list (datetime.now ().date ())
if not new_list:
continue
async with lock:
have = { k[1]['contentId'] for k in kiriban_list }
for item in new_list:
if item[1]['contentId'] not in have:
kiriban_list.append (item)
have.add (item[1]['contentId'])
def fetch_kiriban_list ( def fetch_kiriban_list (
@@ -126,7 +152,7 @@ def fetch_kiriban_list (
previous_views_count = 0 previous_views_count = 0
if previous_views_count >= kiriban_views_count: if previous_views_count >= kiriban_views_count:
continue continue
video_info = 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))
@@ -134,65 +160,13 @@ def fetch_kiriban_list (
return _kiriban_list return _kiriban_list
def fetch_video_info ( def fetch_comments (
video_code: str, video_code: str,
) -> VideoInfo | None: ) -> list[Comment]:
video_info: dict[str, str | list[str]] = { 'contentId': video_code } video = Video.where ('code', video_code).first ()
if video is None:
bs = create_bs_from_url (f"https://www.nicovideo.jp/watch/{ video_code }") return []
if bs is None: return video.comments
return None
try:
title = bs.find ('title')
if title is None:
return None
video_info['title'] = '-'.join (title.text.split ('-')[:(-1)])[:(-1)]
tags: str = bs.find ('meta', attrs = { 'name': 'keywords' }).get ('content') # type: ignore
video_info['tags'] = tags.split (',')
video_info['description'] = bs.find ('meta', attrs = { 'name': 'description' }).get ('content') # type: ignore
except Exception:
return None
return cast (VideoInfo, video_info)
def create_bs_from_url (
url: str,
params: dict | None = None,
) -> BeautifulSoup | None:
"""
URL から BeautifulSoup インスタンス生成
Parameters
----------
url: str
捜査する URL
params: dict
パラメータ
Return
------
BeautifulSoup | None
BeautifulSoup オブゼクト(失敗したら None)
"""
if params is None:
params = { }
try:
req = requests.get (url, params = params, timeout = 60)
except Timeout:
return None
if req.status_code != 200:
return None
req.encoding = req.apparent_encoding
return BeautifulSoup (req.text, 'hecoml.parser')
async def report_nico ( async def report_nico (
@@ -210,13 +184,6 @@ async def wait_until (
await asyncio.sleep ((datetime.combine (d, t) - dt).total_seconds ()) await asyncio.sleep ((datetime.combine (d, t) - dt).total_seconds ())
class VideoInfo (TypedDict):
contentId: str
title: str
tags: list[str]
description: str
kiriban_list = ( kiriban_list = (
fetch_kiriban_list ((d := datetime.now ()).date () fetch_kiriban_list ((d := datetime.now ()).date ()
- timedelta (days = d.hour < 15))) - timedelta (days = d.hour < 15)))
サブモジュール
+1
サブモジュール nicolibb7a88cc774 で追加されました
サブモジュール nizika_ai が更新されました: 9e136a7cb3...3fd16bd1c2
サブモジュール nizika_nico が更新されました: b2f5f81ca8...baa75d68ba