このコミットが含まれているのは:
2025-10-21 22:27:37 +09:00
コミット 7ff729b256
5個のファイルの変更122行の追加173行の削除
-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]
+45 -28
ファイルの表示
@@ -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,29 +83,35 @@ async def check_mentions (
async def answer ( async def answer (
) -> None: ) -> None:
while True:
answered_flags = ( answered_flags = (
AnsweredFlag AnsweredFlag
.where ('platform', Platform.BLUESKY.value) .where ('platform', Platform.BLUESKY.value)
.where ('answered', False) .where ('answered', False)
.get ()) .get ())
for answered_flag in answered_flags: for answered_flag in answered_flags:
td: dict[str, Any]
answer = answered_flag.answer answer = answered_flag.answer
match QueryType (answer.query.query_type): match QueryType (answer.query_rel.query_type):
case QueryType.BLUESKY_COMMENT: case QueryType.BLUESKY_COMMENT:
td = answer.query.transfer_data or { } td = answer.query_rel.transfer_data or { }
uri: str | None = td.get ('uri') uri: str | None = td.get ('uri')
cid: str | None = td.get ('cid') cid: str | None = td.get ('cid')
if (not uri) or (not cid): if (not uri) or (not cid):
continue 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)
try:
client.post (answer.content, reply_to = reply_ref) client.post (answer.content, reply_to = reply_ref)
flag.answered = True except Exception as e:
flag.save () print (f"[answer/reply] { type (e).__name__ }: { e }")
continue
answered_flag.answered = True
answered_flag.save ()
case QueryType.KIRIBAN | QueryType.NICO_REPORT: case QueryType.KIRIBAN | QueryType.NICO_REPORT:
td = answer.query.transfer_data or { } td = answer.query_rel.transfer_data or { }
video_code: str | None = td.get ('video_code') video_code: str | None = td.get ('video_code')
if not video_code: if not video_code:
continue continue
@@ -116,11 +119,15 @@ async def answer (
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 ()
upload = client.com.atproto.repo.upload_blob (BytesIO (resp.content))
thumb = upload.blob thumb = upload.blob
except Timeout: except Timeout:
thumb = None 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,
@@ -128,9 +135,13 @@ async def answer (
thumb = thumb, thumb = thumb,
uri = uri) uri = uri)
embed_external = AppBskyEmbedExternal.Main (external = external) embed_external = AppBskyEmbedExternal.Main (external = external)
try:
client.post (answer.content, embed = embed_external) client.post (answer.content, embed = embed_external)
flag.answered = True except Exception as e:
flag.save () print (f"[answer/nico-post] { type (e).__name__ }: { e }")
continue
answered_flag.answered = True
answered_flag.save ()
case QueryType.SNACK_TIME: case QueryType.SNACK_TIME:
try: try:
with open ('./assets/snack-time.jpg', 'rb') as f: with open ('./assets/snack-time.jpg', 'rb') as f:
@@ -150,8 +161,8 @@ async def answer (
image = client.com.atproto.repo.upload_blob (f).blob) image = client.com.atproto.repo.upload_blob (f).blob)
client.post (answer.content, client.post (answer.content,
embed = AppBskyEmbedImages.Main (images = [image])) embed = AppBskyEmbedImages.Main (images = [image]))
flag.answered = True answered_flag.answered = True
flag.save () answered_flag.save ()
except Exception: except Exception:
pass pass
case QueryType.HOT_SPRING: case QueryType.HOT_SPRING:
@@ -171,10 +182,11 @@ async def answer (
image = client.com.atproto.repo.upload_blob (f).blob) image = client.com.atproto.repo.upload_blob (f).blob)
client.post (answer.content, client.post (answer.content,
embed = AppBskyEmbedImages.Main (images = [image])) embed = AppBskyEmbedImages.Main (images = [image]))
flag.answered = True answered_flag.answered = True
flag.save () answered_flag.save ()
except Exception: except Exception:
pass 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'):
records.append ({ 'strong_ref': { 'uri': res.post.uri,
'cid': res.post.cid },
'did': res.post.author.did, 'did': res.post.author.did,
'handle': res.post.author.handle, 'handle': res.post.author.handle,
'name': res.post.author.display_name, 'name': (res.post.author.display_name
or res.post.author.handle),
'datetime': res.post.record.created_at, 'datetime': res.post.record.created_at,
'text': res.post.record.text, 'text': getattr (res.post.record, 'text', None) or '',
'embed': res.post.record.embed }) 'embed': getattr (res.post.record, 'embed', None) })
res = res.parent 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