class Conference(Bookmark): """ An bookmark for a groupchat. .. attribute:: name The name of the bookmark. .. attribute:: jid The jid under which the groupchat is accessible. .. attribute:: autojoin Whether to join automatically, when the client starts. .. attribute:: nick The nick to use in the groupchat. .. attribute:: password The password used to access the groupchat. """ TAG = (namespaces.xep0048, "conference") autojoin = xso.Attr(tag="autojoin", type_=xso.Bool(), default=False) jid = xso.Attr(tag="jid", type_=xso.JID()) name = xso.Attr(tag="name", type_=xso.String(), default=None) nick = xso.ChildText( (namespaces.xep0048, "nick"), default=None ) password = xso.ChildText( (namespaces.xep0048, "password"), default=None ) def __init__(self, name, jid, *, autojoin=False, nick=None, password=None): self.autojoin = autojoin self.jid = jid self.name = name self.nick = nick self.password = password def __repr__(self): return "Conference({!r}, {!r}, autojoin={!r}, " \ "nick={!r}, password{!r})".\ format(self.name, self.jid, self.autojoin, self.nick, self.password) @property def primary(self): return self.jid @property def secondary(self): return (self.name, self.nick, self.password, self.autojoin)
class Query(xso.XSO): """ :xep:`92` Software Version query :class:`~aioxmpp.xso.XSO`. .. attribute:: name Software name as string. May be :data:`None`. .. attribute:: version Software version as string. May be :data:`None`. .. attribute:: os Operating system as string. May be :data:`None`. """ TAG = namespaces.xep0092_version, "query" version = xso.ChildText( (namespaces.xep0092_version, "version"), default=None, ) name = xso.ChildText( (namespaces.xep0092_version, "name"), default=None, ) os = xso.ChildText( (namespaces.xep0092_version, "os"), default=None, )
class DestroyRequest(xso.XSO): TAG = (namespaces.xep0045_muc_owner, "destroy") reason = xso.ChildText((namespaces.xep0045_muc_owner, "reason"), default=None) password = xso.ChildText((namespaces.xep0045_muc_owner, "password"), default=None) jid = xso.Attr("jid", type_=xso.JID(), default=None)
class Invite(xso.XSO): TAG = (namespaces.xep0045_muc_user, "invite") from_ = xso.Attr("from", type_=xso.JID(), default=None) to = xso.Attr("to", type_=xso.JID(), default=None) reason = xso.ChildText((namespaces.xep0045_muc_user, "reason"), default=None) password = xso.ChildText((namespaces.xep0045_muc_user, "password"), default=None)
class UserExt(xso.XSO): TAG = (namespaces.xep0045_muc_user, "x") status_codes = xso.ChildValueList(StatusCodeList(), container_type=set) destroy = xso.Child([DestroyNotification]) decline = xso.Child([Decline]) invites = xso.ChildList([Invite]) items = xso.ChildList([UserItem]) password = xso.ChildText((namespaces.xep0045_muc_user, "password"), default=None) def __init__(self, status_codes=[], destroy=None, decline=None, invites=[], items=[], password=None): super().__init__() self.status_codes.update(status_codes) self.destroy = destroy self.decline = decline self.invites.extend(invites) self.items.extend(items) self.password = password
class GenericExt(xso.XSO): TAG = (namespaces.xep0045_muc, "x") history = xso.Child([History]) password = xso.ChildText((namespaces.xep0045_muc, "password"), default=None)
class DestroyNotification(xso.XSO): TAG = (namespaces.xep0045_muc_user, "destroy") reason = xso.ChildText((namespaces.xep0045_muc_user, "reason"), default=None) jid = xso.Attr("jid", type_=xso.JID(), default=None)
class Query(xso.XSO): TAG = (namespace, "query") name = xso.ChildText( (namespace, "name"), default=None, ) version = xso.ChildText( (namespace, "version"), default=None, ) os = xso.ChildText( (namespace, "os"), default=None, )
class Conference(xso.XSO): """ An bookmark for a groupchat. .. attribute:: name The name of the bookmark. .. attribute:: jid The jid under which the groupchat is accessible. .. attribute:: autojoin Whether to join automatically, when the client starts. .. attribute:: nick The nick to use in the groupchat. .. attribute:: password The password used to access the groupchat. """ TAG = (namespaces.xep0048, "conference") autojoin = xso.Attr(tag="autojoin", type_=xso.Bool(), default=False) jid = xso.Attr(tag="jid", type_=xso.JID()) name = xso.Attr(tag="name", type_=xso.String(), default=None) nick = xso.ChildText((namespaces.xep0048, "nick"), default=None) password = xso.ChildText((namespaces.xep0048, "password"), default=None) def __init__(self, name, jid, *, autojoin=False, nick=None, password=None): self.autojoin = autojoin self.jid = jid self.name = name self.nick = nick self.password = password def __eq__(self, other): return (isinstance(other, Conference) and other.name == self.name and other.jid == self.jid and other.autojoin == self.autojoin and other.name == self.name and other.password == self.password)
class Decline(xso.XSO): TAG = (namespaces.xep0045_muc_user, "decline") from_ = xso.Attr("from", type_=xso.JID(), default=None) to = xso.Attr("to", type_=xso.JID(), default=None) reason = xso.ChildText((namespaces.xep0045_muc_user, "reason"), default=None)
class AdminItem(ItemBase): TAG = (namespaces.xep0045_muc_admin, "item") actor = xso.Child([AdminActor]) continue_ = xso.Child([Continue]) reason = xso.ChildText((namespaces.xep0045_muc_admin, "reason"), default=None)
class UserItem(ItemBase): TAG = (namespaces.xep0045_muc_user, "item") actor = xso.Child([UserActor]) continue_ = xso.Child([Continue]) reason = xso.ChildText((namespaces.xep0045_muc_user, "reason"), default=None)
class VCardTempUpdate(xso.XSO): """ The vcard update notify element as per :xep:`0153` """ TAG = (namespaces.xep0153, "x") def __init__(self, photo=None): self.photo = photo photo = xso.ChildText((namespaces.xep0153, "photo"), type_=xso.String(), default=None)
class Option(xso.XSO): TAG = (namespaces.xep0004_data, "option") label = xso.Attr( tag="label", default=None, ) value = xso.ChildText( (namespaces.xep0004_data, "value"), default=None, ) def validate(self): if self.value is None: raise ValueError("option is missing a value")
class Data(AbstractItem): """ A :xep:`4` ``x`` element, that is, a Data Form. :param type_: Initial value for the :attr:`type_` attribute. .. attribute:: type_ The ``type`` attribute of the form, represented by one of the members of the :class:`DataType` enumeration. .. attribute:: title The (optional) title of the form. Either a :class:`str` or :data:`None`. .. attribute:: instructions A sequence of strings which represent the instructions elements on the form. .. attribute:: fields If the :class:`Data` is a form, this is a sequence of :class:`Field` elements which represent the fields to be filled in. This does not make sense on :attr:`.DataType.RESULT` typed objects. .. attribute:: items If the :class:`Data` is a table, this is a sequence of :class:`Item` instances which represent the table rows. This only makes sense on :attr:`.DataType.RESULT` typed objects. .. attribute:: reported If the :class:`Data` is a table, this is a :class:`Reported` object representing the table header. This only makes sense on :attr:`.DataType.RESULT` typed objects. .. automethod:: get_form_type """ TAG = (namespaces.xep0004_data, "x") type_ = xso.Attr("type", type_=xso.EnumCDataType(DataType)) title = xso.ChildText( (namespaces.xep0004_data, "title"), default=None, ) instructions = xso.ChildValueList(type_=InstructionsElement()) items = xso.ChildList([Item]) reported = xso.Child([Reported], required=False) def __init__(self, type_): super().__init__() self.type_ = type_ def _validate_result(self): if self.fields: raise ValueError("field in report result") fieldvars = {field.var for field in self.reported.fields} if not fieldvars: raise ValueError("empty report header") for item in self.items: itemvars = {field.var for field in item.fields} if itemvars != fieldvars: raise ValueError("field mismatch between row and header") def validate(self): super().validate() if (self.type_ != DataType.RESULT and (self.reported is not None or self.items)): raise ValueError("report in non-result") if (self.type_ == DataType.RESULT and (self.reported is not None or self.items)): self._validate_result() def get_form_type(self): """ Extract the ``FORM_TYPE`` from the fields. :return: ``FORM_TYPE`` value or :data:`None` :rtype: :class:`str` or :data:`None` Return :data:`None` if no well-formed ``FORM_TYPE`` field is found in the list of fields. .. versionadded:: 0.8 """ for field in self.fields: if field.var == "FORM_TYPE" and field.type_ == FieldType.HIDDEN: if len(field.values) != 1: return None return field.values[0]
class Field(xso.XSO): """ Represent a single field in a Data Form. :param type_: Field type, must be one of the valid field types specified in :xep:`4`. :type type_: :class:`FieldType` :param options: A mapping of values to labels defining the options in a ``list-*`` field. :type options: :class:`dict` mapping :class:`str` to :class:`str` :param values: A sequence of values currently given for the field. Having more than one value is only valid in ``*-multi`` fields. :type values: :class:`list` of :class:`str` :param desc: Description which can be shown in a tool-tip or similar, without newlines. :type desc: :class:`str` or :data:`None`s :param label: Human-readable label to be shown next to the field input :type label: :class:`str` or :data:`None` :param required: Flag to indicate that the field is required :type required: :class:`bool` :param var: "ID" identifying the field uniquely inside the form. Only required for fields carrying a meaning (thus, not for ``fixed``). :type var: :class:`str` or :data:`None` The semantics of a :class:`Field` are different depending on where it occurs: in a :class:`Data`, it is a form field to be filled in, in a :class:`Item` it is a cell of a row and in a :class:`Reported` it represents a column header. .. attribute:: required A boolean flag indicating whether the field is required. If true, the XML serialisation will contain the corresponding ``<required/>`` tag. .. attribute:: desc Single line of description for the field. This attribute represents the ``<desc/>`` element from :xep:`4`. .. attribute:: values A sequence of strings representing the ``<value/>`` elements of the field, one string for each value. .. note:: Since the requirements on the sequence of strings in :attr:`values` change depending on the :attr:`type_` attribute, validation and type conversion on assignment is very lax. The attribute accepts all sequences of strings, even if the field is for example a :attr:`FieldType.BOOLEAN` field, which allows for at most one string of a well-defined format (see the documentation there for the details). This makes it easy to inadvertendly generate invalid forms, which is why you should be using :class:`Form` subclasses when accessing forms from within normal code and some other, generic mechanism taking care of these details when showing forms in a UI framework to users. Note that devising such a mechanism is out of scope for :mod:`aioxmpp`, as every UI framework has different requirements. .. attribute:: options A dictionary mapping values to human-readable labels, representing the ``<option/>`` elements of the field. .. attribute:: var The uniquely identifying string of the (valued, that is, non-:attr:`FieldType.FIXED` field). Represents the ``var`` attribute of the field. .. attribute:: type_ The type of the field. The :attr:`type_` must be a :class:`FieldType` enumeration value and determines restrictions and constraints on other attributes. See the :class:`FieldType` enumeration and :xep:`4` for details. .. attribute:: label The human-readable label for the field, representing the ``label`` attribute of the field. May be :data:`None` if the label is omitted. """ TAG = (namespaces.xep0004_data, "field") required = xso.ChildFlag((namespaces.xep0004_data, "required"), ) desc = xso.ChildText((namespaces.xep0004_data, "desc"), default=None) values = xso.ChildValueList(type_=ValueElement()) options = xso.ChildValueMap( type_=OptionElement(), mapping_type=collections.OrderedDict, ) var = xso.Attr((None, "var"), default=None) type_ = xso.Attr( (None, "type"), type_=xso.EnumCDataType(FieldType, ), default=FieldType.TEXT_SINGLE, ) label = xso.Attr((None, "label"), default=None) def __init__(self, *, type_=FieldType.TEXT_SINGLE, options={}, values=[], desc=None, label=None, required=False, var=None): super().__init__() self.type_ = type_ self.options.update(options) self.values[:] = values self.desc = desc self.label = label self.required = required self.var = var def validate(self): super().validate() if self.type_ != FieldType.FIXED and not self.var: raise ValueError("missing attribute var") if not self.type_.has_options and self.options: raise ValueError("unexpected option on non-list field") if not self.type_.is_multivalued and len(self.values) > 1: raise ValueError("too many values on non-multi field") values_list = [opt for opt in self.options.values()] values_set = set(values_list) if len(values_list) != len(values_set): raise ValueError("duplicate option value")
class Field(xso.XSO): TAG = (namespaces.xep0004_data, "field") required = xso.ChildTag( tags=[ (namespaces.xep0004_data, "required"), ], allow_none=True) desc = xso.ChildText( (namespaces.xep0004_data, "desc"), default=None ) values = xso.ChildValueList( type_=ValueElement() ) options = xso.ChildValueMap( type_=OptionElement() ) var = xso.Attr( (None, "var"), default=None ) type_ = xso.Attr( (None, "type"), validator=xso.RestrictToSet([ "boolean", "fixed", "hidden", "jid-multi", "jid-single", "list-multi", "list-single", "text-multi", "text-private", "text-single", ]), default="text-single" ) label = xso.Attr( (None, "label"), default=None ) def __init__(self, *, type_="text-single", options={}, values=[], desc=None, label=None, required=False, var=None): super().__init__() self.type_ = type_ self.options.update(options) self.values[:] = values self.desc = desc self.label = label if required: self.required = (namespaces.xep0004_data, "required") self.var = var def validate(self): super().validate() if self.type_ != "fixed" and not self.var: raise ValueError("missing attribute var") if not self.type_.startswith("list-") and self.options: raise ValueError("unexpected option on non-list field") if not self.type_.endswith("-multi") and len(self.values) > 1: raise ValueError("too many values on non-multi field") values_list = [opt for opt in self.options.values()] values_set = set(values_list) if len(values_list) != len(values_set): raise ValueError("duplicate option value")
class OOBExtension(xso.XSO): TAG = namespaces.xep0066_oob_x, "x" url = xso.ChildText((namespaces.xep0066_oob_x, "url"))
class ResultSetMetadata(xso.XSO): """ Represent the result set or query metadata. For requests, the following attributes are relevant: .. attribute:: after Either :data:`None` or a :class:`After` object. Generally mutually exclusive with :attr:`index`. .. attribute:: before Either :data:`None` or a :class:`Before` object. Generally mutually exclusive with :attr:`index`. .. attribute:: index The index of the first result to return, or :data:`None`. Generally mutually exclusive with :attr:`after` and :attr:`before`. .. attribute:: max The maximum number of items to return or :data:`None`. Setting :attr:`max` to zero will make the peer return a :class:`ResultSetMetadata` with the total number of items in the :attr:`count` field. These methods are useful when constructing queries: .. automethod:: fetch_page .. automethod:: limit .. automethod:: last_page For responses, the following attributes are relevant: .. attribute:: first Either :data:`None` or a :class:`First` object. .. attribute:: last Either :data:`None` or a :class:`Last` object. .. attribute:: count Either :data:`None` or the number of elements in the result set. If this is a response to a query with :attr:`max` set to zero, this is the total number of elements in the queried data. These methods are useful to construct a new request from a previous response: .. automethod:: next_page .. automethod:: previous_page """ TAG = namespaces.xep0059_rsm, "set" after = xso.Child([After]) before = xso.Child([Before]) first = xso.Child([First]) last = xso.Child([Last]) count = xso.ChildText( (namespaces.xep0059_rsm, "count"), type_=xso.Integer(), default=None, ) max_ = xso.ChildText( (namespaces.xep0059_rsm, "max"), type_=xso.Integer(), default=None, ) index = xso.ChildText( (namespaces.xep0059_rsm, "index"), type_=xso.Integer(), default=None, ) @classmethod def fetch_page(cls, index, max_=None): """ Return a query set which requests a specific page. :param index: Index of the first element of the page to fetch. :type index: :class:`int` :param max_: Maximum number of elements to fetch :type max_: :class:`int` or :data:`None` :rtype: :class:`ResultSetMetadata` :return: A new request set up to request a page starting with the element indexed by `index`. .. note:: This way of retrieving items may be approximate. See :xep:`59` and the embedding protocol for which RSM is used for specifics. """ result = cls() result.index = index result.max_ = max_ return result @magicmethod def limit(self, max_): """ Limit the result set to a given number of items. :param max_: Maximum number of items to return. :type max_: :class:`int` or :data:`None` :rtype: :class:`ResultSetMetadata` :return: A new request set up to request at most `max_` items. This method can be called on the class and on objects. When called on objects, it returns a copy of the object with :attr:`max_` set accordingly. When called on the class, it creates a fresh object with :attr:`max_` set accordingly. """ if isinstance(self, type): result = self() else: result = copy.deepcopy(self) result.max_ = max_ return result def next_page(self, max_=None): """ Return a query set which requests the page after this response. :param max_: Maximum number of items to return. :type max_: :class:`int` or :data:`None` :rtype: :class:`ResultSetMetadata` :return: A new request set up to request the next page. Must be called on a result set which has :attr:`last` set. """ result = type(self)() result.after = After(self.last.value) result.max_ = max_ return result def previous_page(self, max_=None): """ Return a query set which requests the page before this response. :param max_: Maximum number of items to return. :type max_: :class:`int` or :data:`None` :rtype: :class:`ResultSetMetadata` :return: A new request set up to request the previous page. Must be called on a result set which has :attr:`first` set. """ result = type(self)() result.before = Before(self.first.value) result.max_ = max_ return result @classmethod def last_page(self_or_cls, max_=None): """ Return a query set which requests the last page. :param max_: Maximum number of items to return. :type max_: :class:`int` or :data:`None` :rtype: :class:`ResultSetMetadata` :return: A new request set up to request the last page. """ result = self_or_cls() result.before = Before() result.max_ = max_ return result
class Query(xso.XSO): """ :xep:`077` In-Band Registraion query :class:`~aioxmpp.xso.XSO`. It has the following fields described in the XEP document: .. attribute:: username .. attribute:: nick .. attribute:: password .. attribute:: name .. attribute:: first .. attribute:: last .. attribute:: email .. attribute:: address .. attribute:: city .. attribute:: state .. attribute:: zip .. attribute:: phone .. attribute:: url .. attribute:: date .. attribute:: misc .. attribute:: text .. attribute:: key .. attribute:: registered .. attribute:: remove """ TAG = (namespaces.xep0077_in_band, "query") username = xso.ChildText( (namespaces.xep0077_in_band, "username"), default=None, ) instructions = xso.ChildText( (namespaces.xep0077_in_band, "instructions"), default=None, ) nick = xso.ChildText( (namespaces.xep0077_in_band, "nick"), default=None, ) password = xso.ChildText( (namespaces.xep0077_in_band, "password"), default=None, ) name = xso.ChildText( (namespaces.xep0077_in_band, "name"), default=None, ) first = xso.ChildText( (namespaces.xep0077_in_band, "first"), default=None, ) last = xso.ChildText( (namespaces.xep0077_in_band, "last"), default=None, ) email = xso.ChildText( (namespaces.xep0077_in_band, "email"), default=None, ) address = xso.ChildText( (namespaces.xep0077_in_band, "address"), default=None, ) city = xso.ChildText( (namespaces.xep0077_in_band, "city"), default=None, ) state = xso.ChildText( (namespaces.xep0077_in_band, "state"), default=None, ) zip = xso.ChildText( (namespaces.xep0077_in_band, "zip"), default=None, ) phone = xso.ChildText( (namespaces.xep0077_in_band, "phone"), default=None, ) url = xso.ChildText( (namespaces.xep0077_in_band, "url"), default=None, ) date = xso.ChildText( (namespaces.xep0077_in_band, "date"), default=None, ) misc = xso.ChildText( (namespaces.xep0077_in_band, "misc"), default=None, ) text = xso.ChildText( (namespaces.xep0077_in_band, "text"), default=None, ) key = xso.ChildText( (namespaces.xep0077_in_band, "key"), default=None, ) registered = xso.ChildFlag((namespaces.xep0077_in_band, "registered")) remove = xso.ChildFlag((namespaces.xep0077_in_band, "remove")) def __init__(self, username=None, password=None, aux_fields=None): """ Get an xso.Query object with the info provided in he parameters. :param username: Username of the query :type username: :class:`str` :param password: Password of the query. :type password: :class:`str` :param aux_fields: Auxiliary fields in case additional info is needed. :type aux_fields: :class:`dict` :return: :class:`xso.Query` """ self.username = username self.password = password if aux_fields is not None: for key, value in aux_fields.items(): setattr(self, key, value)