inspect_jitsi package#

Subpackages#

Submodules#

Module contents#

Tools for inspecting a running Jitsi Meet deployment.

class inspect_jitsi.DiagnosisResult(ws_domain, config_js_reachable=False, discovered_hosts=None, xmpp_domain_tried=None, domain_recognized=None, sasl_mechanisms=None, anonymous_login_ok=None, error=None)[source]

Bases: object

What's needed to read a Jitsi room's occupancy.

As found by JitsiConference.diagnose().

Example:

DiagnosisResult(
    ws_domain="meet.example.com",
    config_js_reachable=True,
    discovered_hosts={
        "domain": "meet.jitsi",
        "muc": "muc.meet.jitsi",
        "anonymousdomain": None,
    },
    xmpp_domain_tried="meet.jitsi",
    domain_recognized=True,
    sasl_mechanisms=["ANONYMOUS"],
    anonymous_login_ok=True,
    error=None,
)
__annotations__ = {'anonymous_login_ok': 'bool | None', 'config_js_reachable': 'bool', 'discovered_hosts': 'dict[str, str | None] | None', 'domain_recognized': 'bool | None', 'error': 'str | None', 'sasl_mechanisms': 'list[str] | None', 'ws_domain': 'str', 'xmpp_domain_tried': 'str | None'}
__dataclass_fields__ = {'anonymous_login_ok': Field(name='anonymous_login_ok',type='bool | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'config_js_reachable': Field(name='config_js_reachable',type='bool',default=False,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'discovered_hosts': Field(name='discovered_hosts',type='dict[str, str | None] | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'domain_recognized': Field(name='domain_recognized',type='bool | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'error': Field(name='error',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'sasl_mechanisms': Field(name='sasl_mechanisms',type='list[str] | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'ws_domain': Field(name='ws_domain',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'xmpp_domain_tried': Field(name='xmpp_domain_tried',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)}
__dataclass_params__ = _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False,match_args=True,kw_only=False,slots=False,weakref_slot=False)
__dict__ = mappingproxy({'__module__': 'inspect_jitsi.xmpp.diagnosis', '__firstlineno__': 22, '__annotations__': {'ws_domain': 'str', 'config_js_reachable': 'bool', 'discovered_hosts': 'dict[str, str | None] | None', 'xmpp_domain_tried': 'str | None', 'domain_recognized': 'bool | None', 'sasl_mechanisms': 'list[str] | None', 'anonymous_login_ok': 'bool | None', 'error': 'str | None'}, '__doc__': 'What\'s needed to read a Jitsi room\'s occupancy.\n\nAs found by `JitsiConference.diagnose()`.\n\nExample::\n\n    DiagnosisResult(\n        ws_domain="meet.example.com",\n        config_js_reachable=True,\n        discovered_hosts={\n            "domain": "meet.jitsi",\n            "muc": "muc.meet.jitsi",\n            "anonymousdomain": None,\n        },\n        xmpp_domain_tried="meet.jitsi",\n        domain_recognized=True,\n        sasl_mechanisms=["ANONYMOUS"],\n        anonymous_login_ok=True,\n        error=None,\n    )\n\n', 'config_js_reachable': False, 'discovered_hosts': None, 'xmpp_domain_tried': None, 'domain_recognized': None, 'sasl_mechanisms': None, 'anonymous_login_ok': None, 'error': None, 'to_dict': <function DiagnosisResult.to_dict>, '__static_attributes__': (), '__dict__': <attribute '__dict__' of 'DiagnosisResult' objects>, '__weakref__': <attribute '__weakref__' of 'DiagnosisResult' objects>, '__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False,match_args=True,kw_only=False,slots=False,weakref_slot=False), '__dataclass_fields__': {'ws_domain': Field(name='ws_domain',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'config_js_reachable': Field(name='config_js_reachable',type='bool',default=False,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'discovered_hosts': Field(name='discovered_hosts',type='dict[str, str | None] | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'xmpp_domain_tried': Field(name='xmpp_domain_tried',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'domain_recognized': Field(name='domain_recognized',type='bool | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'sasl_mechanisms': Field(name='sasl_mechanisms',type='list[str] | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'anonymous_login_ok': Field(name='anonymous_login_ok',type='bool | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'error': Field(name='error',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)}, '__replace__': <function _replace>, '__hash__': None, '__init__': <function DiagnosisResult.__init__>, '__repr__': <function DiagnosisResult.__repr__>, '__eq__': <function DiagnosisResult.__eq__>, '__match_args__': ('ws_domain', 'config_js_reachable', 'discovered_hosts', 'xmpp_domain_tried', 'domain_recognized', 'sasl_mechanisms', 'anonymous_login_ok', 'error')})
__eq__(other)

Return self==value.

__firstlineno__ = 22
__hash__ = None
__init__(ws_domain, config_js_reachable=False, discovered_hosts=None, xmpp_domain_tried=None, domain_recognized=None, sasl_mechanisms=None, anonymous_login_ok=None, error=None)
__match_args__ = ('ws_domain', 'config_js_reachable', 'discovered_hosts', 'xmpp_domain_tried', 'domain_recognized', 'sasl_mechanisms', 'anonymous_login_ok', 'error')
__module__ = 'inspect_jitsi.xmpp.diagnosis'
__replace__(**changes)
__repr__()

Return repr(self).

__static_attributes__ = ()
__weakref__

list of weak references to the object

anonymous_login_ok: bool | None = None
config_js_reachable: bool = False
discovered_hosts: dict[str, str | None] | None = None
domain_recognized: bool | None = None
error: str | None = None
sasl_mechanisms: list[str] | None = None
to_dict()[source]

Return a plain, JSON-serializable dict of this result.

Return type:

dict

ws_domain: str
xmpp_domain_tried: str | None = None
class inspect_jitsi.JitsiConference(conference_url, nick=None, *, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]

Bases: object

A Jitsi Meet conference (room), joined anonymously as an observer.

Use as an async context manager:

async with JitsiConference(conference_url, "probe") as conference:
    print(await conference.get_participants())

or call open()/close() directly. diagnose() and is_created() can be called standalone, without opening the conference, to check what's needed to join at all and whether there's anyone to join in the first place.

async __aenter__()[source]
Return type:

Self

async __aexit__(*exc_info)[source]
Return type:

None

__dict__ = mappingproxy({'__module__': 'inspect_jitsi.xmpp.conference', '__firstlineno__': 50, '__doc__': 'A Jitsi Meet conference (room), joined anonymously as an observer.\n\nUse as an async context manager::\n\n    async with JitsiConference(conference_url, "probe") as conference:\n        print(await conference.get_participants())\n\nor call :meth:`open`/:meth:`close` directly. :meth:`diagnose` and\n:meth:`is_created` can be called standalone, without opening the\nconference, to check what\'s needed to join at all and whether there\'s\nanyone to join in the first place.\n', '__init__': <function JitsiConference.__init__>, 'room': <property object>, 'nick': <property object>, 'name': <property object>, 'open': <function JitsiConference.open>, 'close': <function JitsiConference.close>, 'get_participants': <function JitsiConference.get_participants>, 'is_created': <function JitsiConference.is_created>, 'diagnose': <function JitsiConference.diagnose>, '_probe_stream': <function JitsiConference._probe_stream>, '__aenter__': <function JitsiConference.__aenter__>, '__aexit__': <function JitsiConference.__aexit__>, '__static_attributes__': ('_anonymous_domain', '_connection', '_muc_domain', 'conference_url', 'timeout'), '__dict__': <attribute '__dict__' of 'JitsiConference' objects>, '__weakref__': <attribute '__weakref__' of 'JitsiConference' objects>, '__annotations__': {}})
__firstlineno__ = 50
__init__(conference_url, nick=None, *, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]
__module__ = 'inspect_jitsi.xmpp.conference'
__static_attributes__ = ('_anonymous_domain', '_connection', '_muc_domain', 'conference_url', 'timeout')
__weakref__

list of weak references to the object

async _probe_stream(ws_domain, xmpp_domain, result)[source]
Return type:

None

async close()[source]

Leave the conference and disconnect.

Return type:

None

async diagnose()[source]

Probe the server and report what's needed to read this room's occupancy.

Unlike open(), this never joins the room - it checks whether /config.js was readable, which internal domains it advertises, whether the XMPP domain is actually served over the WebSocket endpoint, which SASL mechanisms it offers, and whether anonymous login succeeds (i.e. whether a token/JWT is required).

Return type:

DiagnosisResult

async get_participants()[source]

Return the participants currently known to be in the conference.

Return type:

list[Participant]

async is_created()[source]

Return whether this room currently exists, without joining it.

Jitsi's MUC returns a forbidden disco#info error for a room that exists (disco is restricted to occupants) but item-not-found for one that hasn't been created yet (or was destroyed once everyone left) - this tells the two apart without joining.

Raises:

inspect_jitsi.xmpp.JitsiConnectionError – the XMPP connection failed or was lost (refused, dropped, timed out, or an unparseable server response) - not raised for a room that simply doesn't exist, which is reported as False.

Return type:

bool

property name: str | None

The display name (XEP-0172) disclosed when joining, if any.

property nick: str

The MUC nickname this conference is (or will be) joined under.

async open()[source]

Connect and join the conference.

Return type:

None

property room: str

The room name, e.g. "SomeRoomName".

exception inspect_jitsi.JitsiConnectionError[source]

Bases: ConnectionError

Talking to a Jitsi deployment's XMPP endpoint failed or was lost.

Covers everything below the room-existence/participant-roster level - the WebSocket connection being refused, dropped, or timing out, and a server response this hand-rolled client can't parse - as well as the protocol-level rejections (stream errors, failed SASL, a failed MUC join) this module raises as it negotiates a session. Catch this (or the plain ConnectionError it subclasses) instead of guessing at every stdlib/websockets exception a live XMPP session might raise.

__firstlineno__ = 126
__module__ = 'inspect_jitsi.xmpp.connection'
__static_attributes__ = ()
__weakref__

list of weak references to the object

class inspect_jitsi.JitsiXmppConnection(conference_url, nick=None, *, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]

Bases: object

An anonymous XMPP/MUC connection, joined to one Jitsi Meet room.

Connects over the WebSocket endpoint the Jitsi web client itself uses, joins the room's MUC under the given nickname (disclosing name as its XEP-0172 display name, if given - no display name is disclosed by default), and keeps a live roster of the other participants (updated as they join/leave for as long as the connection stays open).

Use as an async context manager:

async with JitsiXmppConnection(conference_url, "probe") as conn:
    print(await conn.get_participant_count())

or call open()/close() directly.

async __aenter__()[source]
Return type:

Self

async __aexit__(*exc_info)[source]
Return type:

None

__annotations__ = {'_occupant_jid': 'str | None', '_participants': 'dict[str, Participant]', '_reader_task': 'asyncio.Task | None', '_ws': 'websockets.ClientConnection | None', 'error': 'Exception | None', 'room_created': 'bool | None'}
__dict__ = mappingproxy({'__module__': 'inspect_jitsi.xmpp.connection', '__firstlineno__': 332, '__doc__': 'An anonymous XMPP/MUC connection, joined to one Jitsi Meet room.\n\nConnects over the WebSocket endpoint the Jitsi web client itself uses,\njoins the room\'s MUC under the given nickname (disclosing `name` as its\nXEP-0172 display name, if given - no display name is disclosed by\ndefault), and keeps a live roster of the other participants (updated as\nthey join/leave for as long as the connection stays open).\n\nUse as an async context manager::\n\n    async with JitsiXmppConnection(conference_url, "probe") as conn:\n        print(await conn.get_participant_count())\n\nor call :meth:`open`/:meth:`close` directly.\n', '__init__': <function JitsiXmppConnection.__init__>, 'ws': <property object>, 'open': <function JitsiXmppConnection.open>, '_read_loop': <function JitsiXmppConnection._read_loop>, '_handle_presence': <function JitsiXmppConnection._handle_presence>, 'close': <function JitsiXmppConnection.close>, 'other_participants': <function JitsiXmppConnection.other_participants>, 'get_participants': <function JitsiXmppConnection.get_participants>, 'get_participant_count': <function JitsiXmppConnection.get_participant_count>, '__aenter__': <function JitsiXmppConnection.__aenter__>, '__aexit__': <function JitsiXmppConnection.__aexit__>, '__static_attributes__': ('_anonymous_domain', '_focus_seen', '_joined', '_muc_domain', '_occupant_jid', '_participants', '_reader_task', '_ws', 'changed', 'conference_url', 'ended', 'error', 'name', 'nick', 'room', 'room_closed', 'room_created', 'timeout', 'ws_domain'), '__dict__': <attribute '__dict__' of 'JitsiXmppConnection' objects>, '__weakref__': <attribute '__weakref__' of 'JitsiXmppConnection' objects>, '__annotations__': {'_ws': 'websockets.ClientConnection | None', '_occupant_jid': 'str | None', '_participants': 'dict[str, Participant]', '_reader_task': 'asyncio.Task | None', 'error': 'Exception | None', 'room_created': 'bool | None'}})
__firstlineno__ = 332
__init__(conference_url, nick=None, *, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]
__module__ = 'inspect_jitsi.xmpp.connection'
__static_attributes__ = ('_anonymous_domain', '_focus_seen', '_joined', '_muc_domain', '_occupant_jid', '_participants', '_reader_task', '_ws', 'changed', 'conference_url', 'ended', 'error', 'name', 'nick', 'room', 'room_closed', 'room_created', 'timeout', 'ws_domain')
__weakref__

list of weak references to the object

_handle_presence(stanza)[source]
Return type:

None

_occupant_jid: str | None
_participants: dict[str, Participant]
async _read_loop()[source]

Continuously update the roster from incoming presence stanzas.

Waits without a timeout - a quiet room is normal here; a dead connection is detected by the WebSocket's own keepalive pings.

Return type:

None

_reader_task: Task | None
_ws: ClientConnection | None
changed

Set whenever the roster changes or the connection ends. Callers clear it themselves after they've looked at the new state.

async close()[source]

Leave the room (if joined) and close the connection.

Return type:

None

ended

Set once the background reader stops (room closed or connection lost).

error: Exception | None

Why the reader stopped, if the connection was lost.

async get_participant_count()[source]

Return the number of participants currently in the room.

Return type:

int

async get_participants()[source]

Return the participants currently known to be in the room.

Return type:

list[Participant]

async open()[source]

Connect, authenticate anonymously, and join the room's MUC.

If the room doesn't exist yet and this deployment restricts room creation to privileged users, this still succeeds - room_created is set to False and the room is treated as empty (no participants), rather than raising.

Return type:

None

other_participants()[source]

The participants known to be in the room, in the order they joined.

Not counting this connection itself. Synchronous, unlike get_participants().

Return type:

list[Participant]

room_closed

the MUC removed us (room destroyed, we were kicked) or jicofo, the conference focus, left it.

Type:

True once the room is known to be over

room_created: bool | None

Whether the room existed to join. None before open().

False means the room didn't exist yet (or was destroyed once everyone left) and this deployment restricts room creation to privileged users (jicofo) - not a genuine failure, just an empty room: open() still succeeds, with no participants.

property ws: ClientConnection

The underlying WebSocket connection.

None before open() is called and after close() - accessing it in either state is a bug, so it raises instead of silently returning None.

class inspect_jitsi.Participant(jid, nick, name=None, role=None, affiliation=None, real_jid=None, occupant_id=None)[source]

Bases: object

One occupant of a room, parsed from their MUC presence stanza.

__annotations__ = {'affiliation': 'str | None', 'jid': 'str', 'name': 'str | None', 'nick': 'str', 'occupant_id': 'str | None', 'real_jid': 'str | None', 'role': 'str | None'}
__dataclass_fields__ = {'affiliation': Field(name='affiliation',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'jid': Field(name='jid',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'name': Field(name='name',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'nick': Field(name='nick',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'occupant_id': Field(name='occupant_id',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'real_jid': Field(name='real_jid',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'role': Field(name='role',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)}
__dataclass_params__ = _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=True,match_args=True,kw_only=False,slots=False,weakref_slot=False)
__delattr__(name)

Implement delattr(self, name).

__dict__ = mappingproxy({'__module__': 'inspect_jitsi.xmpp.participant', '__firstlineno__': 31, '__annotations__': {'jid': 'str', 'nick': 'str', 'name': 'str | None', 'role': 'str | None', 'affiliation': 'str | None', 'real_jid': 'str | None', 'occupant_id': 'str | None'}, '__doc__': 'One occupant of a room, parsed from their MUC presence stanza.', 'name': None, 'role': None, 'affiliation': None, 'real_jid': None, 'occupant_id': None, 'from_presence': <classmethod(<function Participant.from_presence>)>, 'to_dict': <function Participant.to_dict>, 'to_json': <function Participant.to_json>, '__static_attributes__': (), '__dict__': <attribute '__dict__' of 'Participant' objects>, '__weakref__': <attribute '__weakref__' of 'Participant' objects>, '__dataclass_params__': _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=True,match_args=True,kw_only=False,slots=False,weakref_slot=False), '__dataclass_fields__': {'jid': Field(name='jid',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'nick': Field(name='nick',type='str',default=<dataclasses._MISSING_TYPE object>,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'name': Field(name='name',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'role': Field(name='role',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'affiliation': Field(name='affiliation',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'real_jid': Field(name='real_jid',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD), 'occupant_id': Field(name='occupant_id',type='str | None',default=None,default_factory=<dataclasses._MISSING_TYPE object>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD)}, '__replace__': <function _replace>, '__hash__': <function Participant.__hash__>, '__init__': <function Participant.__init__>, '__repr__': <function Participant.__repr__>, '__eq__': <function Participant.__eq__>, '__setattr__': <function Participant.__setattr__>, '__delattr__': <function Participant.__delattr__>, '__match_args__': ('jid', 'nick', 'name', 'role', 'affiliation', 'real_jid', 'occupant_id')})
__eq__(other)

Return self==value.

__firstlineno__ = 31
__hash__()

Return hash(self).

__init__(jid, nick, name=None, role=None, affiliation=None, real_jid=None, occupant_id=None)
__match_args__ = ('jid', 'nick', 'name', 'role', 'affiliation', 'real_jid', 'occupant_id')
__module__ = 'inspect_jitsi.xmpp.participant'
__replace__(**changes)
__repr__()

Return repr(self).

__setattr__(name, value)

Implement setattr(self, name, value).

__static_attributes__ = ()
__weakref__

list of weak references to the object

affiliation: str | None = None

MUC affiliation, e.g. "owner", "member", "none".

classmethod from_presence(presence)[source]

Build a Participant from a MUC <presence/> stanza.

Return type:

Participant

jid: str

The MUC occupant JID, e.g. "room@muc.example.com/<nick-resource>".

name: str | None = None

Human display name, if the client sent one (XEP-0172 <nick/>).

nick: str

The MUC nickname - the resourcepart of jid.

occupant_id: str | None = None

Stable per-session anonymous id (XEP occupant-id), if disclosed.

Unlike jid/nick, this stays the same for one occupant across the conference even where the MUC nick is a random per-join identifier - useful for telling "still the same person" from "someone new".

real_jid: str | None = None

The occupant's real (non-anonymous, but still internal) JID, if disclosed.

role: str | None = None

MUC role, e.g. "moderator", "participant".

to_dict()[source]

Return this participant as a plain, JSON-serializable dict.

Return type:

dict

to_json()[source]

Return this participant as a JSON string.

Return type:

str

inspect_jitsi.diagnose_jitsi_access(conference_url, timeout=10)[source]

Probe a Jitsi deployment and report what's needed to read occupant counts.

See inspect_jitsi.xmpp.JitsiConference.diagnose() for details.

Return type:

DiagnosisResult

async inspect_jitsi.discover_hosts(domain, timeout=10)[source]

Read https://<domain>/config.js to find the real XMPP/MUC domains.

A Jitsi deployment's internal domain names often differ from the public hostname (e.g. docker-jitsi-meet defaults to "meet.jitsi" internally). Returns a dict with keys "domain", "muc", "anonymousdomain" - any of which may be None if not found.

Return type:

dict[str, str | None]

inspect_jitsi.get_participant_count(conference_url, nick=None, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]

Return the number of participants currently in a Jitsi Meet room.

Parameters:
  • conference_url (str) – e.g. "https://meet.example.com/SomeRoomName".

  • nick (str | None) – the MUC nickname to join under. Random if not given.

  • name (str | None) – the display name (XEP-0172) to disclose when joining. No display name is disclosed if not given.

  • anonymous_domain (str | None) – override the XMPP domain used for stream/login. Auto-discovered from the site's /config.js if not given.

  • muc_domain (str | None) – override the MUC component domain (e.g. "muc.meet.jitsi" or "conference.example.com"). Auto-discovered if not given.

  • timeout (float) – seconds to wait for each network step.

Return type:

int

Returns:

Number of people already in the room, not counting this probe (0 if the room is empty).

Raises:

inspect_jitsi.xmpp.JitsiConnectionError – the XMPP connection failed or was lost (refused, dropped, timed out, or an unparseable server response).

inspect_jitsi.get_participants(conference_url, nick=None, name=None, anonymous_domain=None, muc_domain=None, timeout=10)[source]

Return the participants currently in a Jitsi Meet room.

Parameters:
  • conference_url (str) – e.g. "https://meet.example.com/SomeRoomName".

  • nick (str | None) – the MUC nickname to join under. Random if not given.

  • name (str | None) – the display name (XEP-0172) to disclose when joining. No display name is disclosed if not given.

  • anonymous_domain (str | None) – override the XMPP domain used for stream/login. Auto-discovered from the site's /config.js if not given.

  • muc_domain (str | None) – override the MUC component domain (e.g. "muc.meet.jitsi" or "conference.example.com"). Auto-discovered if not given.

  • timeout (float) – seconds to wait for each network step.

Return type:

list[Participant]

Returns:

The participants already in the room, not counting this probe (empty if the room is empty).

Raises:

inspect_jitsi.xmpp.JitsiConnectionError – the XMPP connection failed or was lost (refused, dropped, timed out, or an unparseable server response).

inspect_jitsi.is_room_created(conference_url, anonymous_domain=None, muc_domain=None, timeout=10)[source]

Return whether a Jitsi Meet room currently exists, without joining it.

Parameters:
  • conference_url (str) – e.g. "https://meet.example.com/SomeRoomName".

  • anonymous_domain (str | None) – override the XMPP domain used for stream/login. Auto-discovered from the site's /config.js if not given.

  • muc_domain (str | None) – override the MUC component domain (e.g. "muc.meet.jitsi" or "conference.example.com"). Auto-discovered if not given.

  • timeout (float) – seconds to wait for each network step.

Raises:

inspect_jitsi.xmpp.JitsiConnectionError – the XMPP connection failed or was lost (refused, dropped, timed out, or an unparseable server response) - not raised for a room that simply doesn't exist, which is reported as False.

Return type:

bool

async inspect_jitsi.monitor_conference(conference_url, *, nick=None, name=None, anonymous_domain=None, muc_domain=None, timeout=60, connect_timeout=10, create=False, stay=False)[source]

Yield the room's state whenever it changes, until the room is closed.

A room that does not exist is not joined (joining could create it): a single state with status.open false is yielded instead, unless create is set.

Parameters:
  • conference_url (str) – e.g. "https://meet.example.com/SomeRoomName".

  • nick (str | None) – the MUC nickname to join under. Random if not given.

  • name (str | None) – the display name (XEP-0172) to disclose when joining.

  • anonymous_domain (str | None) – override the XMPP domain. Auto-discovered if not given.

  • muc_domain (str | None) – override the MUC domain. Auto-discovered if not given.

  • timeout (float) – seconds to keep trying to reconnect after the connection is lost, before giving up. 0 disables reconnecting.

  • connect_timeout (float) – seconds to wait for each network step.

  • create (bool) – join the room even if it does not exist, which creates it on deployments that let anyone create rooms. Otherwise the existence of the room is checked before every join.

  • stay (bool) – keep monitoring when nobody else is in the room. Otherwise the room counts as closed - and the monitor leaves it - as soon as the monitor is the only one left in it.

Raises:
  • ValueError – the conference URL cannot be parsed.

  • inspect_jitsi.xmpp.JitsiConnectionError – the first connection failed, or the connection was lost and could not be re-established within timeout seconds.

Return type:

AsyncIterator[dict[str, Any]]

inspect_jitsi.monitor_room(conference_url, nick=None, name=None, anonymous_domain=None, muc_domain=None, timeout=60, connect_timeout=10, create=False, stay=False)[source]

Yield the state of a Jitsi Meet room whenever it changes, until it is closed.

This is what inspect-jitsi monitor prints, one dict per JSON line:

for state in monitor_room("https://meet.example.com/SomeRoomName"):
    print(state["status"], len(state["participants"]))

Each dict has the keys room, participants (a list, in the order they joined) and status (open and attempts). The first one is yielded once connected; the last one has status["open"] false. Reconnecting after a lost connection only increases status["attempts"].

Parameters:
  • conference_url (str) – e.g. "https://meet.example.com/SomeRoomName".

  • nick (str | None) – the MUC nickname to join under. Random if not given.

  • name (str | None) – the display name (XEP-0172) to disclose when joining. No display name is disclosed if not given.

  • anonymous_domain (str | None) – override the XMPP domain used for stream/login. Auto-discovered from the site's /config.js if not given.

  • muc_domain (str | None) – override the MUC component domain. Auto-discovered if not given.

  • timeout (float) – seconds to keep trying to reconnect after the connection is lost, before giving up. 0 disables reconnecting.

  • connect_timeout (float) – seconds to wait for each network step.

  • create (bool) – join the room even if it does not exist, which creates it on deployments that let anyone create rooms. Otherwise a room that does not exist is reported as closed, without joining it.

  • stay (bool) – keep monitoring when nobody else is in the room. Otherwise the room counts as closed - and the monitor leaves it - as soon as the monitor is the only one left in it.

Raises:
  • ValueError – the conference URL cannot be parsed.

  • inspect_jitsi.xmpp.JitsiConnectionError – the first connection failed, or the connection was lost and could not be re-established within timeout seconds.

Return type:

Iterator[dict[str, Any]]