みてるぞ 1 month ago
parent
commit
d735c93526
4 changed files with 45 additions and 121 deletions
  1. +3
    -0
      .gitmodules
  2. +40
    -120
      main.py
  3. +1
    -0
      nicolib
  4. +1
    -1
      nizika_nico

+ 3
- 0
.gitmodules View File

@@ -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

+ 40
- 120
main.py View File

@@ -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
from bs4 import BeautifulSoup
from requests.exceptions import Timeout

import nicolib
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) = (
kiriban_list.pop (random.randint (0, len (kiriban_list) - 1)))
async with lock:
(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
if kiriban_list:
td /= len (kiriban_list)
remain = max (len (kiriban_list), 1)
td = (datetime.combine (d, time (15, 0)) - dt) / remain
# まれに時刻跨ぎでマイナスになるため
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
- 0
nicolib

@@ -0,0 +1 @@
Subproject commit b7a88cc774aa7869678c00abf9f93982e5b6cfb9

+ 1
- 1
nizika_nico

@@ -1 +1 @@
Subproject commit b2f5f81ca8615781d79d807fe92f7824653cea22
Subproject commit baa75d68ba7150775eb024ebab45f3059411ac70

Loading…
Cancel
Save