統合 AI への移行 #16
-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
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
class NotFoundPost:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class BlockedPost:
|
|
||||||
pass
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
from atproto.models.AppBskyFeedDefs import FeedViewPost
|
|
||||||
|
|
||||||
|
|
||||||
class Response:
|
|
||||||
feed: list[FeedViewPost]
|
|
||||||
@@ -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
|
||||||
|
|||||||
+1
-1
サブモジュール nizika_ai が更新されました: 4e5bd13ab4...3be6d9063c
新しい課題から参照
ユーザをブロックする