diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f73924..3e426f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,43 @@ ## [Unreleased] +### Breaking changes 💔 + + +#### 以下のクラスを削除しました + +この変更はMisskeyのSchemaに似せた形で再実装するにあたり、MisskeyのSchemaよりも細かくモデルを作成していたため、そういったものを削除した形となります。 + +- `UserDetailed` -> `UserDetailedNotMe | MeDetailed` +- `MeDetailedModerator` -> `MeDetailed` +- `UserDetailedModerator` -> `UserDetailedNotMe | MeDetailed` +- `UserDetailedNotLogined` -> `UserDetailedNotMe | MeDetailed` +- `AdminAnnouncementClientActions` -> `ClientAdminAnnouncementActions` +- `AnnouncementSystem` -> `AnnoucementDetailed` + +#### クラス名の変更 + +- `AdminAdvertisingModelActions` -> `ClientAdminAdActions` +- `AdminAdvertisingActions` -> `AdminAdActions` +- `AdminAdvertisingModelManager` -> `ClientAdminAdManager` +- `AdminAdvertisingManager` -> `AdminAdManager` + +#### 引数に関する変更 + +`*Actions` 系にて `*_id` のような引数はすべてキーワード引数に変更されました。これはリスコフの置換法則に則るうえで必要な作業であり、今後のコード変更に対する耐性を上げるためでもあります。ご迷惑をお掛けしますがご理解のほどよろしくお願いいたします。 + +#### 戻り値の変更 + +- `Announcement.action -> ClientAdminAnnouncementActions` -> `Announcement.action -> ClientAdminAnnouncementManager` +- `AnnouncementDetailed.action -> ClientAdminAnnouncementActions` -> `AnnouncementDetailed.action -> ClientAdminAnnouncementManager` + +#### `get_all` 引数を廃止 + +今まで多くの配列を返すメソッドをジェネレータとして作成していましたが、少ししかデータは要らないのに `async for` を書くのは大変ということで `get_all` 引数を廃止します。 + +これにより今まで `get_all` 引数があった ジェネレータは全て通常の list等を返すメソッドに変更されます。 +今まで通りのジェネレータとしての機能が必要な場合は `get_all_*` というメソッドが新しく増えているためそちらをご利用ください。 + ## [0.5.99] 2023-12-03 このリリースは最新の Misskey 向けに最適化された `develop` ブランチの物となります。インスタンスで `v11` や `v12` を利用している場合は更新しないことをおすすめします。 diff --git a/mipac/actions/admins/ad.py b/mipac/actions/admins/ad.py index f2199db..1573f9d 100644 --- a/mipac/actions/admins/ad.py +++ b/mipac/actions/admins/ad.py @@ -12,17 +12,12 @@ if TYPE_CHECKING: from mipac.client import ClientManager -class AdminAdvertisingModelActions(AbstractAction): - def __init__(self, ad_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self._ad_id: str | None = ad_id +class SharedAdminAdActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def delete(self, *, id: str | None = None) -> bool: - ad_id = self._ad_id or id - - if ad_id is None: - raise ValueError("ad id is required") + async def delete(self, *, ad_id: str) -> bool: res: bool = await self._session.request( Route("POST", "/api/admin/ad/delete"), json={"id": ad_id}, auth=True, lower=True ) @@ -40,11 +35,8 @@ class AdminAdvertisingModelActions(AbstractAction): starts_at: int, day_of_week: int, *, - ad_id: str | None = None, + ad_id: str, ) -> bool: - ad_id = self._ad_id or ad_id - if ad_id is None: - raise ValueError("ad id is required") data = { "id": ad_id, "memo": memo or "", @@ -63,7 +55,49 @@ class AdminAdvertisingModelActions(AbstractAction): return res -class AdminAdvertisingActions(AdminAdvertisingModelActions): +class ClientAdminAdActions(SharedAdminAdActions): + def __init__(self, ad_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self._ad_id: str = ad_id + + @override + async def delete(self, *, ad_id: str | None = None) -> bool: + ad_id = ad_id or self._ad_id + + return await super().delete(ad_id=ad_id) + + @override + async def update( + self, + memo: str, + url: str, + image_url: str, + place: Literal["square", "horizontal", "horizontal-big"], + priority: Literal["high", "middle", "low"], + ratio: int, + expires_at: int, + starts_at: int, + day_of_week: int, + *, + ad_id: str | None = None, + ) -> bool: + ad_id = ad_id or self._ad_id + + return await super().update( + memo=memo, + url=url, + image_url=image_url, + place=place, + priority=priority, + ratio=ratio, + expires_at=expires_at, + starts_at=starts_at, + day_of_week=day_of_week, + ad_id=ad_id, + ) + + +class AdminAdActions(SharedAdminAdActions): def __init__(self, *, session: HTTPClient, client: ClientManager): super().__init__(session=session, client=client) @@ -95,10 +129,6 @@ class AdminAdvertisingActions(AdminAdvertisingModelActions): ) return Ad(ad_data=raw_ad, client=self._client) - @override - async def delete(self, id: str) -> bool: - return await super().delete(id=id) - async def get_list( self, limit: int = 10, @@ -142,30 +172,3 @@ class AdminAdvertisingActions(AdminAdvertisingModelActions): for raw_ad in raw_ads: yield Ad(ad_data=raw_ad, client=self._client) - - @override - async def update( - self, - ad_id: str, - memo: str, - url: str, - image_url: str, - place: Literal["square", "horizontal", "horizontal-big"], - priority: Literal["high", "middle", "low"], - ratio: int, - expires_at: int, - starts_at: int, - day_of_week: int, - ) -> bool: - return await super().update( - ad_id=ad_id, - memo=memo, - url=url, - image_url=image_url, - place=place, - priority=priority, - ratio=ratio, - expires_at=expires_at, - starts_at=starts_at, - day_of_week=day_of_week, - ) diff --git a/mipac/actions/admins/announcement.py b/mipac/actions/admins/announcement.py index f861d2f..56ad4ad 100644 --- a/mipac/actions/admins/announcement.py +++ b/mipac/actions/admins/announcement.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, AsyncGenerator +from typing import TYPE_CHECKING, AsyncGenerator, override from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route @@ -12,21 +12,18 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class AdminAnnouncementClientActions(AbstractAction): +class SharedAdminAnnouncementActions(AbstractAction): def __init__( self, - announce_id: str | None = None, *, session: HTTPClient, client: ClientManager, - ): - self.__announce_id = announce_id - self.__session: HTTPClient = session - self.__client: ClientManager = client + ) -> None: + self._session: HTTPClient = session + self._client: ClientManager = client - async def delete(self, announce_id: str | None = None) -> bool: - announce_id = announce_id or self.__announce_id - res: bool = await self.__session.request( + async def delete(self, *, announce_id: str) -> bool: + res: bool = await self._session.request( Route("POST", "/api/admin/announcements/delete"), json={"id": announce_id}, auth=True, @@ -39,16 +36,15 @@ class AdminAnnouncementClientActions(AbstractAction): text: str, image_url: str | None = None, *, - announce_id: str | None = None, + announce_id: str, ): - announce_id = announce_id or self.__announce_id body = { "id": announce_id, "title": title, "text": text, "imageUrl": image_url, } - res: bool = await self.__session.request( + res: bool = await self._session.request( Route("POST", "/api/admin/announcements/update"), json=body, auth=True, @@ -57,26 +53,56 @@ class AdminAnnouncementClientActions(AbstractAction): return res -class AdminAnnouncementActions(AdminAnnouncementClientActions): +class ClientAdminAnnouncementActions(SharedAdminAnnouncementActions): def __init__( self, + announce_id: str, + *, + session: HTTPClient, + client: ClientManager, + ) -> None: + super().__init__(session=session, client=client) + self.__announce_id: str = announce_id + + @override + async def delete(self, *, announce_id: str | None = None) -> bool: + announce_id = announce_id or self.__announce_id + return await super().delete(announce_id=announce_id) + + @override + async def update( + self, + title: str, + text: str, + image_url: str | None = None, + *, announce_id: str | None = None, + ): + announce_id = announce_id or self.__announce_id + return await super().update( + title=title, text=text, image_url=image_url, announce_id=announce_id + ) + + +class AdminAnnouncementActions(SharedAdminAnnouncementActions): + def __init__( + self, *, session: HTTPClient, client: ClientManager, ): - super().__init__(announce_id=announce_id, session=session, client=client) + super().__init__(session=session, client=client) async def create(self, title: str, text: str, image_url: str | None = None) -> Announcement: body = {"title": title, "text": text, "imageUrl": image_url} - created_announcement: IAnnouncement = await self.__session.request( + created_announcement: IAnnouncement = await self._session.request( Route("POST", "/api/admin/announcements/create"), json=body, auth=True, lower=True, remove_none=False, ) - return Announcement(created_announcement, client=self.__client) + return Announcement(created_announcement, client=self._client) async def gets( self, @@ -97,13 +123,13 @@ class AdminAnnouncementActions(AdminAnnouncementClientActions): } pagination = Pagination[IAnnouncementDetailed]( - self.__session, Route("POST", "/api/admin/announcements/list"), json=body + self._session, Route("POST", "/api/admin/announcements/list"), json=body ) while True: res_annonuncement_systems = await pagination.next() for res_announcement_system in res_annonuncement_systems: - yield AnnouncementDetailed(res_announcement_system, client=self.__client) + yield AnnouncementDetailed(res_announcement_system, client=self._client) if get_all is False or pagination.is_final: break diff --git a/mipac/actions/antenna.py b/mipac/actions/antenna.py index 1155834..f95087f 100644 --- a/mipac/actions/antenna.py +++ b/mipac/actions/antenna.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, AsyncGenerator +from typing import TYPE_CHECKING, AsyncGenerator, override from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route @@ -16,15 +16,12 @@ if TYPE_CHECKING: from mipac.client import ClientManager -class ClientAntennaActions(AbstractAction): - def __init__( - self, *, antenna_id: str | None = None, session: HTTPClient, client: ClientManager - ): - self._antenna_id: str | None = antenna_id +class SharedAntennaActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def delete(self, antenna_id: str | None = None) -> bool: + async def delete(self, *, antenna_id: str) -> bool: """ Delete antenna from identifier @@ -42,9 +39,6 @@ class ClientAntennaActions(AbstractAction): bool success or failure """ - antenna_id = antenna_id or self._antenna_id - if antenna_id is None: - raise ValueError("antenna id is required") body = {"antennaId": antenna_id} res: bool = await self._session.request( @@ -52,7 +46,7 @@ class ClientAntennaActions(AbstractAction): ) return res - async def show(self, antenna_id: str | None = None) -> Antenna: + async def show(self, *, antenna_id: str) -> Antenna: """Show antenna from identifier Parameters @@ -70,10 +64,6 @@ class ClientAntennaActions(AbstractAction): ParameterError antenna id is required """ - antenna_id = antenna_id or self._antenna_id - if antenna_id is None: - raise ValueError("antenna id is required") - body = {"antennaId": antenna_id} res_antenna: IAntenna = await self._session.request( Route("POST", "/api/antennas/show"), auth=True, json=body @@ -82,19 +72,16 @@ class ClientAntennaActions(AbstractAction): async def get_notes( self, - antenna_id: str | None = None, limit: int = 10, since_id: str | None = None, until_id: str | None = None, since_date: str | None = None, until_date: str | None = None, get_all: bool = False, + *, + antenna_id: str, ) -> AsyncGenerator[Note, None]: - antenna_id = antenna_id or self._antenna_id - if antenna_id is None: - raise ValueError("antenna id is required") - - if limit > 100: + if limit > 100: # TODO: 廃止する raise ValueError("limit must be less than 100") if get_all: @@ -135,7 +122,8 @@ class ClientAntennaActions(AbstractAction): with_file: bool, notify: bool, user_list_id: str | None = None, - antenna_id: str | None = None, + *, + antenna_id: str, ) -> Antenna: """Update an antenna. @@ -167,11 +155,6 @@ class ClientAntennaActions(AbstractAction): Antenna The created antenna. """ - - antenna_id = antenna_id or self._antenna_id - if antenna_id is None: - raise ValueError("antenna id is required") - if ( all( [ @@ -204,7 +187,147 @@ class ClientAntennaActions(AbstractAction): return Antenna(res_antenna, client=self._client) -class AntennaActions(ClientAntennaActions): +class ClientAntennaActions(SharedAntennaActions): + def __init__(self, *, antenna_id: str, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self._antenna_id: str = antenna_id + + @override + async def delete(self, *, antenna_id: str | None = None) -> bool: + """ + Delete antenna from identifier + + Parameters + ---------- + antenna_id : str | None, optional + target identifier + + Raises + ------ + ParameterError + antenna id is required + Returns + ------- + bool + success or failure + """ + antenna_id = antenna_id or self._antenna_id + + return await super().delete(antenna_id=antenna_id) + + @override + async def show(self, *, antenna_id: str | None = None) -> Antenna: + """Show antenna from identifier + + Parameters + ---------- + antenna_id : str | None, optional + target identifier, by default None + + Returns + ------- + Antenna + antenna object + + Raises + ------ + ParameterError + antenna id is required + """ + antenna_id = antenna_id or self._antenna_id + + return await super().show(antenna_id=antenna_id) + + @override + async def get_notes( + self, + limit: int = 10, + since_id: str | None = None, + until_id: str | None = None, + since_date: str | None = None, + until_date: str | None = None, + get_all: bool = False, + *, + antenna_id: str | None = None, + ) -> AsyncGenerator[Note, None]: + antenna_id = antenna_id or self._antenna_id + + async for note in super().get_notes( + limit=limit, + since_id=since_id, + until_id=until_id, + since_date=since_date, + until_date=until_date, + get_all=get_all, + antenna_id=antenna_id, + ): + yield note + + @override + async def update( + self, + name: str, + src: IAntennaReceiveSource, + keywords: list[list[str]], + exclude_keywords: list[list[str]], + users: list[str], + case_sensitive: bool, + with_replies: bool, + with_file: bool, + notify: bool, + user_list_id: str | None = None, + *, + antenna_id: str | None = None, + ) -> Antenna: + """Update an antenna. + + Parameters + ---------- + name : str + Name of the antenna. + src : IAntennaReceiveSource + Receive source of the antenna. + keywords : list[list[str]] + Receive keywords. + exclude_keywords : list[list[str]] + Excluded keywords. + users : list[str] + List of target user ID. Required when selecting 'users' as the receive source. + case_sensitive : bool + Whether to differentiate between uppercase and lowercase letters. + with_replies : bool + Whether to include replies. + with_file : bool + Whether to limit to notes with attached files. + notify : bool + Whether to notify for new notes. + user_list_id : str | None, default None + List of user IDs when selecting 'users' as the receive source for the antenna. + + Returns + ------- + Antenna + The created antenna. + """ + + antenna_id = antenna_id or self._antenna_id + + return await super().update( + name=name, + src=src, + keywords=keywords, + exclude_keywords=exclude_keywords, + users=users, + case_sensitive=case_sensitive, + with_replies=with_replies, + with_file=with_file, + notify=notify, + user_list_id=user_list_id, + antenna_id=antenna_id, + ) + + +class AntennaActions(SharedAntennaActions): def __init__(self, *, session: HTTPClient, client: ClientManager): super().__init__(session=session, client=client) diff --git a/mipac/actions/clip.py b/mipac/actions/clip.py index 33440fe..38f3730 100644 --- a/mipac/actions/clip.py +++ b/mipac/actions/clip.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, AsyncGenerator +from typing import TYPE_CHECKING, AsyncGenerator, override from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route @@ -14,9 +14,8 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientClipActions(AbstractAction): - def __init__(self, *, clip_id: str | None = None, session: HTTPClient, client: ClientManager): - self._clip_id = clip_id +class SharedClipActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session = session self._client = client @@ -26,8 +25,9 @@ class ClientClipActions(AbstractAction): since_id: str | None = None, until_id: str | None = None, get_all: bool = False, + *, clip_id: str | None = None, - ) -> AsyncGenerator[Note, None]: + ) -> AsyncGenerator[Note, None]: # TODO: 作り直し """Get notes from a clip Parameters ---------- @@ -47,12 +47,6 @@ class ClientClipActions(AbstractAction): AsyncGenerator[Note, None] The notes """ - - clip_id = self._clip_id or clip_id - - if clip_id is None: - raise ValueError("clip_id is required") - if limit > 100: raise ValueError("limit must be less than 100") @@ -73,7 +67,7 @@ class ClientClipActions(AbstractAction): if get_all is False or pagination.is_final: break - async def add_note(self, note_id: str, clip_id: str | None = None) -> bool: + async def add_note(self, note_id: str, *, clip_id: str) -> bool: """Add a note to a clip Parameters @@ -88,18 +82,13 @@ class ClientClipActions(AbstractAction): bool True if the note was added to the clip, False otherwise """ - clip_id = self._clip_id or clip_id - - if clip_id is None: - raise ValueError("clip_id is required") - body = {"clipId": clip_id, "noteId": note_id} result: bool = await self._session.request( Route("POST", "/api/clips/add-note"), json=body, auth=True ) return result - async def remove_note(self, note_id: str, clip_id: str | None) -> bool: + async def remove_note(self, note_id: str, *, clip_id: str) -> bool: """Remove a note from a clip Parameters @@ -114,18 +103,13 @@ class ClientClipActions(AbstractAction): bool True if the note was removed from the clip, False otherwise """ - clip_id = self._clip_id or clip_id - - if clip_id is None: - raise ValueError("clip_id is required") - body = {"clipId": clip_id, "noteId": note_id} result: bool = await self._session.request( Route("POST", "/api/clips/remove-note"), json=body, auth=True ) return result - async def delete(self, clip_id: str | None = None) -> bool: + async def delete(self, *, clip_id: str) -> bool: """Delete a clip Parameters @@ -138,11 +122,6 @@ class ClientClipActions(AbstractAction): bool True if the clip was deleted, False otherwise """ - clip_id = self._clip_id or clip_id - - if clip_id is None: - raise ValueError("clip_id is required") - body = {"clipId": clip_id} result: bool = await self._session.request( Route("POST", "/api/clips/delete"), json=body, auth=True @@ -154,7 +133,8 @@ class ClientClipActions(AbstractAction): name: str, is_public: bool | None = None, description: str | None = None, - clip_id: str | None = None, + *, + clip_id: str, ) -> Clip: """Update a clip @@ -175,11 +155,6 @@ class ClientClipActions(AbstractAction): True if the clip was updated, False otherwise """ - clip_id = self._clip_id or clip_id - - if clip_id is None: - raise ValueError("clip_id is required") - body = {"clipId": clip_id, "name": name, "isPublic": is_public, "description": description} result: IClip = await self._session.request( Route("POST", "/api/clips/update"), json=body, auth=True @@ -187,9 +162,138 @@ class ClientClipActions(AbstractAction): return Clip(result, client=self._client) -class ClipActions(ClientClipActions): - def __init__(self, *, clip_id: str | None = None, session: HTTPClient, client: ClientManager): - super().__init__(clip_id=clip_id, session=session, client=client) +class ClientClipActions(SharedClipActions): # TODO: 使うようにする + def __init__(self, clip_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self._clip_id = clip_id + + @override + async def get_notes( + self, + limit: int = 10, + since_id: str | None = None, + until_id: str | None = None, + get_all: bool = False, + *, + clip_id: str | None = None, + ) -> AsyncGenerator[Note, None]: + """Get notes from a clip + Parameters + ---------- + clip_id : str | None, optional, by default None + The clip id + limit : int, optional, by default 10 + The number of notes to get + since_id : str | None, optional, by default None + The note id to get notes after + until_id : str | None, optional, by default None + The note id to get notes before + get_all : bool, optional, by default False + Whether to get all notes + + Yields + ------ + AsyncGenerator[Note, None] + The notes + """ + + clip_id = clip_id or self._clip_id + + async for note in super().get_notes( + limit=limit, since_id=since_id, until_id=until_id, get_all=get_all, clip_id=clip_id + ): + yield note + + async def add_note(self, note_id: str, clip_id: str | None = None) -> bool: + """Add a note to a clip + + Parameters + ---------- + clip_id : str | None, optional, by default None + The clip id + note_id : str + The note id + + Returns + ------- + bool + True if the note was added to the clip, False otherwise + """ + clip_id = clip_id or self._clip_id + + return await super().add_note(note_id=note_id, clip_id=clip_id) + + async def remove_note(self, note_id: str, clip_id: str | None) -> bool: + """Remove a note from a clip + + Parameters + ---------- + clip_id : str | None, optional, by default None + The clip id + note_id : str + The note id + + Returns + ------- + bool + True if the note was removed from the clip, False otherwise + """ + clip_id = clip_id or self._clip_id + + return await super().remove_note(note_id=note_id, clip_id=clip_id) + + async def delete(self, clip_id: str | None = None) -> bool: + """Delete a clip + + Parameters + ---------- + clip_id : str | None, optional, by default None + The clip id + + Returns + ------- + bool + True if the clip was deleted, False otherwise + """ + clip_id = clip_id or self._clip_id + + return await super().delete(clip_id=clip_id) + + async def update( + self, + name: str, + is_public: bool | None = None, + description: str | None = None, + clip_id: str | None = None, + ) -> Clip: + """Update a clip + + Parameters + ---------- + clip_id : str | None, optional, by default None + The clip id + name : str + The clip name + is_public : bool, optional + Whether the clip is public, by default None + description : str, optional + The clip description, by default None + + Returns + ------- + bool + True if the clip was updated, False otherwise + """ + clip_id = clip_id or self._clip_id + + return await super().update( + name=name, is_public=is_public, description=description, clip_id=clip_id + ) + + +class ClipActions(SharedClipActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) async def get_my_favorites(self): """Get my favorite clips diff --git a/mipac/actions/drive/files.py b/mipac/actions/drive/files.py index c7993bc..ed4034e 100644 --- a/mipac/actions/drive/files.py +++ b/mipac/actions/drive/files.py @@ -18,9 +18,8 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientFileActions(AbstractAction): - def __init__(self, file_ids: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__file_ids: str | None = file_ids +class SharedFileActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client @@ -30,7 +29,7 @@ class ClientFileActions(AbstractAction): until_id: str | None = None, limit: int = 10, *, - file_id: str | None = None, + file_id: str, ) -> list[Note]: """Get the attached notes of a file @@ -52,12 +51,6 @@ class ClientFileActions(AbstractAction): list[Note] The attached notes of the file """ - - file_id = file_id or self.__file_ids - - if file_id is None: - raise ValueError("file_id is required") - body = { "sinceId": since_id, "untilId": until_id, @@ -76,13 +69,8 @@ class ClientFileActions(AbstractAction): until_id: str | None = None, limit: int = 10, *, - file_id: str | None = None, + file_id: str, ) -> AsyncGenerator[Note, None]: - file_id = file_id or self.__file_ids - - if file_id is None: - raise ValueError("file_id is required") - body = { "sinceId": since_id, "untilId": until_id, @@ -101,7 +89,7 @@ class ClientFileActions(AbstractAction): for raw_note in await pagination.next(): yield Note(raw_note, client=self._client) - async def delete(self, *, file_id: str | None = None) -> bool: + async def delete(self, *, file_id: str) -> bool: """指定したファイルIDのファイルを削除します Endpoint: `/api/drive/files/delete` @@ -116,9 +104,6 @@ class ClientFileActions(AbstractAction): bool 削除に成功したかどうか """ - - file_id = file_id or self.__file_ids - data = {"fileId": file_id} res: bool = await self._session.request( @@ -133,7 +118,7 @@ class ClientFileActions(AbstractAction): is_sensitive: bool = MISSING, comment: str | None = MISSING, *, - file_id: str | None = None, + file_id: str, ) -> File: """指定したIDのファイル情報を更新します @@ -157,8 +142,6 @@ class ClientFileActions(AbstractAction): File 更新後のファイル """ - file_id = file_id or self.__file_ids - data = remove_dict_missing( { "fileId": file_id, @@ -175,7 +158,128 @@ class ClientFileActions(AbstractAction): return File(res, client=self._client) -class FileActions(ClientFileActions): +class ClientFileActions(SharedFileActions): + def __init__(self, file_ids: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self.__file_ids: str = file_ids + + @override + async def get_attached_notes( + self, + since_id: str | None = None, + until_id: str | None = None, + limit: int = 10, + *, + file_id: str | None = None, + ) -> list[Note]: + """Get the attached notes of a file + + Endpoint: `/api/drive/files/attached-notes` + + Parameters + ---------- + since_id: str | None + The id of the note to start from, defaults to None + until_id: str | None + The id of the note to end at, defaults to None + limit: int + The amount of notes to get, defaults to 10 + file_id: str | None + The id of the file to get notes from, defaults to None + + Returns + ------- + list[Note] + The attached notes of the file + """ + + file_id = file_id or self.__file_ids + + return await super().get_attached_notes( + since_id=since_id, until_id=until_id, limit=limit, file_id=file_id + ) + + @override + async def get_all_attached_notes( + self, + since_id: str | None = None, + until_id: str | None = None, + limit: int = 10, + *, + file_id: str | None = None, + ) -> AsyncGenerator[Note, None]: + file_id = file_id or self.__file_ids + + async for note in super().get_all_attached_notes( + since_id=since_id, until_id=until_id, limit=limit, file_id=file_id + ): + yield note + + @override + async def delete(self, *, file_id: str | None = None) -> bool: + """指定したファイルIDのファイルを削除します + + Endpoint: `/api/drive/files/delete` + + Parameters + ---------- + file_id: str | None + 対象のファイルID, default=None + + Returns + ------- + bool + 削除に成功したかどうか + """ + + file_id = file_id or self.__file_ids + + return await super().delete(file_id=file_id) + + @override + async def update( + self, + folder_id: str | None = MISSING, + name: str | None = MISSING, + is_sensitive: bool = MISSING, + comment: str | None = MISSING, + *, + file_id: str | None = None, + ) -> File: + """指定したIDのファイル情報を更新します + + Endpoint: `/api/drive/files/update` + + Parameters + ---------- + folder_id: str | None + ファイルを置くフォルダID, default=MISSING + name: str | None + ファイル名, default=MISSING + is_sensitive: bool + ファイルがセンシティブかどうか, default=MISSING + comment: str | None + ファイルのコメント, default=MISSING + file_id: str | None + 対象のファイルID, default=None + + Returns + ------- + File + 更新後のファイル + """ + file_id = file_id or self.__file_ids + + return await super().update( + folder_id=folder_id, + name=name, + is_sensitive=is_sensitive, + comment=comment, + file_id=file_id, + ) + + +class FileActions(SharedFileActions): def __init__(self, *, session: HTTPClient, client: ClientManager): super().__init__(session=session, client=client) @@ -254,66 +358,6 @@ class FileActions(ClientFileActions): for raw_file in await pagination.next(): yield File(raw_file, client=self._client) - @override - async def get_attached_notes( - self, - file_id: str, - since_id: str | None = None, - until_id: str | None = None, - limit: int = 10, - ) -> list[Note]: - """指定したファイルを含む全てのノートを取得します - - Parameters - ---------- - file_id: str - ノートを取得するファイルID - since_id: str | None - 指定するとそのノートIDよりも後のノートを返します, default=None - until_id: str | None - 指定するとそのノートIDよりも前のノートを返します, default=None - limit: int - 一度に取得するノート数, default=10 - - Returns - ------- - list[Note] - 取得したノート - """ - - return await super().get_attached_notes( - since_id=since_id, until_id=until_id, limit=limit, file_id=file_id - ) - - @override - async def get_all_attached_notes( - self, - file_id: str, - since_id: str | None = None, - until_id: str | None = None, - limit: int = 10, - ) -> AsyncGenerator[Note, None]: - """指定したファイルを含む全てのノートを取得します - - Parameters - ---------- - file_id: str - ノートを取得するファイルID - since_id: str | None - 指定するとそのノートIDよりも後のノートを返します, default=None - until_id: str | None - 指定するとそのノートIDよりも前のノートを返します, default=None - limit: int - 一度に取得するノート数, default=10 - - Returns - ------- - AsyncGenerator[Note, None] - 取得したノート - """ - async for i in super().get_all_attached_notes(since_id, until_id, limit, file_id=file_id): - yield i - @credentials_required async def check_existence(self, md5: str) -> bool: """指定したmd5のファイルが既に存在するか確認します @@ -398,24 +442,6 @@ class FileActions(ClientFileActions): ) return File(res, client=self._client) - async def delete(self, file_id: str) -> bool: - """指定したファイルIDのファイルを削除します - - Endpoint: `/api/drive/files/delete` - - Parameters - ---------- - file_id: str - 対象のファイルID - - Returns - ------- - bool - 削除に成功したかどうか - """ - - return await super().delete(file_id=file_id) - async def find_by_hash(self, md5: str) -> list[File]: """指定したハッシュのファイルを検索します @@ -489,45 +515,6 @@ class FileActions(ClientFileActions): ) return File(res, client=self._client) - async def update( - self, - file_id: str, - folder_id: str | None = MISSING, - name: str | None = MISSING, - is_sensitive: bool = MISSING, - comment: str | None = MISSING, - ) -> File: - """指定したIDのファイル情報を更新します - - Endpoint: `/api/drive/files/update` - - Parameters - ---------- - file_id: str - 対象のファイルID - folder_id: str | None - ファイルを置くフォルダID, default=MISSING - name: str | None - ファイル名, default=MISSING - is_sensitive: bool - ファイルがセンシティブかどうか, default=MISSING - comment: str | None - ファイルのコメント, default=MISSING - - Returns - ------- - File - 更新後のファイル - """ - - return await super().update( - file_id=file_id, - folder_id=folder_id, - name=name, - is_sensitive=is_sensitive, - comment=comment, - ) - async def upload_from_url( self, url: str, diff --git a/mipac/actions/drive/folders.py b/mipac/actions/drive/folders.py index 46328c9..7d96316 100644 --- a/mipac/actions/drive/folders.py +++ b/mipac/actions/drive/folders.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, override from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route @@ -154,11 +154,8 @@ class ClientFileActionsInFolder(AbstractAction): ) -class ClientFolderActions(AbstractAction): - def __init__( - self, folder_id: str | None = None, *, session: HTTPClient, client: ClientManager - ): - self.__folder_id: str | None = folder_id +class SharedFolderActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client @@ -168,7 +165,7 @@ class ClientFolderActions(AbstractAction): since_id: str | None = None, until_id: str | None = None, *, - folder_id: str | None = None, + folder_id: str, ) -> list[Folder]: """Get folders @@ -194,7 +191,7 @@ class ClientFolderActions(AbstractAction): "limit": limit, "sinceId": since_id, "untilId": until_id, - "folderId": folder_id or self.__folder_id, + "folderId": folder_id, } raw_folders: list[IFolder] = await self._session.request( Route("POST", "/api/drive/folders"), @@ -204,7 +201,7 @@ class ClientFolderActions(AbstractAction): return [Folder(raw_folder=raw_folder, client=self._client) for raw_folder in raw_folders] - async def create(self, name: str | None = None, *, parent_id: str | None = None) -> Folder: + async def create(self, name: str | None = None, parent_id: str | None = None) -> Folder: """Create a new folder Endpoint: `/api/drive/folders/create` @@ -221,8 +218,6 @@ class ClientFolderActions(AbstractAction): Folder The created folder """ - parent_id = parent_id or self.__folder_id - data = {"name": name, "parentId": parent_id} raw_created_folder: IFolder = await self._session.request( Route("POST", "/api/drive/folders/create"), auth=True, json=data @@ -230,7 +225,7 @@ class ClientFolderActions(AbstractAction): return Folder(raw_folder=raw_created_folder, client=self._client) - async def delete(self, folder_id: str | None = None) -> bool: + async def delete(self, *, folder_id: str) -> bool: """Delete a folder Endpoint: `/api/drive/folders/delete` @@ -245,8 +240,6 @@ class ClientFolderActions(AbstractAction): bool Whether the folder was deleted or not """ - folder_id = folder_id or self.__folder_id - res: bool = await self._session.request( Route("POST", "/api/drive/folders/delete"), auth=True, json={"folderId": folder_id} ) @@ -258,7 +251,7 @@ class ClientFolderActions(AbstractAction): name: str | None = MISSING, parent_id: str | None = MISSING, *, - folder_id: str | None = None, + folder_id: str, ) -> Folder: """Update a folder @@ -278,9 +271,7 @@ class ClientFolderActions(AbstractAction): Folder The updated folder """ - data = remove_dict_missing( - {"folderId": folder_id or self.__folder_id, "name": name, "parentId": parent_id} - ) + data = remove_dict_missing({"folderId": folder_id, "name": name, "parentId": parent_id}) raw_updated_folder: IFolder = await self._session.request( Route("POST", "/api/drive/folders/update"), auth=True, json=data ) @@ -288,15 +279,19 @@ class ClientFolderActions(AbstractAction): return Folder(raw_folder=raw_updated_folder, client=self._client) -class FolderActions(ClientFolderActions): - def __init__(self, *, session: HTTPClient, client: ClientManager): - super().__init__(session=session, client=client) +class ClientFolderActions(SharedFolderActions): + def __init__(self, folder_id: str, *, session: HTTPClient, client: ClientManager): + self.__folder_id: str = folder_id + self._session: HTTPClient = session + self._client: ClientManager = client + @override async def gets( self, limit: int = 10, since_id: str | None = None, until_id: str | None = None, + *, folder_id: str | None = None, ) -> list[Folder]: """Get folders @@ -305,24 +300,30 @@ class FolderActions(ClientFolderActions): Parameters ---------- - folder_id: str | None - The ID of the folder to get, defaults to None limit: int The limit of folders to get, defaults to 10 since_id: str | None The ID of the folder to get since, defaults to None until_id: str | None The ID of the folder to get until, defaults to None + folder_id: str | None + The ID of the folder to get, defaults to None Returns ------- list[Folder] The found folders """ + folder_id = folder_id or self.__folder_id + return await super().gets( - limit=limit, since_id=since_id, until_id=until_id, folder_id=folder_id + limit=limit, + since_id=since_id, + until_id=until_id, + folder_id=folder_id, ) + @override async def create(self, name: str | None = None, parent_id: str | None = None) -> Folder: """Create a new folder @@ -340,25 +341,64 @@ class FolderActions(ClientFolderActions): Folder The created folder """ + parent_id = parent_id or self.__folder_id + return await super().create(name=name, parent_id=parent_id) - async def delete(self, folder_id: str) -> bool: + @override + async def delete(self, folder_id: str | None = None) -> bool: """Delete a folder Endpoint: `/api/drive/folders/delete` Parameters ---------- - folder_id : str - The ID of the folder + folder_id : str, optional + The ID of the folder, by default None Returns ------- bool Whether the folder was deleted or not """ - res: bool = await super().delete(folder_id=folder_id) - return res + folder_id = folder_id or self.__folder_id + + return await super().delete(folder_id=folder_id) + + @override + async def update( + self, + name: str | None = MISSING, + parent_id: str | None = MISSING, + *, + folder_id: str | None = None, + ) -> Folder: + """Update a folder + + Endpoint: `/api/drive/folders/update` + + Parameters + ---------- + name : str, optional + The name of the folder, by default MISSING + parent_id : str, optional + The parent ID of the folder, by default MISSING + folder_id : str, optional + The ID of the folder, by default None + + Returns + ------- + Folder + The updated folder + """ + folder_id = folder_id or self.__folder_id + + return await super().update(name=name, parent_id=parent_id, folder_id=folder_id) + + +class FolderActions(SharedFolderActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) async def find(self, name: str, parent_id: str | None = None) -> list[Folder]: """Find folders @@ -409,26 +449,3 @@ class FolderActions(ClientFolderActions): ) return Folder(raw_folder=raw_folder, client=self._client) - - async def update( - self, folder_id: str | None = None, name: str | None = None, parent_id: str | None = None - ) -> Folder: - """Update a folder - - Endpoint: `/api/drive/folders/update` - - Parameters - ---------- - folder_id : str - The ID of the folder - name : str, optional - The name of the folder, by default None - parent_id : str, optional - The parent ID of the folder, by default None - - Returns - ------- - Folder - The updated folder - """ - return await super().update(name=name, parent_id=parent_id, folder_id=folder_id) diff --git a/mipac/actions/favorite.py b/mipac/actions/favorite.py index cee3a27..9a08125 100644 --- a/mipac/actions/favorite.py +++ b/mipac/actions/favorite.py @@ -10,18 +10,12 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientFavoriteActions(AbstractAction): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__note_id: str | None = note_id +class SharedFavoriteActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def create(self, *, note_id: str | None = None) -> bool: - note_id = note_id or self.__note_id - - if note_id is None: - raise ValueError("note_id is required") - + async def create(self, *, note_id: str) -> bool: data = {"noteId": note_id} return bool( await self._session.request( @@ -32,15 +26,10 @@ class ClientFavoriteActions(AbstractAction): ) @deprecated - async def add(self, *, note_id: str | None = None) -> bool: + async def add(self, *, note_id: str) -> bool: return await self.create(note_id=note_id) - async def delete(self, *, note_id: str | None = None) -> bool: - note_id = note_id or self.__note_id - - if note_id is None: - raise ValueError("note_id is required") - + async def delete(self, *, note_id: str) -> bool: data = {"noteId": note_id} return bool( await self._session.request( @@ -51,26 +40,42 @@ class ClientFavoriteActions(AbstractAction): ) @deprecated - async def remove(self, *, note_id: str | None = None) -> bool: + async def remove(self, *, note_id: str) -> bool: return await self.delete(note_id=note_id) -class FavoriteActions(ClientFavoriteActions): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - super().__init__(note_id, session=session, client=client) +class ClientFavoriteActions(SharedFavoriteActions): + def __init__(self, note_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self.__note_id: str = note_id @override - async def create(self, note_id: str) -> bool: + async def create(self, *, note_id: str | None = None) -> bool: + note_id = note_id or self.__note_id + return await super().create(note_id=note_id) + @deprecated @override - async def add(self, note_id: str) -> bool: - return await super().add(note_id=note_id) + async def add(self, *, note_id: str | None = None) -> bool: + note_id = note_id or self.__note_id + + return await super().create(note_id=note_id) @override - async def delete(self, note_id: str) -> bool: + async def delete(self, *, note_id: str | None = None) -> bool: + note_id = note_id or self.__note_id + return await super().delete(note_id=note_id) + @deprecated @override - async def remove(self, note_id: str) -> bool: - return await super().remove(note_id=note_id) + async def remove(self, *, note_id: str | None = None) -> bool: + note_id = note_id or self.__note_id + + return await super().delete(note_id=note_id) + + +class FavoriteActions(SharedFavoriteActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) diff --git a/mipac/actions/invite.py b/mipac/actions/invite.py index 81acf4f..cb0c3b5 100644 --- a/mipac/actions/invite.py +++ b/mipac/actions/invite.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, AsyncGenerator +from typing import TYPE_CHECKING, AsyncGenerator, override from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route @@ -13,13 +13,12 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientInviteActions(AbstractAction): - def __init__(self, invite_id: str | None, *, session: HTTPClient, client: ClientManager): - self._invite_id: str | None = invite_id +class SharedInviteActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def delete(self, *, invite_id: str | None = None) -> bool: + async def delete(self, *, invite_id: str) -> bool: """Delete an invite code. Parameters @@ -33,8 +32,6 @@ class ClientInviteActions(AbstractAction): Whether the invite code was deleted. """ - invite_id = invite_id or self._invite_id - res: bool = await self._session.request( Route("POST", "/api/invite/delete"), json={"inviteId": invite_id}, auth=True ) @@ -42,11 +39,34 @@ class ClientInviteActions(AbstractAction): return res -class InviteActions(ClientInviteActions): - def __init__( - self, invite_id: str | None = None, *, session: HTTPClient, client: ClientManager - ): - super().__init__(invite_id=invite_id, session=session, client=client) +class ClientInviteActions(SharedInviteActions): + def __init__(self, invite_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self._invite_id: str = invite_id + + @override + async def delete(self, *, invite_id: str | None = None) -> bool: + """Delete an invite code. + + Parameters + ---------- + invite_id : str | None, optional + The invite code to delete, by default None + + Returns + ------- + bool + Whether the invite code was deleted. + """ + + invite_id = invite_id or self._invite_id + + return await super().delete(invite_id=invite_id) + + +class InviteActions(SharedInviteActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) async def create(self) -> InviteCode: """Create a new invite code. @@ -63,24 +83,6 @@ class InviteActions(ClientInviteActions): ) return InviteCode(raw_code, client=self._client) - async def delete(self, invite_id: str) -> bool: - """Delete an invite code. - - Endpoint: `/api/invite/delete` - - Parameters - ---------- - invite_id : str - The invite code to delete - - Returns - ------- - bool - Whether the invite code was deleted. - """ - - return await super().delete(invite_id=invite_id) - @credentials_required async def get_list( self, limit: int = 30, since_id: str | None = None, until_id: str | None = None diff --git a/mipac/actions/poll.py b/mipac/actions/poll.py index bd96808..9181564 100644 --- a/mipac/actions/poll.py +++ b/mipac/actions/poll.py @@ -13,18 +13,17 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientPollActions(AbstractAction): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self._note_id: str | None = note_id +class SharedPollActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def vote(self, choice: int, *, note_id: str | None = None) -> bool: - note_id = note_id or self._note_id - - if note_id is None: - raise ValueError("note_id is required") - + async def vote( + self, + choice: int, + *, + note_id: str, + ) -> bool: data = {"noteId": note_id, "choice": choice} res: bool = await self._session.request( Route("POST", "/api/notes/polls/vote"), auth=True, json=data @@ -32,9 +31,21 @@ class ClientPollActions(AbstractAction): return res -class PollActions(ClientPollActions): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - super().__init__(note_id=note_id, session=session, client=client) +class ClientPollActions(SharedPollActions): + def __init__(self, note_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self._note_id: str = note_id + + @override + async def vote(self, choice: int, *, note_id: str | None = None) -> bool: + note_id = note_id or self._note_id + + return await super().vote(choice=choice, note_id=note_id) + + +class PollActions(SharedPollActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) @credentials_required async def recommendation(self, limit: int = 100, offset: int = 0): @@ -68,7 +79,3 @@ class PollActions(ClientPollActions): yield Note(note, client=self._client) if pagination.is_final: break - - @override - async def vote(self, note_id: str, choice: int) -> bool: - return await super().vote(note_id=note_id, choice=choice) diff --git a/mipac/actions/reaction.py b/mipac/actions/reaction.py index ee4723f..27310cc 100644 --- a/mipac/actions/reaction.py +++ b/mipac/actions/reaction.py @@ -15,13 +15,12 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager -class ClientReactionActions(AbstractAction): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__note_id: str | None = note_id - self.__session: HTTPClient = session - self.__client: ClientManager = client +class SharedReactionActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager) -> None: + self._session: HTTPClient = session + self._client: ClientManager = client - async def add(self, reaction: str, *, note_id: str | None = None) -> bool: + async def add(self, reaction: str, *, note_id: str) -> bool: """Add reaction to note Endpoint: `/api/notes/reactions/create` @@ -38,15 +37,12 @@ class ClientReactionActions(AbstractAction): bool success or not """ - - note_id = note_id or self.__note_id - data = remove_dict_empty({"noteId": note_id, "reaction": reaction}) route = Route("POST", "/api/notes/reactions/create") - res: bool = await self.__session.request(route, json=data, auth=True, lower=True) + res: bool = await self._session.request(route, json=data, auth=True, lower=True) return bool(res) - async def remove(self, *, note_id: str | None = None) -> bool: + async def remove(self, *, note_id: str) -> bool: """Remove reaction from note Endpoint: `/api/notes/reactions/delete` @@ -61,11 +57,9 @@ class ClientReactionActions(AbstractAction): bool success or not """ - note_id = note_id or self.__note_id - data = remove_dict_empty({"noteId": note_id}) route = Route("POST", "/api/notes/reactions/delete") - res: bool = await self.__session.request(route, json=data, auth=True, lower=True) + res: bool = await self._session.request(route, json=data, auth=True, lower=True) return bool(res) @cache(group="get_note_reaction") @@ -76,13 +70,8 @@ class ClientReactionActions(AbstractAction): since_id: str | None = None, until_id: str | None = None, *, - note_id: str | None = None, + note_id: str, ) -> list[NoteReaction]: - note_id = note_id or self.__note_id - - if note_id is None: - raise ValueError("note_id is required.") - data = remove_dict_empty( { "noteId": note_id, @@ -92,13 +81,13 @@ class ClientReactionActions(AbstractAction): "untilId": until_id, } ) - res: list[INoteReaction] = await self.__session.request( + res: list[INoteReaction] = await self._session.request( Route("POST", "/api/notes/reactions"), json=data, auth=True, lower=True, ) - return [NoteReaction(i, client=self.__client) for i in res] + return [NoteReaction(i, client=self._client) for i in res] @cache(group="get_note_reaction", override=True) async def fetch_reactions( @@ -108,58 +97,105 @@ class ClientReactionActions(AbstractAction): since_id: str | None = None, until_id: str | None = None, *, - note_id: str | None = None, + note_id: str, ) -> list[NoteReaction]: return await self.get_reactions( type=type, note_id=note_id, limit=limit, since_id=since_id, until_id=until_id ) -class ReactionActions(ClientReactionActions): - def __init__(self, *, session: HTTPClient, client: ClientManager): +class ClientReactionActions(SharedReactionActions): + def __init__(self, note_id: str, *, session: HTTPClient, client: ClientManager) -> None: super().__init__(session=session, client=client) + self.__note_id: str = note_id @override - async def add(self, note_id: str, reaction: str): + async def add(self, reaction: str, *, note_id: str | None = None) -> bool: + """Add reaction to note + + Endpoint: `/api/notes/reactions/create` + + Parameters + ---------- + reaction : str + reaction + note_id : str, optional + note id, by default None + + Returns + ------- + bool + success or not + """ + + note_id = note_id or self.__note_id + return await super().add(reaction=reaction, note_id=note_id) @override - async def remove(self, note_id: str): + async def remove(self, *, note_id: str | None = None) -> bool: + """Remove reaction from note + + Endpoint: `/api/notes/reactions/delete` + + Parameters + ---------- + note_id : str, optional + note id, by default None + + Returns + ------- + bool + success or not + """ + note_id = note_id or self.__note_id + return await super().remove(note_id=note_id) @override async def get_reactions( self, - note_id: str, type: str | None = None, limit: int = 10, since_id: str | None = None, until_id: str | None = None, - ): + *, + note_id: str | None = None, + ) -> list[NoteReaction]: + note_id = note_id or self.__note_id + return await super().get_reactions( - type=type, limit=limit, since_id=since_id, until_id=until_id, note_id=note_id + type=type, note_id=note_id, limit=limit, since_id=since_id, until_id=until_id ) @override async def fetch_reactions( self, - note_id: str, type: str | None = None, limit: int = 10, since_id: str | None = None, until_id: str | None = None, - ): + *, + note_id: str | None = None, + ) -> list[NoteReaction]: + note_id = note_id or self.__note_id + return await super().fetch_reactions( - type=type, limit=limit, since_id=since_id, until_id=until_id, note_id=note_id + type=type, note_id=note_id, limit=limit, since_id=since_id, until_id=until_id ) + +class ReactionActions(SharedReactionActions): + def __init__(self, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + async def get_emoji_list( self, ) -> list[CustomEmoji]: # TODO: metaからemojisは削除されてるので別の方法に切り替える - data: IPartialMeta = await self.__session.request( + data: IPartialMeta = await self._session.request( Route("GET", "/api/meta"), json={"detail": False}, auth=True, replace_list={"ToSUrl": "tos_url", "ToSTextUrl": "tos_text_url"}, ) - return [CustomEmoji(i, client=self.__client) for i in data.get("emojis", [])] + return [CustomEmoji(i, client=self._client) for i in data.get("emojis", [])] diff --git a/mipac/actions/users/mute.py b/mipac/actions/users/mute.py index e2357d6..fd1d2c0 100644 --- a/mipac/actions/users/mute.py +++ b/mipac/actions/users/mute.py @@ -12,13 +12,12 @@ if TYPE_CHECKING: from mipac.client import ClientManager -class ClientMuteActions(AbstractAction): - def __init__(self, user_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__user_id: str | None = user_id +class SharedMuteActions(AbstractAction): + def __init__(self, *, session: HTTPClient, client: ClientManager): self._session: HTTPClient = session self._client: ClientManager = client - async def create(self, expires_at: int | None = None, *, user_id: str | None = None) -> bool: + async def create(self, expires_at: int | None = None, *, user_id: str) -> bool: """指定したユーザーをミュートします Parameters @@ -33,17 +32,13 @@ class ClientMuteActions(AbstractAction): bool ミュートに成功したかどうか """ - - if user_id or self.__user_id: - raise ValueError("Parameter 'user_id' is required.") - body = {"userId": user_id, "expiresAt": expires_at} res: bool = await self._session.request(route=Route("POST", "/api/mute/create"), json=body) return res - async def delete(self, *, user_id: str | None = None) -> bool: + async def delete(self, *, user_id: str) -> bool: """指定したユーザーのミュートを解除します Parameters @@ -56,10 +51,6 @@ class ClientMuteActions(AbstractAction): bool ミュート解除に成功したかどうか """ - - if user_id or self.__user_id: - raise ValueError("Parameter 'user_id' is required.") - res: bool = await self._session.request( route=Route("POST", "/api/mute/delete"), json={"userId": user_id} ) @@ -67,7 +58,51 @@ class ClientMuteActions(AbstractAction): return res -class MuteActions(ClientMuteActions): +class ClientMuteActions(SharedMuteActions): + def __init__(self, user_id: str, *, session: HTTPClient, client: ClientManager): + super().__init__(session=session, client=client) + self.__user_id: str = user_id + + @override + async def create(self, expires_at: int | None = None, *, user_id: str | None = None) -> bool: + """指定したユーザーをミュートします + + Parameters + ---------- + user_id : str + 対象のユーザーID + expires_at : int | None + ミュートする期間(秒)、無期限でミュートする場合はNoneを指定します + + Returns + ------- + bool + ミュートに成功したかどうか + """ + user_id = user_id or self.__user_id + + return await super().create(expires_at=expires_at, user_id=user_id or self.__user_id) + + @override + async def delete(self, *, user_id: str | None = None) -> bool: + """指定したユーザーのミュートを解除します + + Parameters + ---------- + user_id : str + 対象のユーザーID + + Returns + ------- + bool + ミュート解除に成功したかどうか + """ + user_id = user_id or self.__user_id + + return await super().delete(user_id=user_id or self.__user_id) + + +class MuteActions(SharedMuteActions): def __init__(self, *, session: HTTPClient, client: ClientManager): super().__init__(session=session, client=client) @@ -118,41 +153,3 @@ class MuteActions(ClientMuteActions): while pagination.is_final is False: for raw_muted_user in await pagination.next(): yield MutedUser(raw_mute_user=raw_muted_user, client=self._client) - - @override - async def create( - self, - user_id: str, - expires_at: int | None = None, - ) -> bool: - """指定したユーザーをミュートします - - Parameters - ---------- - user_id : str - 対象のユーザーID - expires_at : int | None - ミュートする期間(秒)、無期限でミュートする場合はNoneを指定します - - Returns - ------- - bool - ミュートに成功したかどうか - """ - return await super().create(user_id=user_id, expires_at=expires_at) - - @override - async def delete(self, user_id: str) -> bool: - """指定したユーザーのミュートを解除します - - Parameters - ---------- - user_id : str - 対象のユーザーID - - Returns - ------- - bool - ミュート解除に成功したかどうか - """ - return await super().delete(user_id=user_id) diff --git a/mipac/manager/admins/ad.py b/mipac/manager/admins/ad.py index e0f1646..be01590 100644 --- a/mipac/manager/admins/ad.py +++ b/mipac/manager/admins/ad.py @@ -3,36 +3,36 @@ from __future__ import annotations from typing import TYPE_CHECKING, Literal from mipac.abstract.manager import AbstractManager -from mipac.actions.admins.ad import AdminAdvertisingActions, AdminAdvertisingModelActions +from mipac.actions.admins.ad import AdminAdActions, ClientAdminAdActions from mipac.http import HTTPClient, Route if TYPE_CHECKING: from mipac.client import ClientManager -__all__ = ("AdminAdvertisingManager", "AdminAdvertisingModelManager") +__all__ = ("AdminAdManager", "ClientAdminAdManager") -class AdminAdvertisingModelManager(AbstractManager): - def __init__(self, ad_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__ad_id: str | None = ad_id +class ClientAdminAdManager(AbstractManager): + def __init__(self, ad_id: str, *, session: HTTPClient, client: ClientManager): + self.__ad_id: str = ad_id self.__session: HTTPClient = session self.__client: ClientManager = client @property - def action(self) -> AdminAdvertisingModelActions: - return AdminAdvertisingModelActions( + def action(self) -> ClientAdminAdActions: + return ClientAdminAdActions( ad_id=self.__ad_id, session=self.__session, client=self.__client ) -class AdminAdvertisingManager(AbstractManager): +class AdminAdManager(AbstractManager): def __init__(self, *, session: HTTPClient, client: ClientManager): self.__session: HTTPClient = session self.__client: ClientManager = client @property - def action(self) -> AdminAdvertisingActions: - return AdminAdvertisingActions(session=self.__session, client=self.__client) + def action(self) -> AdminAdActions: + return AdminAdActions(session=self.__session, client=self.__client) async def create( self, diff --git a/mipac/manager/admins/admin.py b/mipac/manager/admins/admin.py index b47c5dd..d78cb51 100644 --- a/mipac/manager/admins/admin.py +++ b/mipac/manager/admins/admin.py @@ -5,8 +5,11 @@ from typing import TYPE_CHECKING from mipac.abstract.manager import AbstractManager from mipac.actions.admins.admin import AdminActions from mipac.http import HTTPClient -from mipac.manager.admins.ad import AdminAdvertisingManager, AdminAdvertisingModelManager -from mipac.manager.admins.announcement import AdminAnnouncementManager +from mipac.manager.admins.ad import AdminAdManager, ClientAdminAdManager +from mipac.manager.admins.announcement import ( + AdminAnnouncementManager, + ClientAdminAnnouncementManager, +) from mipac.manager.admins.drive import AdminDriveManager from mipac.manager.admins.emoji import AdminEmojiManager from mipac.manager.admins.invite import AdminInviteManager @@ -24,7 +27,7 @@ class AdminManager(AbstractManager): self.__client: ClientManager = client self.emoji: AdminEmojiManager = AdminEmojiManager(session=session, client=client) self.user: AdminUserManager = AdminUserManager(session=session, client=client) - self.ad: AdminAdvertisingManager = AdminAdvertisingManager(session=session, client=client) + self.ad: AdminAdManager = AdminAdManager(session=session, client=client) self.moderator: AdminModeratorManager = AdminModeratorManager( session=session, client=client ) @@ -44,7 +47,14 @@ class AdminManager(AbstractManager): role_id=role_id, session=self.__session, client=self.__client ) - def create_ad_model_manager(self, ad_id: str | None = None) -> AdminAdvertisingModelManager: - return AdminAdvertisingModelManager( - ad_id=ad_id, session=self.__session, client=self.__client + def _create_client_ad_manager(self, ad_id: str) -> ClientAdminAdManager: + return ClientAdminAdManager(ad_id=ad_id, session=self.__session, client=self.__client) + + def _create_client_announcement_manager( + self, announce_id: str + ) -> ClientAdminAnnouncementManager: + return ClientAdminAnnouncementManager( + announce_id=announce_id, + session=self.__session, + client=self.__client, ) diff --git a/mipac/manager/admins/announcement.py b/mipac/manager/admins/announcement.py index c17fe22..aca93e9 100644 --- a/mipac/manager/admins/announcement.py +++ b/mipac/manager/admins/announcement.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING from mipac.abstract.manager import AbstractManager from mipac.actions.admins.announcement import ( AdminAnnouncementActions, - AdminAnnouncementClientActions, + ClientAdminAnnouncementActions, ) from mipac.http import HTTPClient @@ -13,20 +13,27 @@ if TYPE_CHECKING: from mipac.manager.client import ClientManager +class ClientAdminAnnouncementManager(AbstractManager): + def __init__(self, announce_id: str, *, session: HTTPClient, client: ClientManager): + self.__announce_id: str = announce_id + self.__session: HTTPClient = session + self.__client: ClientManager = client + + @property + def action(self) -> ClientAdminAnnouncementActions: + return ClientAdminAnnouncementActions( + announce_id=self.__announce_id, session=self.__session, client=self.__client + ) + + class AdminAnnouncementManager(AbstractManager): def __init__(self, *, session: HTTPClient, client: ClientManager): self.__session: HTTPClient = session self.__client: ClientManager = client + self.__action: AdminAnnouncementActions = AdminAnnouncementActions( + session=self.__session, client=self.__client + ) @property def action(self) -> AdminAnnouncementActions: - return AdminAnnouncementActions(session=self.__session, client=self.__client) - - def _create_client_announcement_instance( - self, announce_id: str - ) -> AdminAnnouncementClientActions: - return AdminAnnouncementClientActions( - announce_id=announce_id, - session=self.__session, - client=self.__client, - ) + return self.__action diff --git a/mipac/manager/client.py b/mipac/manager/client.py index 515064c..355609d 100644 --- a/mipac/manager/client.py +++ b/mipac/manager/client.py @@ -8,7 +8,7 @@ from mipac.manager.admins.admin import AdminManager from mipac.manager.antenna import AntennaManager from mipac.manager.channel import ChannelManager, ClientChannelManager from mipac.manager.chart import ChartManager -from mipac.manager.clip import ClipManager +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 @@ -83,5 +83,8 @@ class ClientManager: def _create_client_user_manager(self, user: PartialUser) -> ClientUserManager: return ClientUserManager(user=user, session=self.__session, client=self) + def _get_client_clip_instance(self, *, clip_id: str) -> ClientClipManager: + return ClientClipManager(clip_id=clip_id, session=self.__session, client=self) + async def get_me(self) -> MeDetailed: return await self.user.action.get_me() diff --git a/mipac/manager/clip.py b/mipac/manager/clip.py index 670bc65..72e8398 100644 --- a/mipac/manager/clip.py +++ b/mipac/manager/clip.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING from mipac.abstract.manager import AbstractManager -from mipac.actions.clip import ClipActions +from mipac.actions.clip import ClientClipActions, ClipActions from mipac.http import HTTPClient if TYPE_CHECKING: @@ -13,7 +13,7 @@ if TYPE_CHECKING: class ClientClipManager(AbstractManager): def __init__( self, - clip_id: str | None = None, + clip_id: str, *, session: HTTPClient, client: ClientManager, @@ -23,8 +23,8 @@ class ClientClipManager(AbstractManager): self.__client = client @property - def action(self) -> ClipActions: - return ClipActions( + def action(self) -> ClientClipActions: + return ClientClipActions( clip_id=self.__clip_id, session=self.__session, client=self.__client, @@ -39,6 +39,3 @@ class ClipManager(AbstractManager): @property def action(self) -> ClipActions: return ClipActions(session=self.__session, client=self.__client) - - def _get_client_clip_instance(self, *, clip_id: str | None = None) -> ClientClipManager: - return ClientClipManager(clip_id=clip_id, session=self.__session, client=self.__client) diff --git a/mipac/manager/note.py b/mipac/manager/note.py index c02b738..7977e39 100644 --- a/mipac/manager/note.py +++ b/mipac/manager/note.py @@ -43,8 +43,8 @@ class NoteManager(AbstractManager): self.__session: HTTPClient = session self.__client: ClientManager = client self.reaction: ReactionManager = ReactionManager(session=session, client=client) - self.favorite = FavoriteManager(note_id=note_id, session=session, client=client) - self.poll: PollManager = PollManager(note_id=note_id, session=session, client=client) + self.favorite = FavoriteManager(session=session, client=client) + self.poll: PollManager = PollManager(session=session, client=client) self.__action: NoteActions = NoteActions( note_id=self.__note_id, session=self.__session, diff --git a/mipac/manager/poll.py b/mipac/manager/poll.py index 7a3f0d5..dc69ed8 100644 --- a/mipac/manager/poll.py +++ b/mipac/manager/poll.py @@ -11,30 +11,25 @@ if TYPE_CHECKING: class ClientPollManager(AbstractManager): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__note_id: str | None = note_id + def __init__(self, note_id: str, *, session: HTTPClient, client: ClientManager): + self.__note_id: str = note_id self.__session: HTTPClient = session self.__client: ClientManager = client + self.__action: ClientPollActions = ClientPollActions( + note_id=self.__note_id, session=self.__session, client=self.__client + ) @property def action(self) -> ClientPollActions: - return ClientPollActions( - note_id=self.__note_id, - session=self.__session, - client=self.__client, - ) + return self.__action class PollManager(AbstractManager): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): - self.__note_id: str | None = note_id + def __init__(self, *, session: HTTPClient, client: ClientManager): self.__session: HTTPClient = session self.__client: ClientManager = client + self.__action: PollActions = PollActions(session=self.__session, client=self.__client) @property def action(self) -> PollActions: - return PollActions( - note_id=self.__note_id, - session=self.__session, - client=self.__client, - ) + return self.__action diff --git a/mipac/manager/reaction.py b/mipac/manager/reaction.py index 0ebeb4d..c1e2701 100644 --- a/mipac/manager/reaction.py +++ b/mipac/manager/reaction.py @@ -11,11 +11,12 @@ if TYPE_CHECKING: class ClientReactionManager(AbstractManager): - def __init__(self, note_id: str | None = None, *, session: HTTPClient, client: ClientManager): + def __init__(self, note_id: str, *, session: HTTPClient, client: ClientManager): self.__note_id: str | None = note_id self.__session: HTTPClient = session self.__client: ClientManager = client self.__action = ClientReactionActions( + note_id=self.__note_id, session=self.__session, client=self.__client, ) diff --git a/mipac/models/announcement.py b/mipac/models/announcement.py index b795bb0..342c0e9 100644 --- a/mipac/models/announcement.py +++ b/mipac/models/announcement.py @@ -7,7 +7,7 @@ from mipac.types.announcement import IAnnouncement, IAnnouncementDetailed from mipac.utils.format import str_to_datetime if TYPE_CHECKING: - from mipac.actions.admins.announcement import AdminAnnouncementClientActions + from mipac.manager.admins.announcement import ClientAdminAnnouncementManager from mipac.manager.client import ClientManager @@ -75,10 +75,8 @@ class Announcement: return not self.__eq__(__value) @property - def action(self) -> AdminAnnouncementClientActions: - return self.__client.admin.announcement._create_client_announcement_instance( - announce_id=self.id - ) + def action(self) -> ClientAdminAnnouncementManager: + return self.__client.admin._create_client_announcement_manager(announce_id=self.id) class AnnouncementDetailed: @@ -154,7 +152,5 @@ class AnnouncementDetailed: return not self.__eq__(__value) @property - def action(self) -> AdminAnnouncementClientActions: - return self.__client.admin.announcement._create_client_announcement_instance( - announce_id=self.id - ) + def action(self) -> ClientAdminAnnouncementManager: + return self.__client.admin._create_client_announcement_manager(announce_id=self.id) diff --git a/mipac/models/clip.py b/mipac/models/clip.py index a7a9f32..e5d6952 100644 --- a/mipac/models/clip.py +++ b/mipac/models/clip.py @@ -67,4 +67,4 @@ class Clip: @property def api(self) -> ClientClipManager: - return self.__client.clip._get_client_clip_instance(clip_id=self.id) + return self.__client._get_client_clip_instance(clip_id=self.id) diff --git a/mipac/models/lite/ad.py b/mipac/models/lite/ad.py index 9bcda0e..f8cc481 100644 --- a/mipac/models/lite/ad.py +++ b/mipac/models/lite/ad.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, TypeVar from mipac.types.ads import IAdPlaces, IPartialAd if TYPE_CHECKING: - from mipac.manager.admins.ad import AdminAdvertisingModelManager + from mipac.manager.admins.ad import ClientAdminAdManager from mipac.manager.client import ClientManager @@ -53,8 +53,8 @@ class PartialAd[T: IPartialAd]: return self._raw_ad.get(key) @property - def api(self) -> AdminAdvertisingModelManager: - return self._client.admin.create_ad_model_manager(ad_id=self.id) + def api(self) -> ClientAdminAdManager: + return self._client.admin._create_client_ad_manager(ad_id=self.id) def __eq__(self, __value: object) -> bool: return isinstance(__value, PartialAd) and self.id == __value.id diff --git a/mipac/models/mute.py b/mipac/models/mute.py index 087d97d..bd6ef34 100644 --- a/mipac/models/mute.py +++ b/mipac/models/mute.py @@ -40,8 +40,8 @@ class MutedUser: def mutee(self) -> UserDetailedNotMe: return UserDetailedNotMe(self.__raw_mute_user["mutee"], client=self.__client) - def __eq__(self, __value: MutedUser) -> bool: + def __eq__(self, __value: object) -> bool: return isinstance(__value, MutedUser) and self.id == __value.id - def __ne__(self, __value: MutedUser) -> bool: + def __ne__(self, __value: object) -> bool: return not self.__eq__(__value)