From d12159ee335b5230d345ae200512fc4a302b25a6 Mon Sep 17 00:00:00 2001 From: yupix Date: Mon, 17 Apr 2023 11:50:15 +0000 Subject: [PATCH 1/5] chore: format --- mipac/actions/channel.py | 1 - mipac/config.py | 1 + mipac/manager/channel.py | 9 ++++++--- mipac/models/lite/channel.py | 2 +- mipac/models/note.py | 1 - mipac/utils/util.py | 1 + 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mipac/actions/channel.py b/mipac/actions/channel.py index b27eddf..77a8c21 100644 --- a/mipac/actions/channel.py +++ b/mipac/actions/channel.py @@ -71,4 +71,3 @@ class ChannelActions(ClientChannelActions): Route('POST', '/api/channels/my-favorites'), auth=True ) return [Channel(i, client=self._client) for i in res] - diff --git a/mipac/config.py b/mipac/config.py index d010dce..51c1164 100644 --- a/mipac/config.py +++ b/mipac/config.py @@ -17,6 +17,7 @@ class CacheConfig: IMisskeyDistribution = Literal['ayuskey', 'm544', 'areionskey', 'official'] IMisskeyVersions = Literal[13, 12, 11] + class ILimits(TypedDict, total=False): channel_name: int channel_description: int diff --git a/mipac/manager/channel.py b/mipac/manager/channel.py index 9094779..5f1d2af 100644 --- a/mipac/manager/channel.py +++ b/mipac/manager/channel.py @@ -1,4 +1,3 @@ - from __future__ import annotations from typing import TYPE_CHECKING @@ -11,11 +10,15 @@ if TYPE_CHECKING: class ChannelManager(AbstractManager): - def __init__(self, channel_id: str | None = None, *, session: HTTPClient, client: ClientManager): + def __init__( + self, channel_id: str | None = None, *, session: HTTPClient, client: ClientManager + ): self.__channel_id: str | None = channel_id self.__session: HTTPClient = session self.__client: ClientManager = client @property def action(self) -> ChannelActions: - return ChannelActions(channel_id=self.__channel_id, session=self.__session, client=self.__client) + return ChannelActions( + channel_id=self.__channel_id, session=self.__session, client=self.__client + ) diff --git a/mipac/models/lite/channel.py b/mipac/models/lite/channel.py index dbe0787..5183da0 100644 --- a/mipac/models/lite/channel.py +++ b/mipac/models/lite/channel.py @@ -8,7 +8,7 @@ from mipac.utils.format import str_to_datetime if TYPE_CHECKING: from mipac.manager import ClientManager from mipac.manager.channel import ChannelManager - + T = TypeVar('T', bound=IChannelLite) diff --git a/mipac/models/note.py b/mipac/models/note.py index 37267ec..fb48276 100644 --- a/mipac/models/note.py +++ b/mipac/models/note.py @@ -206,7 +206,6 @@ class Note(PartialNote[INote]): return Poll(self._note['poll'], client=self._client) if 'poll' in self._note else None - class NoteTranslateResult: """ NoteTranslateResult diff --git a/mipac/utils/util.py b/mipac/utils/util.py index 7354e43..dac38c4 100644 --- a/mipac/utils/util.py +++ b/mipac/utils/util.py @@ -66,4 +66,5 @@ class Colors: self.green = '\x1b[92;1m' self.reset = '\x1b[0m' + COLORS = Colors() From 6c851e86010ab09a6452249722515695afa0c195 Mon Sep 17 00:00:00 2001 From: yupix Date: Mon, 17 Apr 2023 11:52:19 +0000 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20import=E3=82=92=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mipac/actions/channel.py | 5 ++--- mipac/manager/channel.py | 3 ++- mipac/models/lite/channel.py | 1 + mipac/models/lite/note.py | 4 ++-- mipac/models/note.py | 10 ++++++++-- mipac/utils/util.py | 2 +- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/mipac/actions/channel.py b/mipac/actions/channel.py index 77a8c21..cc29a53 100644 --- a/mipac/actions/channel.py +++ b/mipac/actions/channel.py @@ -1,12 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, overload +from typing import TYPE_CHECKING from mipac.abstract.action import AbstractAction from mipac.http import HTTPClient, Route from mipac.models.channel import Channel -from mipac.models.lite.channel import ChannelLite -from mipac.types.channel import IChannel, IChannelLite +from mipac.types.channel import IChannel if TYPE_CHECKING: from mipac.client import ClientManager diff --git a/mipac/manager/channel.py b/mipac/manager/channel.py index 5f1d2af..bb60afc 100644 --- a/mipac/manager/channel.py +++ b/mipac/manager/channel.py @@ -1,9 +1,10 @@ from __future__ import annotations + from typing import TYPE_CHECKING from mipac.abstract.manager import AbstractManager -from mipac.http import HTTPClient from mipac.actions.channel import ChannelActions +from mipac.http import HTTPClient if TYPE_CHECKING: from mipac.manager.client import ClientManager diff --git a/mipac/models/lite/channel.py b/mipac/models/lite/channel.py index 5183da0..f97a35a 100644 --- a/mipac/models/lite/channel.py +++ b/mipac/models/lite/channel.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import datetime from typing import TYPE_CHECKING, Generic, TypeVar + from mipac.types.channel import IChannelLite from mipac.utils.format import str_to_datetime diff --git a/mipac/models/lite/note.py b/mipac/models/lite/note.py index 29ccec1..cbdba73 100644 --- a/mipac/models/lite/note.py +++ b/mipac/models/lite/note.py @@ -1,10 +1,10 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime from typing import TYPE_CHECKING, Generic, Literal, TypeVar + from mipac.models.lite.user import LiteUser from mipac.types.drive import IDriveFile - from mipac.types.note import IPartialNote from mipac.utils.format import str_to_datetime diff --git a/mipac/models/note.py b/mipac/models/note.py index fb48276..3ea0684 100644 --- a/mipac/models/note.py +++ b/mipac/models/note.py @@ -7,9 +7,15 @@ from mipac.errors.base import NotExistRequiredData from mipac.models.lite.note import PartialNote from mipac.models.lite.user import LiteUser from mipac.models.poll import Poll -from mipac.types.note import INoteState, INoteTranslateResult, INoteUpdated, INoteUpdatedDelete +from mipac.types.note import ( + INote, + INoteReaction, + INoteState, + INoteTranslateResult, + INoteUpdated, + INoteUpdatedDelete, +) from mipac.utils.format import str_to_datetime -from mipac.types.note import INote, INoteReaction if TYPE_CHECKING: from mipac.manager.client import ClientManager diff --git a/mipac/utils/util.py b/mipac/utils/util.py index dac38c4..fc78354 100644 --- a/mipac/utils/util.py +++ b/mipac/utils/util.py @@ -1,7 +1,7 @@ import functools import json import warnings -from datetime import timedelta, datetime +from datetime import datetime, timedelta from typing import Any try: From baadb1303dbb2bb61037430829666dc18fb2c2e2 Mon Sep 17 00:00:00 2001 From: yupix Date: Wed, 26 Jul 2023 00:41:32 +0000 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20channel=E3=81=AE=E3=82=B5=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=88=20=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?= =?UTF-8?q?=E3=83=AB=E3=82=92=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88=E3=81=99?= =?UTF-8?q?=E3=82=8B=20Resolve=20#64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 12 ++ mipac/actions/channel.py | 432 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 437 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07bc845..4fd932b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,18 @@ async for file in api.drive.file.action.get_files(get_all=True): - `/api/clips/notes` - `/api/clips/my-favorites` - `/api/users/clips` +- `/api/channels/create` +- `/api/channels/featured` +- `/api/channels/follow` +- `/api/channels/followed` +- `/api/channels/owned` +- `/api/channels/show` +- `/api/channels/unfollow` +- `/api/channels/update` +- `/api/channels/favorite` +- `/api/channels/unfavorite` +- `/api/channels/my-favorites` +- `/api/channels/search` ### Breaking changes 💔 diff --git a/mipac/actions/channel.py b/mipac/actions/channel.py index cc29a53..0e1f466 100644 --- a/mipac/actions/channel.py +++ b/mipac/actions/channel.py @@ -1,11 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, AsyncGenerator, Literal from mipac.abstract.action import AbstractAction +from mipac.errors.base import NotSupportVersion, NotSupportVersionText, ParameterError from mipac.http import HTTPClient, Route from mipac.models.channel import Channel +from mipac.models.lite.channel import ChannelLite from mipac.types.channel import IChannel +from mipac.utils.pagination import Pagination if TYPE_CHECKING: from mipac.client import ClientManager @@ -20,8 +23,22 @@ class ClientChannelActions(AbstractAction): self._client: ClientManager = client async def favorite(self, channel_id: str | None = None): + """ + Favorite a channel. + + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + bool + True if success else False + """ + if self._client._config.use_version < 13: - raise Exception() + raise NotSupportVersion(NotSupportVersionText) channel_id = self._channel_id or channel_id if channel_id is None: @@ -34,9 +51,21 @@ class ClientChannelActions(AbstractAction): return res async def unfavorite(self, channel_id: str | None = None): + """ + Unfavorite a channel. + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + bool + True if success else False + """ if self._client._config.use_version < 13: - raise Exception() + raise NotSupportVersion(NotSupportVersionText) channel_id = self._channel_id or channel_id if channel_id is None: @@ -48,6 +77,168 @@ class ClientChannelActions(AbstractAction): return res + async def follow(self, channel_id: str | None = None) -> bool: + """ + Follow a channel. + + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + bool + True if success else False + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + channel_id = self._channel_id or channel_id + if channel_id is None: + raise ParameterError('required channel_id') + + res: bool = await self._session.request( + Route('POST', '/api/channels/follow'), auth=True, json={'channelId': channel_id} + ) + + return res + + async def unfollow(self, channel_id: str | None = None) -> bool: + """ + Unfollow a channel. + + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + bool + True if success else False + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + channel_id = self._channel_id or channel_id + if channel_id is None: + raise ParameterError('required channel_id') + + res: bool = await self._session.request( + Route('POST', '/api/channels/unfollow'), auth=True, json={'channelId': channel_id} + ) + + return res + + async def update( + self, + name: str | None = None, + description: str | None = None, + banner_id: str | None = None, + is_archived: bool | None = None, + pinned_note_ids: list[str] | None = None, + color: str | None = None, + *, + channel_id: str | None = None, + ) -> Channel: + """ + Update a channel. + + Parameters + ---------- + name : str, optional, by default None + Channel name + description : str, optional, by default None + Channel description + banner_id : str, optional, by default None + Channel banner id + is_archived : bool, optional, by default None + Channel is archived + pinned_note_ids : list[str], optional, by default None + Channel pinned note ids + color : str, optional, by default None + Channel color + channel_id : str, optional, by default None + Channel id + + Returns + ------- + Channel + Channel + """ + + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + channel_id = self._channel_id or channel_id + if channel_id is None: + raise ParameterError('required channel_id') + + body = { + 'channelId': channel_id, + 'name': name, + 'description': description, + 'bannerId': banner_id, + 'isArchived': is_archived, + 'pinnedNoteIds': pinned_note_ids, + 'color': color, + } + res: IChannel = await self._session.request( + Route('POST', '/api/channels/update'), auth=True, json=body + ) + + return Channel(res, client=self._client) + + async def archive(self, channel_id: str | None = None) -> Channel: + """ + Archive a channel. + + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + Channel + Channel + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + channel_id = self._channel_id or channel_id + if channel_id is None: + raise ParameterError('required channel_id') + + res = await self.update(channel_id=channel_id, is_archived=True) + + return res + + async def unarchive(self, channel_id: str | None = None) -> Channel: + """ + Unarchive a channel. + + Parameters + ---------- + channel_id : str, optional, by default None + Channel id + + Returns + ------- + Channel + Channel + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + channel_id = self._channel_id or channel_id + if channel_id is None: + raise ParameterError('required channel_id') + + res = await self.update(channel_id=channel_id, is_archived=False) + + return res class ChannelActions(ClientChannelActions): def __init__( @@ -55,18 +246,245 @@ class ChannelActions(ClientChannelActions): ): super().__init__(channel_id=channel_id, session=session, client=client) - async def get_my_favorite(self) -> list[Channel]: - """お気に入りに登録したチャンネルの一覧を取得します。 + async def create( + self, + name: str, + description: str | None = None, + banner_id: str | None = None, + color: str = '#000', + ) -> ChannelLite: + """ + Create a channel. + + Parameters + ---------- + name : str + Channel name. + description : str, optional, by default None + Channel description + banner_id : str, optional, by default None + Channel banner id + color : str, optional, by default '#000' + Channel color Returns ------- Channel - チャンネル + ChannelLite """ if self._client._config.use_version < 13: - raise Exception() + raise NotSupportVersion(NotSupportVersionText) + + body = {'name': name, 'description': description, 'bannerId': banner_id, 'color': color} + res: IChannel = await self._session.request( + Route('POST', '/api/channels/create'), auth=True, json=body + ) + + return ChannelLite(res, client=self._client) + + async def get_featured(self) -> list[Channel]: + """ + Get featured channels. + + Returns + ------- + list[Channel] + Channel + """ + + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + res: list[IChannel] = await self._session.request( + Route('POST', '/api/channels/featured'), auth=True + ) + return [Channel(i, client=self._client) for i in res] + + async def get_followed( + self, + since_id: str | None = None, + until_id: str | None = None, + limit: int = 10, + *, + get_all: bool = False, + ) -> AsyncGenerator[Channel, None]: + """ + Get followed channels. + + Parameters + ---------- + since_id : str, optional, by default None + Since id + until_id : str, optional, by default None + Until id + limit : int, optional, by default 10 + Limit + get_all : bool, optional, by default False + Get all channels flag + + Yields + ------- + AsyncGenerator[Channel, None] + Channel + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + if limit > 100: + raise ParameterError('limit must be less than 100') + + if get_all: + limit = 100 + + body = {'sinceId': since_id, 'untilId': until_id, 'limit': limit} + + pagination = Pagination[IChannel]( + self._session, Route('POST', '/api/channels/followed'), auth=True, json=body + ) + + while True: + raw_channels = await pagination.next() + for raw_channel in raw_channels: + yield Channel(raw_channel, client=self._client) + + if get_all is False or pagination.is_final: + break + + async def get_owned( + self, since_id: str | None = None, until_id: str | None = None, *, get_all: bool = False + ) -> AsyncGenerator[Channel, None]: + """ + Get owned channels. + + Parameters + ---------- + since_id : str, optional, by default None + Since id + until_id : str, optional, by default None + Until id + get_all : bool, optional, by default False + Get all channels flag + + Yields + ------ + AsyncGenerator[Channel, None] + Channel + """ + + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + body = {'sinceId': since_id, 'untilId': until_id} + + pagination = Pagination[IChannel]( + self._session, Route('POST', '/api/channels/owned'), auth=True, json=body + ) + + while True: + raw_channels = await pagination.next() + for raw_channel in raw_channels: + yield Channel(raw_channel, client=self._client) + + if get_all is False or pagination.is_final: + break + + async def get(self, channel_id: str) -> Channel: + """ + Get a channel. + + Parameters + ---------- + channel_id : str + Channel id + + Returns + ------- + Channel + Channel + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + res: IChannel = await self._session.request( + Route('POST', '/api/channels/show'), auth=True, json={'channelId': channel_id} + ) + return Channel(res, client=self._client) + + async def get_my_favorite(self) -> list[Channel]: + """ + Get my favorite channels. + + Returns + ------- + list[Channel] + Channel + """ + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) res: list[IChannel] = await self._session.request( Route('POST', '/api/channels/my-favorites'), auth=True ) return [Channel(i, client=self._client) for i in res] + + async def search( + self, + query: str, + type: Literal['nameAndDescription', 'nameOnly'] = 'nameAndDescription', + since_id: str | None = None, + until_id: str | None = None, + limit: int = 5, + get_all: bool = False, + ) -> AsyncGenerator[Channel, None]: + """ + Search channels. + + Parameters + ---------- + query : str + Search query + type : Literal['nameAndDescription', 'nameOnly'], optional, by default 'nameAndDescription' + Search type + since_id : str, optional, by default None + Since id + until_id : str, optional, by default None + Until id + limit : int, optional, by default 5 + Limit + get_all : bool, optional, by default False + Get all channels flag + + Yields + ------ + AsyncGenerator[Channel, None] + Channel + """ + + if self._client._config.use_version < 13: + raise NotSupportVersion(NotSupportVersionText) + + if limit > 100: + raise ParameterError('limit must be less than 100') + + if get_all: + limit = 100 + + body = { + 'query': query, + 'type': type, + 'sinceId': since_id, + 'untilId': until_id, + 'limit': limit, + } + + pagination = Pagination[IChannel]( + self._session, Route('POST', '/api/channels/search'), auth=True, json=body + ) + + while True: + raw_channels = await pagination.next() + for raw_channel in raw_channels: + yield Channel(raw_channel, client=self._client) + + if get_all is False or pagination.is_final: + break From e8e3e223e607a6d2c7ee957ee42e8ec4aedd7dd4 Mon Sep 17 00:00:00 2001 From: yupix Date: Wed, 26 Jul 2023 00:42:59 +0000 Subject: [PATCH 4/5] chore: format --- mipac/actions/channel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mipac/actions/channel.py b/mipac/actions/channel.py index 0e1f466..943a838 100644 --- a/mipac/actions/channel.py +++ b/mipac/actions/channel.py @@ -161,7 +161,7 @@ class ClientChannelActions(AbstractAction): Channel color channel_id : str, optional, by default None Channel id - + Returns ------- Channel @@ -214,7 +214,7 @@ class ClientChannelActions(AbstractAction): res = await self.update(channel_id=channel_id, is_archived=True) return res - + async def unarchive(self, channel_id: str | None = None) -> Channel: """ Unarchive a channel. @@ -240,6 +240,7 @@ class ClientChannelActions(AbstractAction): return res + class ChannelActions(ClientChannelActions): def __init__( self, channel_id: str | None = None, *, session: HTTPClient, client: ClientManager From a2837e2fc693876595ddeda2c0eb90d136607a1f Mon Sep 17 00:00:00 2001 From: yupix Date: Wed, 26 Jul 2023 00:44:50 +0000 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20ci=E3=81=AEjob=E5=90=8D=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 4bc5b8a..c9b9508 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -13,7 +13,7 @@ permissions: contents: read jobs: - build: + type-check: runs-on: ubuntu-latest