統合 AI への移行 #16

マージ済み
みてるぞ が 6 個のコミットを ai-migration から main へマージ 2025-10-22 21:50:02 +09:00
5個のファイルの変更122行の追加173行の削除
コミット 7ff729b256 の変更だけを表示してゐます - すべてのコミットを表示
-58
ファイルの表示
@@ -1,58 +0,0 @@
from datetime import datetime
from atproto.models.AppBskyFeedDefs import BlockedPost, NotFoundPost
from atproto_client.models.app.bsky.feed import get_timeline
class Client:
app: AppNamespace
def get_current_time_iso (self) -> datetime: ...
def get_post_thread (
self,
uri: str,
parent_height: int | None = None
) -> Response: ...
def get_timeline (self) -> get_timeline.Response: ...
def follow (self, did: str) -> None: ...
def like (self, uri: str, cid: str) -> None: ...
class AppNamespace:
bsky: AppBskyNamespace
class AppBskyNamespace:
notification: AppBskyNotificationNamespace
class AppBskyNotificationNamespace:
def list_notifications (self) -> Response: ...
def update_seen (self, seen: dict[str, datetime]) -> None: ...
class Response:
notifications: list[Notification]
thread: (ThreadViewPost
| NotFoundPost
| BlockedPost)
class ThreadViewPost:
pass
class Notification:
is_read: bool
reason: str
uri: str
author: ProfileView
class ProfileView:
did: str
-5
ファイルの表示
@@ -1,5 +0,0 @@
class NotFoundPost:
pass
class BlockedPost:
pass
-5
ファイルの表示
@@ -1,5 +0,0 @@
from atproto.models.AppBskyFeedDefs import FeedViewPost
class Response:
feed: list[FeedViewPost]
+121 -104
ファイルの表示
@@ -5,16 +5,14 @@ import os
import time import time
from datetime import datetime from datetime import datetime
from io import BytesIO from io import BytesIO
from typing import TypedDict from typing import Any, TypedDict
import atproto import atproto # type: ignore
import requests import requests
from atproto import Client from atproto import Client # type: ignore
from atproto.models import AppBskyEmbedExternal, AppBskyEmbedImages from atproto.models import AppBskyEmbedExternal, AppBskyEmbedImages # type: ignore
from atproto.models.AppBskyFeedPost import ReplyRef from atproto.models.AppBskyFeedPost import ReplyRef # type: ignore
from atproto.models.app.bsky.embed import images from atproto_client.models.app.bsky.feed.get_timeline import Response # type: ignore
from atproto.models.com.atproto.repo import strong_ref
from atproto_client.models.app.bsky.feed.get_timeline import Response
from requests.exceptions import Timeout from requests.exceptions import Timeout
import account import account
@@ -68,7 +66,6 @@ async def check_mentions (
records = fetch_thread_contents (uri, 20) records = fetch_thread_contents (uri, 20)
if records: if records:
record = records[0] record = records[0]
content = record['text']
image_url: str | None = None image_url: str | None = None
if record['embed'] and hasattr (record['embed'], 'images'): if record['embed'] and hasattr (record['embed'], 'images'):
image_url = ('https://cdn.bsky.app/img/feed_fullsize/plain' image_url = ('https://cdn.bsky.app/img/feed_fullsize/plain'
@@ -86,95 +83,110 @@ async def check_mentions (
async def answer ( async def answer (
) -> None: ) -> None:
answered_flags = ( while True:
AnsweredFlag answered_flags = (
.where ('platform', Platform.BLUESKY.value) AnsweredFlag
.where ('answered', False) .where ('platform', Platform.BLUESKY.value)
.get ()) .where ('answered', False)
for answered_flag in answered_flags: .get ())
answer = answered_flag.answer for answered_flag in answered_flags:
match QueryType (answer.query.query_type): td: dict[str, Any]
case QueryType.BLUESKY_COMMENT: answer = answered_flag.answer
td = answer.query.transfer_data or { } match QueryType (answer.query_rel.query_type):
uri: str | None = td.get ('uri') case QueryType.BLUESKY_COMMENT:
cid: str | None = td.get ('cid') td = answer.query_rel.transfer_data or { }
if (not uri) or (not cid): uri: str | None = td.get ('uri')
continue cid: str | None = td.get ('cid')
if (not uri) or (not cid):
continue
sref = { 'uri': uri, 'cid': cid } sref = { 'uri': uri, 'cid': cid }
strong_ref = atproto.models.create_strong_ref (sref) strong_ref = atproto.models.create_strong_ref (sref) # type: ignore
reply_ref = ReplyRef (root = strong_ref, parent = strong_ref) reply_ref = ReplyRef (root = strong_ref, parent = strong_ref)
client.post (answer.content, reply_to = reply_ref) try:
flag.answered = True client.post (answer.content, reply_to = reply_ref)
flag.save () except Exception as e:
case QueryType.KIRIBAN | QueryType.NICO_REPORT: print (f"[answer/reply] { type (e).__name__ }: { e }")
td = answer.query.transfer_data or { } continue
video_code: str | None = td.get ('video_code') answered_flag.answered = True
if not video_code: answered_flag.save ()
continue case QueryType.KIRIBAN | QueryType.NICO_REPORT:
td = answer.query_rel.transfer_data or { }
video_code: str | None = td.get ('video_code')
if not video_code:
continue
uri = f"https://www.nicovideo.jp/watch/{ video_code }" uri = f"https://www.nicovideo.jp/watch/{ video_code }"
(title, description, thumbnail) = nicolib.fetch_embed_info (uri) (title, description, thumbnail) = nicolib.fetch_embed_info (uri)
try: try:
upload = client.com.atproto.repo.upload_blob ( resp = requests.get (thumbnail, timeout = 60)
BytesIO (requests.get (thumbnail, timeout = 60).content)) resp.raise_for_status ()
thumb = upload.blob upload = client.com.atproto.repo.upload_blob (BytesIO (resp.content))
except Timeout: thumb = upload.blob
thumb = None except Timeout:
thumb = None
except Exception as e:
print (f"[answer/nico-thumb] { type (e).__name__ }: { e }")
thumb = None
external = AppBskyEmbedExternal.External ( external = AppBskyEmbedExternal.External (
title = title, title = title,
description = description, description = description,
thumb = thumb, thumb = thumb,
uri = uri) uri = uri)
embed_external = AppBskyEmbedExternal.Main (external = external) embed_external = AppBskyEmbedExternal.Main (external = external)
client.post (answer.content, embed = embed_external) try:
flag.answered = True client.post (answer.content, embed = embed_external)
flag.save () except Exception as e:
case QueryType.SNACK_TIME: print (f"[answer/nico-post] { type (e).__name__ }: { e }")
try: continue
with open ('./assets/snack-time.jpg', 'rb') as f: answered_flag.answered = True
image = AppBskyEmbedImages.Image ( answered_flag.save ()
alt = ( case QueryType.SNACK_TIME:
'左に喜多ちゃん、右に人面鹿のニジカが' try:
'V字に並んでいる。' with open ('./assets/snack-time.jpg', 'rb') as f:
'喜多ちゃんは右手でピースサインをして' image = AppBskyEmbedImages.Image (
'片目をウインクしている。' alt = (
'ニジカは両手を広げ、' '左に喜多ちゃん、右に人面鹿のニジカが'
'右手にスプーンを持って' 'V字に並んでいる。'
'ポーズを取っている。' '喜多ちゃんは右手でピースサインをして'
'背景には' '片目をウインクしている。'
'赤と黄色の放射線状の模様が広がり' 'ニジカは両手を広げ'
'下部に「おやつタイムだ!!!!」という' '右手にスプーンを持って'
'日本語のテキストが表示されている。'), 'ポーズを取っている。'
image = client.com.atproto.repo.upload_blob (f).blob) '背景には'
client.post (answer.content, '赤と黄色の放射線状の模様が広がり、'
embed = AppBskyEmbedImages.Main (images = [image])) '下部に「おやつタイムだ!!!!」という'
flag.answered = True '日本語のテキストが表示されている。'),
flag.save () image = client.com.atproto.repo.upload_blob (f).blob)
except Exception: client.post (answer.content,
pass embed = AppBskyEmbedImages.Main (images = [image]))
case QueryType.HOT_SPRING: answered_flag.answered = True
try: answered_flag.save ()
with open ('./assets/hot-spring.jpg', 'rb') as f: except Exception:
image = AppBskyEmbedImages.Image ( pass
alt = ('左に喜多ちゃん、右にわさび県産滋賀県が' case QueryType.HOT_SPRING:
'V字に並んでいる。' try:
'喜多ちゃんは右手でピースサインをして' with open ('./assets/hot-spring.jpg', 'rb') as f:
'片目をウインクしている。' image = AppBskyEmbedImages.Image (
'わさび県産滋賀県はただ茫然と' alt = ('左に喜多ちゃん、右にわさび県産滋賀県'
'立ち尽くしている。' 'V字に並んでいる。'
'背景には' '喜多ちゃんは右手でピースサインをして'
'血と空の色をした放射線状の模様が広がり、' '片目をウインクしている。'
'下部に「温泉に入ろう!!!」という' 'わさび県産滋賀県はただ茫然と'
'日本語のテキストが表示されている。'), '立ち尽くしている。'
image = client.com.atproto.repo.upload_blob (f).blob) '背景には'
client.post (answer.content, '血と空の色をした放射線状の模様が広がり、'
embed = AppBskyEmbedImages.Main (images = [image])) '下部に「温泉に入ろう!!!」という'
flag.answered = True '日本語のテキストが表示されている。'),
flag.save () image = client.com.atproto.repo.upload_blob (f).blob)
except Exception: client.post (answer.content,
pass embed = AppBskyEmbedImages.Main (images = [image]))
answered_flag.answered = True
answered_flag.save ()
except Exception:
pass
await asyncio.sleep (10)
def check_notifications ( def check_notifications (
@@ -208,14 +220,19 @@ def fetch_thread_contents (
records: list[Record] = [] records: list[Record] = []
while res: while res:
records.append ({ 'strong_ref': { 'uri': res.post.uri, 'cid': res.post.cid }, if hasattr (res, 'post'):
'did': res.post.author.did, records.append ({ 'strong_ref': { 'uri': res.post.uri,
'handle': res.post.author.handle, 'cid': res.post.cid },
'name': res.post.author.display_name, 'did': res.post.author.did,
'datetime': res.post.record.created_at, 'handle': res.post.author.handle,
'text': res.post.record.text, 'name': (res.post.author.display_name
'embed': res.post.record.embed }) or res.post.author.handle),
res = res.parent 'datetime': res.post.record.created_at,
'text': getattr (res.post.record, 'text', None) or '',
'embed': getattr (res.post.record, 'embed', None) })
res = res.parent
else:
break
return records return records
@@ -241,7 +258,7 @@ def _add_query (
user: User, user: User,
content: str, content: str,
image_url: str | None = None, image_url: str | None = None,
transfer_data: dict | None = None, transfer_data: dict[str, Any] | None = None,
) -> None: ) -> None:
query = Query () query = Query ()
query.user_id = user.id query.user_id = user.id
サブモジュール nizika_ai が更新されました: 4e5bd13ab4...3be6d9063c