Source code for osmium_chat.member
"""Community member: a user viewed through the lens of a specific community."""
from typing import TYPE_CHECKING
from osmium_protos import (
PB_CommunityMember,
PB_CommunityMemberRoleIds,
PB_EditMember,
PB_User,
)
from osmium_chat.user.user import User
if TYPE_CHECKING:
from osmium_chat.client import Client
from osmium_chat.community import Community
from osmium_chat.role import Role
__all__: tuple[str, ...] = (
"Member",
)
[docs]
class Member(User):
"""A user as a member of a specific community.
Extends :class:`~osmium_chat.user.user.User` with community-specific data:
a :attr:`nickname`, a list of :attr:`role_ids`, and helpers to manage roles.
.. note::
When a :class:`~osmium_chat.member.Member` is built from an incoming
message event the gateway does not include full member metadata, so
:attr:`nickname` starts as ``None`` and :attr:`role_ids` starts empty.
Call :meth:`~osmium_chat.community.Community.fetch_members` (once
available) if you need the current roles.
"""
__slots__: tuple[str, ...] = (
"community",
"nickname",
"role_ids",
"_client",
)
def __init__(
self,
member: PB_CommunityMember,
user: PB_User,
client: "Client",
*,
community: "Community",
) -> None:
"""Build a member from protobuf payloads.
:param member: The raw ``PB_CommunityMember`` for community-specific data.
:param user: The raw ``PB_User`` for the underlying user data.
:param client: The client used to perform role edits.
:param community: The community this member belongs to.
"""
super().__init__(user, client)
self.community: "Community" = community
self.nickname: str | None = member.nickname or None
self.role_ids: list[int] = list(member.role_ids)
self._client = client
@property
def display_name(self) -> str:
"""Display name: the community :attr:`nickname` if set, otherwise the user's name."""
return self.nickname or self.name
@property
def roles(self) -> "list[Role]":
"""The member's roles, resolved from the community's cached role list.
Call :meth:`~osmium_chat.community.Community.fetch_roles` first if you
need up-to-date role data.
"""
role_id_set = set(self.role_ids)
return [r for r in self.community.roles if r.id in role_id_set]
[docs]
async def add_role(self, role: "Role") -> None:
"""Add a role to this member.
No-ops if the member already has the role. Updates :attr:`role_ids` on
success.
:param role: The role to add.
:raises RequestError: If the gateway rejects the request.
"""
if role.id in self.role_ids:
return
new_ids = self.role_ids + [role.id]
await self._client.request(PB_EditMember(
community_id=self.community.id,
member_id=self.id,
role_ids=PB_CommunityMemberRoleIds(role_ids=new_ids),
))
self.role_ids = new_ids
[docs]
async def remove_role(self, role: "Role") -> None:
"""Remove a role from this member.
No-ops if the member does not have the role. Updates :attr:`role_ids` on
success.
:param role: The role to remove.
:raises RequestError: If the gateway rejects the request.
"""
if role.id not in self.role_ids:
return
new_ids = [r for r in self.role_ids if r != role.id]
await self._client.request(PB_EditMember(
community_id=self.community.id,
member_id=self.id,
role_ids=PB_CommunityMemberRoleIds(role_ids=new_ids),
))
self.role_ids = new_ids