2023-06-13 03:26:14 -06:00
<!DOCTYPE HTML>
< html lang = "en" class = "sidebar-visible no-js light" >
< head >
<!-- Book generated using mdBook -->
< meta charset = "UTF-8" >
< title > Presence router callbacks - Synapse< / title >
<!-- Custom HTML head -->
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
< meta name = "description" content = "" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< meta name = "theme-color" content = "#ffffff" / >
< link rel = "icon" href = "../favicon.svg" >
< link rel = "shortcut icon" href = "../favicon.png" >
< link rel = "stylesheet" href = "../css/variables.css" >
< link rel = "stylesheet" href = "../css/general.css" >
< link rel = "stylesheet" href = "../css/chrome.css" >
< link rel = "stylesheet" href = "../css/print.css" media = "print" >
<!-- Fonts -->
< link rel = "stylesheet" href = "../FontAwesome/css/font-awesome.css" >
< link rel = "stylesheet" href = "../fonts/fonts.css" >
<!-- Highlight.js Stylesheets -->
< link rel = "stylesheet" href = "../highlight.css" >
< link rel = "stylesheet" href = "../tomorrow-night.css" >
< link rel = "stylesheet" href = "../ayu-highlight.css" >
<!-- Custom theme stylesheets -->
< link rel = "stylesheet" href = "../docs/website_files/table-of-contents.css" >
< link rel = "stylesheet" href = "../docs/website_files/remove-nav-buttons.css" >
< link rel = "stylesheet" href = "../docs/website_files/indent-section-headers.css" >
2023-12-11 07:54:04 -07:00
< link rel = "stylesheet" href = "../docs/website_files/version-picker.css" >
2023-06-13 03:26:14 -06:00
< / head >
< body >
<!-- Provide site root to javascript -->
< script type = "text/javascript" >
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
< / script >
<!-- Work around some values being stored in localStorage wrapped in quotes -->
< script type = "text/javascript" >
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') & & theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') & & sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
< / script >
<!-- Set the theme before any content is loaded, prevents flash -->
< script type = "text/javascript" >
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
< / script >
<!-- Hide / unhide sidebar before it is displayed -->
< script type = "text/javascript" >
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
< / script >
< nav id = "sidebar" class = "sidebar" aria-label = "Table of contents" >
< div class = "sidebar-scrollbox" >
< ol class = "chapter" > < li class = "chapter-item expanded affix " > < li class = "part-title" > Introduction< / li > < li class = "chapter-item expanded " > < a href = "../welcome_and_overview.html" > Welcome and Overview< / a > < / li > < li class = "chapter-item expanded affix " > < li class = "part-title" > Setup< / li > < li class = "chapter-item expanded " > < a href = "../setup/installation.html" > Installation< / a > < / li > < li class = "chapter-item expanded " > < a href = "../postgres.html" > Using Postgres< / a > < / li > < li class = "chapter-item expanded " > < a href = "../reverse_proxy.html" > Configuring a Reverse Proxy< / a > < / li > < li class = "chapter-item expanded " > < a href = "../setup/forward_proxy.html" > Configuring a Forward/Outbound Proxy< / a > < / li > < li class = "chapter-item expanded " > < a href = "../turn-howto.html" > Configuring a Turn Server< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../setup/turn/coturn.html" > coturn TURN server< / a > < / li > < li class = "chapter-item expanded " > < a href = "../setup/turn/eturnal.html" > eturnal TURN server< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../delegate.html" > Delegation< / a > < / li > < li class = "chapter-item expanded affix " > < li class = "part-title" > Upgrading< / li > < li class = "chapter-item expanded " > < a href = "../upgrade.html" > Upgrading between Synapse Versions< / a > < / li > < li class = "chapter-item expanded affix " > < li class = "part-title" > Usage< / li > < li class = "chapter-item expanded " > < a href = "../federate.html" > Federation< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/index.html" > Configuration< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../usage/configuration/config_documentation.html" > Configuration Manual< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/homeserver_sample_config.html" > Homeserver Sample Config File< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/logging_sample_config.html" > Logging Sample Config File< / a > < / li > < li class = "chapter-item expanded " > < a href = "../structured_logging.html" > Structured Logging< / a > < / li > < li class = "chapter-item expanded " > < a href = "../templates.html" > Templates< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/user_authentication/index.html" > User Authentication< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../usage/configuration/user_authentication/single_sign_on/index.html" > Single-Sign On< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../openid.html" > OpenID Connect< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/user_authentication/single_sign_on/saml.html" > SAML< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/user_authentication/single_sign_on/cas.html" > CAS< / a > < / li > < li class = "chapter-item expanded " > < a href = "../sso_mapping_providers.html" > SSO Mapping Providers< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../password_auth_providers.html" > Password Auth Providers< / a > < / li > < li class = "chapter-item expanded " > < a href = "../jwt.html" > JSON Web Tokens< / a > < / li > < li class = "chapter-item expanded " > < a href = "../usage/configuration/user_authentication/refresh_tokens.html" > Refresh Tokens< / a > < / li > < / ol > < / li > < li class = "chapter-item expanded " > < a href = "../CAPTCHA_SETUP.html" > Registration Captcha< / a > < / li > < li class = "chapter-item expanded " > < a href = "../application_services.html" > Application Services< / a > < / li > < li class = "chapter-item expanded " > < a href = "../server_notices.html" > Server Notices< / a > < / li > < li class = "chapter-item expanded " > < a href = "../consent_tracking.html" > Consent Tracking< / a > < / li > < li class = "chapter-item expanded " > < a href = "../user_directory.html" > User Directory< / a > < / li > < li class = "chapter-item expanded " > < a href = "../message_retention_policies.html" > Message Retention Policies< / a > < / li > < li class = "chapter-item expanded " > < a href = "../modules/index.html" > Pluggable Modules< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded " > < a href = "../modules/writing_a_module.html" > Writing a module< / a > < / li > < li > < ol class = "section" > < li class = "chapter-item expanded "
< / div >
< div id = "sidebar-resize-handle" class = "sidebar-resize-handle" > < / div >
< / nav >
< div id = "page-wrapper" class = "page-wrapper" >
< div class = "page" >
< div id = "menu-bar-hover-placeholder" > < / div >
< div id = "menu-bar" class = "menu-bar sticky bordered" >
< div class = "left-buttons" >
< button id = "sidebar-toggle" class = "icon-button" type = "button" title = "Toggle Table of Contents" aria-label = "Toggle Table of Contents" aria-controls = "sidebar" >
< i class = "fa fa-bars" > < / i >
< / button >
< button id = "theme-toggle" class = "icon-button" type = "button" title = "Change theme" aria-label = "Change theme" aria-haspopup = "true" aria-expanded = "false" aria-controls = "theme-list" >
< i class = "fa fa-paint-brush" > < / i >
< / button >
< ul id = "theme-list" class = "theme-popup" aria-label = "Themes" role = "menu" >
< li role = "none" > < button role = "menuitem" class = "theme" id = "light" > Light (default)< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "rust" > Rust< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "coal" > Coal< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "navy" > Navy< / button > < / li >
< li role = "none" > < button role = "menuitem" class = "theme" id = "ayu" > Ayu< / button > < / li >
< / ul >
< button id = "search-toggle" class = "icon-button" type = "button" title = "Search. (Shortkey: s)" aria-label = "Toggle Searchbar" aria-expanded = "false" aria-keyshortcuts = "S" aria-controls = "searchbar" >
< i class = "fa fa-search" > < / i >
< / button >
2023-12-11 07:54:04 -07:00
< div class = "version-picker" >
< div class = "dropdown" >
< div class = "select" >
< span > < / span >
< i class = "fa fa-chevron-down" > < / i >
< / div >
< input type = "hidden" name = "version" >
< ul class = "dropdown-menu" >
<!-- Versions will be added dynamically in version - picker.js -->
< / ul >
< / div >
< / div >
2023-06-13 03:26:14 -06:00
< / div >
< h1 class = "menu-title" > Synapse< / h1 >
< div class = "right-buttons" >
< a href = "../print.html" title = "Print this book" aria-label = "Print this book" >
< i id = "print-button" class = "fa fa-print" > < / i >
< / a >
< a href = "https://github.com/matrix-org/synapse" title = "Git repository" aria-label = "Git repository" >
< i id = "git-repository-button" class = "fa fa-github" > < / i >
< / a >
< a href = "https://github.com/matrix-org/synapse/edit/develop/docs/modules/presence_router_callbacks.md" title = "Suggest an edit" aria-label = "Suggest an edit" >
< i id = "git-edit-button" class = "fa fa-edit" > < / i >
< / a >
< / div >
< / div >
< div id = "search-wrapper" class = "hidden" >
< form id = "searchbar-outer" class = "searchbar-outer" >
< input type = "search" id = "searchbar" name = "searchbar" placeholder = "Search this book ..." aria-controls = "searchresults-outer" aria-describedby = "searchresults-header" >
< / form >
< div id = "searchresults-outer" class = "searchresults-outer hidden" >
< div id = "searchresults-header" class = "searchresults-header" > < / div >
< ul id = "searchresults" >
< / ul >
< / div >
< / div >
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
< script type = "text/javascript" >
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
< / script >
< div id = "content" class = "content" >
< main >
<!-- Page table of contents -->
< div class = "sidetoc" >
< nav class = "pagetoc" > < / nav >
< / div >
< h1 id = "presence-router-callbacks" > < a class = "header" href = "#presence-router-callbacks" > Presence router callbacks< / a > < / h1 >
< p > Presence router callbacks allow module developers to specify additional users (local or remote)
to receive certain presence updates from local users. Presence router callbacks can be
registered using the module API's < code > register_presence_router_callbacks< / code > method.< / p >
< h2 id = "callbacks" > < a class = "header" href = "#callbacks" > Callbacks< / a > < / h2 >
< p > The available presence router callbacks are:< / p >
< h3 id = "get_users_for_states" > < a class = "header" href = "#get_users_for_states" > < code > get_users_for_states< / code > < / a > < / h3 >
< p > < em > First introduced in Synapse v1.42.0< / em > < / p >
< pre > < code class = "language-python" > async def get_users_for_states(
state_updates: Iterable[" synapse.api.UserPresenceState" ],
) -> Dict[str, Set[" synapse.api.UserPresenceState" ]]
< / code > < / pre >
< p > < strong > Requires< / strong > < code > get_interested_users< / code > to also be registered< / p >
< p > Called when processing updates to the presence state of one or more users. This callback can
be used to instruct the server to forward that presence state to specific users. The module
must return a dictionary that maps from Matrix user IDs (which can be local or remote) to the
< code > UserPresenceState< / code > changes that they should be forwarded.< / p >
< p > Synapse will then attempt to send the specified presence updates to each user when possible.< / p >
< p > If multiple modules implement this callback, Synapse merges all the dictionaries returned
by the callbacks. If multiple callbacks return a dictionary containing the same key,
Synapse concatenates the sets associated with this key from each dictionary. < / p >
< h3 id = "get_interested_users" > < a class = "header" href = "#get_interested_users" > < code > get_interested_users< / code > < / a > < / h3 >
< p > < em > First introduced in Synapse v1.42.0< / em > < / p >
< pre > < code class = "language-python" > async def get_interested_users(
user_id: str
) -> Union[Set[str], " synapse.module_api.PRESENCE_ALL_USERS" ]
< / code > < / pre >
< p > < strong > Requires< / strong > < code > get_users_for_states< / code > to also be registered< / p >
< p > Called when determining which users someone should be able to see the presence state of. This
callback should return complementary results to < code > get_users_for_state< / code > or the presence information
may not be properly forwarded.< / p >
< p > The callback is given the Matrix user ID for a local user that is requesting presence data and
should return the Matrix user IDs of the users whose presence state they are allowed to
query. The returned users can be local or remote. < / p >
< p > Alternatively the callback can return < code > synapse.module_api.PRESENCE_ALL_USERS< / code >
to indicate that the user should receive updates from all known users.< / p >
< p > If multiple modules implement this callback, they will be considered in order. Synapse
calls each callback one by one, and use a concatenation of all the < code > set< / code > s returned by the
callbacks. If one callback returns < code > synapse.module_api.PRESENCE_ALL_USERS< / code > , Synapse uses
this value instead. If this happens, Synapse does not call any of the subsequent
implementations of this callback.< / p >
< h2 id = "example" > < a class = "header" href = "#example" > Example< / a > < / h2 >
< p > The example below is a module that implements both presence router callbacks, and ensures
that < code > @alice:example.org< / code > receives all presence updates from < code > @bob:example.com< / code > and
< code > @charlie:somewhere.org< / code > , regardless of whether Alice shares a room with any of them.< / p >
< pre > < code class = "language-python" > from typing import Dict, Iterable, Set, Union
from synapse.module_api import ModuleApi
class CustomPresenceRouter:
def __init__(self, config: dict, api: ModuleApi):
self.api = api
self.api.register_presence_router_callbacks(
get_users_for_states=self.get_users_for_states,
get_interested_users=self.get_interested_users,
)
async def get_users_for_states(
self,
state_updates: Iterable[" synapse.api.UserPresenceState" ],
) -> Dict[str, Set[" synapse.api.UserPresenceState" ]]:
res = {}
for update in state_updates:
if (
update.user_id == " @bob:example.com"
or update.user_id == " @charlie:somewhere.org"
):
res.setdefault(" @alice:example.com" , set()).add(update)
return res
async def get_interested_users(
self,
user_id: str,
) -> Union[Set[str], " synapse.module_api.PRESENCE_ALL_USERS" ]:
if user_id == " @alice:example.com" :
return {" @bob:example.com" , " @charlie:somewhere.org" }
return set()
< / code > < / pre >
< / main >
< nav class = "nav-wrapper" aria-label = "Page navigation" >
<!-- Mobile navigation buttons -->
< a rel = "prev" href = "../modules/third_party_rules_callbacks.html" class = "mobile-nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< a rel = "next" href = "../modules/account_validity_callbacks.html" class = "mobile-nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< div style = "clear: both" > < / div >
< / nav >
< / div >
< / div >
< nav class = "nav-wide-wrapper" aria-label = "Page navigation" >
< a rel = "prev" href = "../modules/third_party_rules_callbacks.html" class = "nav-chapters previous" title = "Previous chapter" aria-label = "Previous chapter" aria-keyshortcuts = "Left" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< a rel = "next" href = "../modules/account_validity_callbacks.html" class = "nav-chapters next" title = "Next chapter" aria-label = "Next chapter" aria-keyshortcuts = "Right" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< / nav >
< / div >
< script type = "text/javascript" >
window.playground_copyable = true;
< / script >
< script src = "../elasticlunr.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../mark.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../searcher.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../clipboard.min.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../highlight.js" type = "text/javascript" charset = "utf-8" > < / script >
< script src = "../book.js" type = "text/javascript" charset = "utf-8" > < / script >
<!-- Custom JS scripts -->
< script type = "text/javascript" src = "../docs/website_files/table-of-contents.js" > < / script >
2023-12-11 07:54:04 -07:00
< script type = "text/javascript" src = "../docs/website_files/version-picker.js" > < / script >
< script type = "text/javascript" src = "../docs/website_files/version.js" > < / script >
2023-06-13 03:26:14 -06:00
< / body >
2023-12-11 07:54:04 -07:00
< / html >