Skip to content

Add group relationships #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 211 additions & 3 deletions roblox/bases/basegroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from datetime import datetime
from dateutil.parser import parse
from enum import Enum

from .baseitem import BaseItem
from ..members import Member, MemberRelationship
Expand All @@ -18,13 +19,13 @@
from ..shout import Shout
from ..sociallinks import SocialLink
from ..utilities.exceptions import InvalidRole
from ..utilities.iterators import PageIterator, SortOrder
from ..utilities.iterators import PageIterator, RowIterator, SortOrder
from ..wall import WallPost, WallPostRelationship

if TYPE_CHECKING:
from ..client import Client
from .baseuser import BaseUser
from ..utilities.types import UserOrUserId, RoleOrRoleId
from ..utilities.types import UserOrUserId, RoleOrRoleId, GroupOrGroupId


class JoinRequest:
Expand Down Expand Up @@ -118,6 +119,96 @@ def __repr__(self):
return f"<{self.__class__.__name__} name={self.name!r} created={self.created}>"


class GroupRelationshipType(Enum):
"""
Represents a type of relationship between two groups.
"""

allies = "allies"
enemies = "enemies"


class GroupRelationship:
"""
Represents a relationship between two groups.

Attributes:
relationship_type: The type of relationship between both groups.
group: The group.
related_group: The related group.
"""

def __init__(self, client: Client, data: dict, group: BaseGroup, relationship_type: GroupRelationshipType):
"""
Arguments:
client: The Client this object belongs to.
data: A GroupDetailResponse Object.
group: The group getting queried for it's relationships.
relationship_type: The type of relationship between both groups.
"""

from ..groups import Group

self._client: Client = client
self.relationship_type: GroupRelationshipType = relationship_type
self.group: BaseGroup = group
self.related_group: Group = Group(client=client, data=data)

async def remove(self):
"""
Severs the relationship between both groups.
"""

await self._client.requests.delete(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.group.id}/relationships/{self.relationship_type.value}/{self.related_group.id}")
)


class GroupRelationshipRequest:
"""
Represents a request to establish a relationship with another group.

Attributes:
relationship_type: The type of relationship to be established between both groups.
group: The group that received the request.
related_group: The group that sent the request.
"""

def __init__(self, client: Client, data: dict, group: BaseGroup, relationship_type: GroupRelationshipType):
"""
Arguments:
client: The Client this object belongs to.
data: A GroupDetailResponse Object.
group: The group that received the request.
relationship_type: The type of relationship to be established between both groups.
"""

from ..groups import Group

self._client: Client = client
self.relationship_type: GroupRelationshipType = relationship_type
self.group: BaseGroup = group
self.related_group: Group = Group(client=client, data=data)

async def accept(self):
"""
Accepts the relationship request.
"""

await self._client.requests.post(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.group.id}/relationships/{self.relationship_type.value}/requests/{self.related_group.id}")
)

async def decline(self):
"""
Declines the relationship request.
"""

await self._client.requests.delete(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.group.id}/relationships/{self.relationship_type.value}/requests/{self.related_group.id}")
)


class BaseGroup(BaseItem):
"""
Represents a Roblox group ID.
Expand Down Expand Up @@ -473,4 +564,121 @@ def get_name_history(
sort_order=sort_order,
max_items=max_items,
handler=lambda client, data: GroupNameHistoryItem(client=client, data=data),
)
)

def get_ally_relationship_requests(
self,
rows: int = 50,
sort_order: SortOrder = SortOrder.Ascending,
max_items: int = None
) -> RowIterator:
"""
Returns the group's pending ally requests from other groups.

Arguments:
rows: How many ally requests should be returned for each iteration.
sort_order: Order in which data should be grabbed.
max_items: The maximum items to return when looping through this object.

Returns:
A RowIterator containing the groups's pending relationship requests.
"""

return RowIterator(
client=self._client,
url=self._client._url_generator.get_url("groups", f"v1/groups/{self.id}/relationships/{GroupRelationshipType.allies.value}/requests"),
data_field_name="relatedGroups",
sort_order=sort_order,
rows=rows,
max_rows=max_items,
handler=lambda client, data, group, relationship_type: GroupRelationshipRequest(client, data, group, relationship_type),
handler_kwargs={"group": self, "relationship_type": GroupRelationshipType.allies}
)

def get_relationships(
self,
relationshipType: GroupRelationshipType = GroupRelationshipType.allies,
rows: int = 50,
sort_order: SortOrder = SortOrder.Ascending,
max_items: int = None,
) -> RowIterator:
"""
Returns established relationships with other groups.

Arguments:
relationshipType: The type of relationship established between both groups.
rows: How many relationships should be returned for each iteration.
sort_order: Order in which data should be grabbed.
max_items: The maximum items to return when looping through this object.

Returns:
A RowIterator containing the groups's relationships.
"""

return RowIterator(
client=self._client,
url=self._client._url_generator.get_url("groups", f"v1/groups/{self.id}/relationships/{relationshipType.value}"),
data_field_name="relatedGroups",
sort_order=sort_order,
rows=rows,
max_rows=max_items,
handler=lambda client, data, group, relationship_type: GroupRelationship(client, data, group, relationship_type),
handler_kwargs={"group": self, "relationship_type": relationshipType}
)

async def request_relationship(
self,
group: GroupOrGroupId,
relationshipType: GroupRelationshipType = GroupRelationshipType.allies
):
"""
Requests to establish a relationship with another group.

Arguments:
group: The group to prepose the relationship with.
relationshipType: The type of relationship to be established.
"""

await self._client.requests.post(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.id}/relationships/{relationshipType.value}/{int(group)}")
)

async def decline_relationship_requests(
self,
groups: List[GroupOrGroupId],
relationshipType: GroupRelationshipType = GroupRelationshipType.allies
):
"""
Declines all relationship requests from a list of groups.

Arguments:
groups: A list of groups to decline.
relationshipType: The type of relationship.
"""

await self._client.requests.delete(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.id}/relationships/{relationshipType.value}/requests"),
json={
"GroupIds": list(map(int, groups))
}
)

async def accept_relationship_requests(
self,
groups: List[GroupOrGroupId],
relationshipType: GroupRelationshipType = GroupRelationshipType.allies
):
"""
Accepts all relationship requests from a list of groups.

Arguments:
groups: A list of groups to accept.
relationshipType: The type of relationship.
"""

await self._client.requests.post(
url=self._client.url_generator.get_url("groups", f"v1/groups/{self.id}/relationships/{relationshipType.value}/requests"),
json={
"GroupIds": list(map(int, groups))
}
)
80 changes: 79 additions & 1 deletion roblox/utilities/iterators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""
from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, List

if TYPE_CHECKING:
from ..client import Client
Expand Down Expand Up @@ -323,3 +323,81 @@ async def next(self):
]

return data

class RowIterator(RobloxIterator):
"""
Represents an iterator that is meant to iterate over rows, like those seen on groups.roblox.com.

Attributes:
url: The endpoint to hit for new row data.
data_field_name: The name of the field containing the data.
sort_order: The sort order to use for returned data.
row_index: The starting row index.
rows: The maximum amount of rows to return on each iteration.
max_rows: The maximum amount of items to return when this iterator is looped through.
extra_parameters: Extra parameters to pass to the endpoint.
handler: A callable object to use to convert raw endpoint data to parsed objects.
handler_kwargs: Extra keyword arguments to pass to the handler.
"""

def __init__(
self,
client: Client,
url: str,
data_field_name: str,
sort_order: SortOrder = SortOrder.Ascending,
row_index: int = 0,
rows: int = 50,
max_rows: int = None,
extra_parameters: Optional[dict] = None,
handler: Optional[Callable] = None,
handler_kwargs: Optional[dict] = None
):
super().__init__()

self._client: Client = client

self.url: str = url
self.data_field_name: str = data_field_name
self.sort_order: SortOrder = sort_order
self.row_index: int = row_index
self.rows: int = rows
self.max_items: int = max_rows

self.extra_parameters: Dict = extra_parameters or {}
self.handler: Callable = handler
self.handler_kwargs: Dict = handler_kwargs or {}

async def next(self) -> List:
"""
Advances the iterator to the next set of rows.
"""

page_response = await self._client.requests.get(
url=self.url,
params={
"startRowIndex": self.row_index,
"maxRows": self.rows,
"sortOrder": self.sort_order.value,
**self.extra_parameters
}
)
body: dict = page_response.json()
next_row_index: int = body.get("nextRowIndex")
data = body.get(self.data_field_name)

if len(data) == 0:
raise NoMoreItems("No more items.")

self.row_index = next_row_index

if self.handler:
data = [
self.handler(
client=self._client,
data=item_data,
**self.handler_kwargs
) for item_data in data
]

return data