このコミットが含まれているのは:
@@ -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
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
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 Comment, Video, VideoHistory
|
from db.models import Comment, Video, VideoHistory
|
||||||
|
from nicolib import VideoInfo
|
||||||
from nizika_ai.consts import Character, GPTModel, QueryType
|
from nizika_ai.consts import Character, GPTModel, QueryType
|
||||||
from nizika_ai.models import Query
|
from nizika_ai.models import Query
|
||||||
|
|
||||||
@@ -22,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 (
|
||||||
@@ -36,20 +36,32 @@ 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
|
||||||
|
_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 }"
|
uri = f"https://www.nicovideo.jp/watch/{ video_code }"
|
||||||
(title, description, _) = fetch_embed_info (uri)
|
(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,
|
||||||
@@ -57,7 +69,7 @@ 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"{ since_posted.days }日と{ since_posted.seconds }秒前にニコニコに投稿された『{ video_info['title'] }』という動画が{ views_count }再生を突破しました。\n"
|
prompt = f"{ _days }日{ _hours }時間{ _mins }分{ _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"
|
||||||
@@ -82,14 +94,18 @@ async def report_kiriban (
|
|||||||
query.answered = False
|
query.answered = False
|
||||||
query.transfer_data = { 'video_code': video_code }
|
query.transfer_data = { 'video_code': video_code }
|
||||||
query.save ()
|
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,105 +160,6 @@ def fetch_kiriban_list (
|
|||||||
return _kiriban_list
|
return _kiriban_list
|
||||||
|
|
||||||
|
|
||||||
def fetch_video_info (
|
|
||||||
video_code: str,
|
|
||||||
) -> VideoInfo | None:
|
|
||||||
video_info: dict[str, str | list[str]] = { 'contentId': video_code }
|
|
||||||
|
|
||||||
bs = create_bs_from_url (f"https://www.nicovideo.jp/watch/{ video_code }")
|
|
||||||
if bs is None:
|
|
||||||
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')
|
|
||||||
|
|
||||||
|
|
||||||
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 (
|
def fetch_comments (
|
||||||
video_code: str,
|
video_code: str,
|
||||||
) -> list[Comment]:
|
) -> list[Comment]:
|
||||||
@@ -257,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
サブモジュール nicolib が b7a88cc774 で追加されました
+1
-1
サブモジュール nizika_nico が更新されました: b2f5f81ca8...baa75d68ba
新しい課題から参照
ユーザをブロックする