Merge pull request #135 from yupix/feat/support-hashtags

feat: support /hashtags/*
pull/142/head
yupix 6 months ago committed by GitHub
commit 2239f74f87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -815,13 +815,13 @@
"path": "/drive/folders",
"request_body_hash": "c7994546f15ac56e6d47b2c9513c720ffb05b146e2c58f4d5f8b7cdd1fa5fe27",
"response_body_hash": "05c56769de07a802e8ac7956c76ec95c025fd4ef2445500a18afdca430fca406",
"status": "needToWork"
"status": "supported"
},
"/drive/folders/create": {
"path": "/drive/folders/create",
"request_body_hash": "9c570cb675f851abf3624e608486d98f75932447e983524d8a2fef010a832c4e",
"response_body_hash": "65943f2e45eac25dbbd3acd9ba9249b2b5a83176797b863389a1932cdd5b94f8",
"status": "needToWork"
"status": "supported"
},
"/drive/folders/delete": {
"path": "/drive/folders/delete",
@ -833,19 +833,19 @@
"path": "/drive/folders/find",
"request_body_hash": "f2f302ec5a018e252ce013efab000ef654223cdbd470afb2aee18e1fd0e6865b",
"response_body_hash": "05c56769de07a802e8ac7956c76ec95c025fd4ef2445500a18afdca430fca406",
"status": "needToWork"
"status": "supported"
},
"/drive/folders/show": {
"path": "/drive/folders/show",
"request_body_hash": "7ddbf084df376fe1ced074c9454678879e8908a99fe0432fb2abaadfa597afec",
"response_body_hash": "5ab07c4af0d6e1e2bbcb44f5e15fae98ce7b37a16583f3a2ebe85eeafd0406ae",
"status": "needToWork"
"status": "supported"
},
"/drive/folders/update": {
"path": "/drive/folders/update",
"request_body_hash": "6b715c2e3d185c4928e6c5f3f714570d9ccc1dbb7880c528f72290d760fa303f",
"response_body_hash": "479f18df687ff202504b1964269dedd1ec139c927c446d544809be0645a43042",
"status": "needToWork"
"status": "supported"
},
"/drive/stream": {
"path": "/drive/stream",
@ -1037,31 +1037,31 @@
"path": "/hashtags/list",
"request_body_hash": "94e486046065b88b3864bc95ed283707ab96b7f9f91147e06148406a75086369",
"response_body_hash": "2094d4c2bf514341a838d01f1a89477ebd8e2ce1ca04b40374cf60d0d6e4a74c",
"status": "notSupported"
"status": "supported"
},
"/hashtags/search": {
"path": "/hashtags/search",
"request_body_hash": "74c5456046bdb4aa84b5b15d12b3034eca208232c692d68595c455d5d2963952",
"response_body_hash": "b27c6f9999974cc9d2aed6a70c37dba1e70641c4fdf5632bca440a6382106105",
"status": "notSupported"
"status": "supported"
},
"/hashtags/show": {
"path": "/hashtags/show",
"request_body_hash": "bcb5dbf5529f1fa586775fca5006b1aa723fe5b3fd61243ae776be03ce3897e3",
"response_body_hash": "8c8eb54b8e881447ea295ba25d2689a6936f8385f335b531f98fff4b30e31d3e",
"status": "notSupported"
"status": "supported"
},
"/hashtags/trend": {
"path": "/hashtags/trend",
"request_body_hash": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"response_body_hash": "49f1aefa2720add5521ce6999aa347a293810614dc3cc9e88ef986b494ce3684",
"status": "notSupported"
"status": "supported"
},
"/hashtags/users": {
"path": "/hashtags/users",
"request_body_hash": "709f3bb1ebf5a6ea85eca2694ed80dafc896ffa7a8c8bc46bafe3695e70bb90c",
"response_body_hash": "f711c88a06f184d959627953fff320b77c3bcfbb871fe226d61cfacd7b6eb5cd",
"status": "notSupported"
"status": "supported"
},
"/i": {
"path": "/i",
@ -2321,7 +2321,7 @@
"Hashtag": {
"name": "Hashtag",
"hash": "619643560ecb9b56a12db881efff441954be264ba1f41a9d9d20ae46ad2d1dfd",
"status": "notSupported"
"status": "supported"
},
"InviteCode": {
"name": "InviteCode",

@ -4,7 +4,7 @@
`2024.3.1`
## Supported endpoints (144/368)
## Supported endpoints (149/368)
- [x] /admin/accounts/create
- [x] /admin/accounts/delete
@ -47,9 +47,19 @@
- [x] /drive/files/show
- [x] /drive/files/update
- [x] /drive/files/upload-from-url
- [x] /drive/folders
- [x] /drive/folders/create
- [x] /drive/folders/delete
- [x] /drive/folders/find
- [x] /drive/folders/show
- [x] /drive/folders/update
- [x] /following/create
- [x] /following/delete
- [x] /hashtags/list
- [x] /hashtags/search
- [x] /hashtags/show
- [x] /hashtags/trend
- [x] /hashtags/users
- [x] /invite/delete
- [x] /emojis
- [x] /emoji
@ -187,11 +197,6 @@
- [ ] /gallery/posts/unlike
- [ ] /gallery/posts/update
- [ ] /get-avatar-decorations
- [ ] /hashtags/list
- [ ] /hashtags/search
- [ ] /hashtags/show
- [ ] /hashtags/trend
- [ ] /hashtags/users
- [ ] /i/claim-achievement
- [ ] /i/favorites
- [ ] /i/gallery/likes
@ -312,11 +317,6 @@
- [ ] /admin/roles/show (Need to work)
- [ ] /admin/roles/users (Need to work)
- [ ] /drive (Need to work)
- [ ] /drive/folders (Need to work)
- [ ] /drive/folders/create (Need to work)
- [ ] /drive/folders/find (Need to work)
- [ ] /drive/folders/show (Need to work)
- [ ] /drive/folders/update (Need to work)
- [ ] /drive/stream (Need to work)
- [ ] /endpoint (Need to work)
- [ ] /federation/instances (Need to work)
@ -410,7 +410,7 @@
- [ ] Muting
- [ ] RenoteMuting
- [ ] Blocking
- [ ] Hashtag
- [x] Hashtag
- [x] InviteCode
- [ ] Page
- [x] Channel

@ -0,0 +1,172 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Literal
from mipac.abstract.action import AbstractAction
from mipac.http import HTTPClient, Route
from mipac.models.hashtag import Hashtag, TrendHashtag
from mipac.models.user import MeDetailed, UserDetailedNotMe, packed_user
from mipac.types.hashtag import IHashtag, ITrendHashtag
from mipac.types.user import IMeDetailedSchema, IUserDetailedNotMeSchema
if TYPE_CHECKING:
from mipac.client import ClientManager
class HashtagActions(AbstractAction):
def __init__(self, *, session: HTTPClient, client: ClientManager):
self.__session: HTTPClient = session
self.__client: ClientManager = client
async def get_list(
self,
sort: Literal[
"+mentionedUsers",
"-mentionedUsers",
"+mentionedLocalUsers",
"-mentionedLocalUsers",
"+mentionedRemoteUsers",
"-mentionedRemoteUsers",
"+attachedUsers",
"-attachedUsers",
"+attachedLocalUsers",
"-attachedLocalUsers",
"+attachedRemoteUsers",
"-attachedRemoteUsers",
],
limit: int = 10,
attached_to_user_only: bool = False,
attached_to_local_user_only: bool = False,
attached_to_remote_user_only: bool = False,
) -> list[Hashtag]:
"""ハッシュタグのリストを取得します。
Parameters
----------
sort: Literal[
"+mentionedUsers",
"-mentionedUsers",
"+mentionedLocalUsers",
"-mentionedLocalUsers",
"+mentionedRemoteUsers",
"-mentionedRemoteUsers",
"+attachedUsers",
"-attachedUsers",
"+attachedLocalUsers",
"-attachedLocalUsers",
"+attachedRemoteUsers",
"-attachedRemoteUsers",
]
ソートの方法を指定します
limit: int, optional
取得するハッシュタグの数を指定します, default=10
attached_to_user_only: bool, optional
ユーザーに添付されたハッシュタグのみを取得するかどうかを指定します, default=False
attached_to_local_user_only: bool, optional
ローカルユーザーに添付されたハッシュタグのみを取得するかどうかを指定します, default=False
attached_to_remote_user_only: bool, optional
リモートユーザーに添付されたハッシュタグのみを取得するかどうかを指定します, default=False
Returns
-------
list[Hashtag]
取得したハッシュタグのリストです
"""
body = {
"limit": limit,
"attachedToUserOnly": attached_to_user_only,
"attachedToLocalUserOnly": attached_to_local_user_only,
"attachedToRemoteUserOnly": attached_to_remote_user_only,
"sort": sort,
}
raw_hashtags: list[IHashtag] = await self.__session.request(
Route("POST", "/api/hashtags/list"), json=body
)
return [
Hashtag(raw_hashtag=raw_hashtag, client=self.__client) for raw_hashtag in raw_hashtags
]
async def search(self, query: str, limit: int = 10, offset: int = 0) -> list[str]:
"""ハッシュタグを検索します
Parameters
----------
query: str
検索するクエリを指定します
limit: int, optional
取得するハッシュタグの数を指定します, default=10
offset: int, optional
オフセットを指定します, default=0
Returns
-------
list[Hashtag]
取得したハッシュタグのリストです
"""
body = {"query": query, "limit": limit, "offset": offset}
raw_hashtags: list[str] = await self.__session.request(
Route("POST", "/api/hashtags/search"),
json=body,
lower=False, # 戻り値がarrayのstrなのでlowerするとエラーになる
)
return raw_hashtags
async def show(self, tag: str):
"""ハッシュタグの情報を取得します。
Parameters
----------
tag: str
取得するハッシュタグの名前です
Returns
-------
Hashtag
取得したハッシュタグの情報です
"""
body = {"tag": tag}
raw_hashtag: IHashtag = await self.__session.request(
Route("POST", "/api/hashtags/show"), json=body
)
return Hashtag(raw_hashtag=raw_hashtag, client=self.__client)
async def get_trend(self):
"""トレンドのハッシュタグを取得します。
Returns
-------
list[TrendHashtag]
取得したハッシュタグのリストです
"""
raw_trend_hashtags: list[ITrendHashtag] = await self.__session.request(
Route("GET", "/api/hashtags/trend")
)
return [
TrendHashtag(raw_trend_hashtag=raw_trend_hashtag, client=self.__client)
for raw_trend_hashtag in raw_trend_hashtags
]
async def get_users(
self,
tag: str,
sort: Literal[
"+follower", "-follower", "+createdAt", "-createdAt", "+updatedAt", "-updatedAt"
],
state: Literal["all", "alive"] = "all",
origin: Literal["combined", "local", "remote"] = "local",
) -> list[UserDetailedNotMe | MeDetailed]:
body = {"tag": tag, "sort": sort, "state": state, "origin": origin}
raw_users: list[
IUserDetailedNotMeSchema | IMeDetailedSchema
] = await self.__session.request(Route("POST", "/api/hashtags/users"), json=body)
return [packed_user(raw_user, client=self.__client) for raw_user in raw_users]

@ -12,6 +12,7 @@ from mipac.manager.clip import ClientClipManager, ClipManager
from mipac.manager.drive.drive import DriveManager
from mipac.manager.emoji import EmojiManager
from mipac.manager.follow import FollowManager, FollowRequestManager
from mipac.manager.hashtag import HashtagManager
from mipac.manager.invite import ClientInviteManager, InviteManager
from mipac.manager.my import MyManager
from mipac.manager.note import ClientNoteManager, NoteManager
@ -46,6 +47,7 @@ class ClientManager:
session=session,
client=self,
)
self.hashtag: HashtagManager = HashtagManager(session=session, client=self)
self.clip: ClipManager = ClipManager(session=session, client=self)
self.emoji: EmojiManager = EmojiManager(session=session, client=self)
self.antenna: AntennaManager = AntennaManager(session=session, client=self)

@ -0,0 +1,19 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from mipac.abstract.manager import AbstractManager
from mipac.http import HTTPClient
from mipac.actions.hashtag import HashtagActions
if TYPE_CHECKING:
from mipac.manager.client import ClientManager
class HashtagManager(AbstractManager):
def __init__(self, *, session: HTTPClient, client: ClientManager):
self.__session: HTTPClient = session
self.__client: ClientManager = client
@property
def action(self) -> HashtagActions:
return HashtagActions(session=self.__session, client=self.__client)

@ -0,0 +1,99 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from mipac.types.hashtag import IHashtag, ITrendHashtag
if TYPE_CHECKING:
from mipac.manager.client import ClientManager
class Hashtag:
def __init__(self, *, raw_hashtag: IHashtag, client: ClientManager) -> None:
self.__client: ClientManager = client
self.__raw_hashtag: IHashtag = raw_hashtag
@property
def tag(self):
return self.__raw_hashtag["tag"]
@property
def mentioned_users_count(self):
return self.__raw_hashtag["mentioned_users_count"]
@property
def mentioned_local_users_count(self):
return self.__raw_hashtag["mentioned_local_users_count"]
@property
def mentioned_remote_users_count(self):
return self.__raw_hashtag["mentioned_remote_users_count"]
@property
def attached_users_count(self):
return self.__raw_hashtag["attached_users_count"]
@property
def attached_local_users_count(self):
return self.__raw_hashtag["attached_local_users_count"]
@property
def attached_remote_users_count(self):
return self.__raw_hashtag["attached_remote_users_count"]
def _get(self, key: str) -> Any | None:
"""生のレスポンスデータに直接アクセスすることができます
Returns
-------
Any | None
生のレスポンスデータ
"""
return self.__raw_hashtag.get(key)
class TrendHashtag:
def __init__(self, *, raw_trend_hashtag: ITrendHashtag, client: ClientManager) -> None:
self.__raw_trend_hashtag: ITrendHashtag = raw_trend_hashtag
self.__client: ClientManager = client
@property
def tag(self):
"""ハッシュタグ
Returns
-------
str
ハッシュタグ
"""
return self.__raw_trend_hashtag["tag"]
@property
def chart(self):
"""チャート
Returns
-------
list[int]
チャート
"""
return self.__raw_trend_hashtag["chart"]
@property
def users_count(self):
"""ユーザー数
Returns
-------
int
ユーザー数
"""
return self.__raw_trend_hashtag["users_count"]
def _get(self, key: str) -> Any | None:
"""生のレスポンスデータに直接アクセスすることができます
Returns
-------
Any | None
生のレスポンスデータ
"""
return self.__raw_trend_hashtag.get(key)

@ -40,7 +40,23 @@ if TYPE_CHECKING:
from mipac.manager.users.list import ClientUserListManager
__all__ = ("PartialUser", "Achievement", "MeDetailed")
__all__ = (
"FollowCommon",
"Follower",
"Following",
"Achievement",
"UserField",
"UserRole",
"UserDetailedNotMeOnly",
"MeDetailedOnly",
"UserDetailedNotMe",
"MeDetailed",
"packed_user",
"UserList",
"UserListMembership",
"FrequentlyRepliedUser",
"CreatedUser",
)
class FollowCommon[FFC: IFederationFollowCommon]:

@ -0,0 +1,17 @@
from typing import TypedDict
class IHashtag(TypedDict):
tag: str
mentioned_users_count: int
mentioned_local_users_count: int
mentioned_remote_users_count: int
attached_users_count: int
attached_local_users_count: int
attached_remote_users_count: int
class ITrendHashtag(TypedDict):
tag: str
chart: list[int]
users_count: int
Loading…
Cancel
Save