Working with roles¶
Roles are self-managed groups whose membership can change as users join or leave them.
Roles can be added to group chats as members. When a user joins a role, they immediately gain access to all chats the role participates in.
This guide shows how to inspect roles in the directory, use them as chat members, read the sender roles stored on messages, and react to role events.
Checking whether roles are enabled¶
Role features are controlled by the organization. Check roles_enabled before
offering role-specific operations:
from teamwire_api import Connection
async def print_role_status(connection):
permissions = await connection.get_permissions()
print(f'Roles enabled: {permissions.roles_enabled}')
with Connection() as connection:
connection.exec_async(print_role_status(connection))
When roles_enabled is False, applications should not try to list roles or
use them as chat members.
Listing visible roles¶
The role directory is available through connection.roles. The get_list()
coroutine returns the current non-deleted roles visible to the bot:
from teamwire_api import Connection
async def list_roles(connection):
permissions = await connection.get_permissions()
if not permissions.roles_enabled:
print('Roles are not enabled for this organization')
return
roles = await connection.roles.get_list()
for role in roles:
organization = role.organisation.name if role.organisation else 'unknown'
print(
f'{role.name or "unnamed"}: '
f'id={role.id}, visibility={role.visibility}, '
f'organization={organization}'
)
with Connection() as connection:
connection.exec_async(list_roles(connection))
Role IDs should be used when storing references or accepting role input. Role names are intended for display and are not stable identifiers.
To retrieve one role by ID, use connection.roles.get_role(role_id). The SDK
raises TWNotFoundError if the role is not visible or does not exist.
Listing the users in a role¶
Role.get_members() resolves the role's current member IDs to SDK User
objects. It is a coroutine because resolving users may require directory
access:
async def print_role_members(role):
users = await role.get_members()
print(f'Members of {role.name or role.id}:')
for user in users:
print(f'- {user.full_name()} ({user.uid})')
Users who are deleted or not visible to the bot are omitted from the returned list.
Inspecting roles in a chat¶
A Chat exposes its direct role members through get_role_members():
async def print_chat_roles(connection, thread_id):
chat = await connection.chats.get_by_thread_id(thread_id)
if chat is None:
print('Chat not found')
return
for role in chat.get_role_members():
print(f'- {role.name or "unnamed"} ({role.id})')
This returns Role objects, not the individual users currently represented by
those roles. Use await role.get_members() when the expanded user list is
needed.
Creating a chat with roles¶
Roles can be passed alongside direct users and public circles when creating a group chat:
from teamwire_api import Connection
async def create_role_chat(connection):
permissions = await connection.get_permissions()
if not permissions.roles_enabled:
print('Roles are not enabled for this organization')
return
role = await connection.roles.get_role('ROLE_ID')
chat = await connection.chats.create_chat(
title='Operations',
roles=[role],
message='Welcome to the operations chat'
)
print(f'Created chat {chat.tid}')
with Connection() as connection:
connection.exec_async(create_role_chat(connection))
Roles can only be used with group chats. They cannot be recipients of a 1:1 chat.
Adding and removing role members from a chat¶
Chat administrators can update role membership with the same methods used for users and circles:
async def update_chat_role(connection, thread_id, role_id):
permissions = await connection.get_permissions()
if not permissions.roles_enabled:
print('Roles are not enabled for this organization')
return
chat = await connection.chats.get_by_thread_id(thread_id)
role = await connection.roles.get_role(role_id)
await chat.add_members(roles=[role])
print(f'Added {role.name or role.id}')
await chat.remove_members(roles=[role])
print(f'Removed {role.name or role.id}')
These operations require permission to edit chat members and are not supported for 1:1 chats.
Reading a message sender's roles¶
Messages can contain the names of the roles held by the sender when the message
was sent. They are exposed through Message.sender_roles:
from teamwire_api import Connection, Events
def message_handler(event_type, message):
print(f'From: {message.from_user.full_name()}')
if message.sender_roles:
print(f'Sender roles: {", ".join(message.sender_roles)}')
with Connection() as connection:
connection.set_handler([Events.NEW_MESSAGE], message_handler)
connection.start_notifications()
sender_roles is a list of role names, not Role objects. It is message
metadata frozen at send time, so it may differ from the sender's current role
membership and does not require a role-directory lookup.
Handling role-related events¶
Role changes affecting a chat are available through the notifications API. A
ChatMemberChangeInfo event payload can include affected roles, users, the
chat, and the user who performed the change in editor:
from teamwire_api import Connection, Events
ROLE_EVENTS = [
Events.CHAT_MEMBERS_ADDED,
Events.CHAT_MEMBERS_REMOVED,
Events.ROLES_JOINED,
Events.ROLES_LEFT,
Events.ROLE_USERS_REMOVED,
Events.ROLES_DELETED,
]
def role_event_handler(event_type, info):
role_names = [role.name or role.id for role in info.roles]
user_names = [user.full_name() for user in info.users]
print(f'{event_type.value} in chat {info.chat.tid}')
print(f'Roles: {", ".join(role_names) or "none"}')
print(f'Users: {", ".join(user_names) or "none"}')
with Connection() as connection:
connection.set_handler(ROLE_EVENTS, role_event_handler)
connection.start_notifications()
CHAT_MEMBERS_ADDED and CHAT_MEMBERS_REMOVED report roles added to or removed
from a chat. The other events describe users joining or leaving chat roles,
users being removed from a role, and roles being deleted.