From c8d802a6c78c91878971f5ef6ac7bb86c7f060ba Mon Sep 17 00:00:00 2001 From: Paulina Date: Sun, 15 Mar 2020 17:27:47 +0100 Subject: Correction and editing of the tutorials. [95 %] English version [95 %] Polish version [100%] Both version consistency check [80 %] Final sanity check + formating --- ...make_plugin_extension_for_message_and_iq.pl.rst | 160 ++++++++++++--------- .../make_plugin_extension_for_message_and_iq.rst | 122 ++++++++-------- 2 files changed, 152 insertions(+), 130 deletions(-) (limited to 'docs/howto') diff --git a/docs/howto/make_plugin_extension_for_message_and_iq.pl.rst b/docs/howto/make_plugin_extension_for_message_and_iq.pl.rst index f138311c..5d774cff 100644 --- a/docs/howto/make_plugin_extension_for_message_and_iq.pl.rst +++ b/docs/howto/make_plugin_extension_for_message_and_iq.pl.rst @@ -1,5 +1,5 @@ Jak stworzyć własny plugin rozszerzający obiekty Message i Iq w Slixmpp -======================================================================= +==================================================================== Wstęp i wymagania ----------------- @@ -61,8 +61,7 @@ Jeśli jakaś biblioteka zwróci NameError, należy zainstalować pakiet ponowni * `Konta dla Jabber` -Do testowania niezbędne będą dwa prywatne konta jabbera. -Można je stworzyć na jednym z dostępnych darmowych serwerów: +Do testowania niezbędne będą dwa prywatne konta jabbera. Można je stworzyć na jednym z dostępnych darmowych serwerów: https://www.google.com/search?q=jabber+server+list @@ -293,7 +292,7 @@ Następny plik, który należy stworzyć to `'example_plugin'`. Powinien być w class ExampleTag(ElementBase): - name = "example_tag" ##~ Nazwa głównego tagu dla XML w tym rozszerzeniu. + name = "example_tag" ##~ Nazwa głównego pliku XML w tym rozszerzeniu. namespace = "https://example.net/our_extension" ##~ Namespace obiektu jest definiowana w tym miejscu, powinien się odnosić do nazwy portalu xmpp; w wiadomości wygląda tak: plugin_attrib = "example_tag" ##~ Nazwa pod którą można odwoływać się do danych zawartych w tym pluginie. Bardziej szczegółowo: tutaj rejestrujemy nazwę obiektu by móc się do niego odwoływać z zewnątrz. Można się do niego odwoływać jak do słownika: stanza_object['example_tag'], gdzie `'example_tag'` staje się nazwą pluginu i powinno być takie samo jak name. @@ -359,7 +358,7 @@ Przykład: def send_example_message(self, to, body): #~ make_message(mfrom=None, mto=None, mtype=None, mquery=None) - # Domyślnie mtype == "chat" if None; + # Domyślnie mtype == "chat"; msg = self.make_message(mto=to, mbody=body) msg.send() #<<<<<<<<<<<< @@ -396,7 +395,7 @@ Aby otrzymać tę wiadomość, responder powinien wykorzystać odpowiedni event: #<<<<<<<<<<<< Rozszerzenie Message o nowy tag -+++++++++++++++++++++++++++++++ +------------------------- Aby rozszerzyć obiekt Message o wybrany tag, plugin powinien zostać zarejestrowany jako rozszerzenie dla obiektu Message: @@ -415,8 +414,8 @@ Aby rozszerzyć obiekt Message o wybrany tag, plugin powinien zostać zarejestro #<<<<<<<<<<<< class ExampleTag(ElementBase): - name = "example_tag" ##~ Nazwa pliku XML dla tego rozszerzenia.. - namespace = "https://example.net/our_extension" ##~ Nazwa obiektu, np. . + name = "example_tag" ##~ Nazwa głównego pliku XML dla tego rozszerzenia.. + namespace = "https://example.net/our_extension" ##~ Nazwa obiektu, np. . Powinna zostać zmieniona na własną. plugin_attrib = "example_tag" ##~ Nazwa, którą można odwołać się do obiektu. W szczególności, do zarejestronanego obieksu można odwołać się przez: nazwa_obiektu['tag']. gdzie `'tag'` jest nazwą ElementBase extension. Nazwa powinna być taka sama jak "name" wyżej. @@ -464,7 +463,7 @@ Teraz, po rejestracji tagu, można rozszerzyć wiadomość. Po uruchomieniu, logging powinien wyświetlić Message wraz z tagiem `'example_tag'` zawartym w środku , oraz z napisem `'Work'` i nadanym namespace. Nadanie oddzielnego sygnału dla rozszerzonej wiadomości -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ +------------------------- Jeśli event nie zostanie sprecyzowany, to zarówno rozszerzona jak i podstawowa wiadomość będą przechwytywane przez sygnał `'message'`. Aby nadać im oddzielny event, należy zarejestrować odpowiedni handler dla namespace'a i tagu, aby stworzyć unikalną kombinację, która pozwoli na przechwycenie wyłącznie pożądanych wiadomości (lub Iq object). @@ -480,10 +479,9 @@ Jeśli event nie zostanie sprecyzowany, to zarówno rozszerzona jak i podstawowa namespace = ExampleTag.namespace self.xmpp.register_handler( - Callback('ExampleMessage Event:example_tag', ##~ Nazwa tego Callback + Callback('ExampleMessage Event:example_tag',##~ Nazwa tego Callback StanzaPath(f'message/{{{namespace}}}example_tag'), ##~ Przechwytuje wyłącznie Message z tagiem example_tag i namespace takim jaki zdefiniowaliśmy w ExampleTag self.__handle_message)) ##~ Metoda do której zostaje przypisany przechwycony odpowiedni obiekt, powinna wywołać odpowiedni event dla klienta. - register_stanza_plugin(Message, ExampleTag) ##~ Zarejestrowany rozszerzony tag dla obiektu Message. Jeśli to nie zostanie zrobione, message['example_tag'] będzie polem tekstowym, a nie rozszerzeniem i nie będzie mogło zawierać atrybutów i podelementów. def __handle_message(self, msg): @@ -560,8 +558,8 @@ Natomiast obiekt komunikacji (Iq) już będzie wymagał odpowiedzi, więc obydwa Użyteczne metody i inne ----------------------- -Modyfikacja przykładowego obiektu `Message` na obiekt `Iq`. -++++++++++++++++++++++++++++++++++++++++++++++++++++ +Modyfikacja przykładowego obiektu `Message` na obiekt `Iq` +------------------------- Aby przerobić przykładowy obiekt Message na obiekt Iq, należy zarejestrować nowy handler dla Iq, podobnie jak zostało to przedstawione w rozdziale `"Rozszerzenie Message o tag"`. Tym razem, przykład będzie zawierał kilka rodzajów Iq o oddzielnych typami. Poprawia to czytelność kodu oraz usprawnia weryfikację poprawności działania. Wszystkie Iq powinny odesłać odpowiedź z tym samym Id i odpowiedzią do wysyłającego. W przeciwnym wypadku, wysyłający dostanie Iq zwrotne typu error, zawierające informacje o przekroczonym czasie oczekiwania (timeout). @@ -661,6 +659,7 @@ Domyślnie parametr `'clear'` dla `'Iq.reply'` jest ustawiony na True. Wtedy to, #<<<<<<<<<<<< def start(self, event): + # Dwie niewymagane metody pozwalające innym użytkownikom zobaczyć dostępnośc online self.send_presence() self.get_roster() @@ -688,7 +687,7 @@ Domyślnie parametr `'clear'` dla `'Iq.reply'` jest ustawiony na True. Wtedy to, #<<<<<<<<<<<< Dostęp do elementów -+++++++++++++++++++ +------------------------- Jest kilka możliwości dostania się do pól wewnątrz Message lub Iq. Po pierwsze, z poziomu klienta, można dostać zawartość jak ze słownika: @@ -715,12 +714,12 @@ Z rozszerzenia ExampleTag, dostęp do elementów jest podobny, tyle że, nie wym #File: $WORKDIR/example/example plugin.py class ExampleTag(ElementBase): - name = "example_tag" - namespace = "https://example.net/our_extension" + name = "example_tag" ##~ Nazwa głównego pliku XML tego rozszerzenia. + namespace = "https://example.net/our_extension" ##~ Nazwa obiektu, np. . Powinna zostać zmieniona na własną. - plugin_attrib = "example_tag" + plugin_attrib = "example_tag" ##~ Nazwa, którą można odwołać się do obiektu. W szczególności, do zarejestronanego obieksu można odwołać się przez: nazwa_obiektu['tag']. gdzie `'tag'` jest nazwą ElementBase extension. Nazwa powinna być taka sama jak "name" wyżej. - interfaces = {"boolean", "some_string"} + interfaces = {"boolean", "some_string"} ##~ Lista kluczy słownika, które mogą być użyte z obiektem. Na przykład: `stanza_object['example_tag']` zwraca {"another": "some", "data": "some"}, gdzie `'example_tag'` jest nazwą rozszerzenia ElementBase. #>>>>>>>>>>>> def get_some_string(self): @@ -756,6 +755,7 @@ Kiedy odpowiednie gettery i settery są tworzone, można sprawdzić, czy na pewn self.add_event_handler("example_tag_error_iq", self.example_tag_error_iq) def send_example_iq(self, to): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get") iq['example_tag']['boolean'] = "True" #Przypisanie wprost #>>>>>>>>>>>> @@ -765,7 +765,7 @@ Kiedy odpowiednie gettery i settery są tworzone, można sprawdzić, czy na pewn iq.send() Wczytanie ExampleTag ElementBase z pliku XML, łańcucha znaków i innych obiektów -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +------------------------- Jest wiele możliwości na wczytanie wcześniej zdefiniowanego napisu z pliku albo lxml (ElementTree). Poniższy przykład wykorzystuje parsowanie typu napisowego do lxml (ElementTree) i przekazanie atrybutów. @@ -778,12 +778,12 @@ Jest wiele możliwości na wczytanie wcześniej zdefiniowanego napisu z pliku al #... class ExampleTag(ElementBase): - name = "example_tag" - namespace = "https://example.net/our_extension" + name = "example_tag" ##~ Nazwa głównego pliku XML tego rozszerzenia. + namespace = "https://example.net/our_extension" ##~ Nazwa obiektu, np. . Powinna zostać zmieniona na własną. - plugin_attrib = "example_tag" + plugin_attrib = "example_tag" ##~ Nazwa, którą można odwołać się do obiektu. W szczególności, do zarejestronanego obieksu można odwołać się przez: nazwa_obiektu['tag']. gdzie `'tag'` jest nazwą ElementBase extension. Nazwa powinna być taka sama jak "name" wyżej. - interfaces = {"boolean", "some_string"} + interfaces = {"boolean", "some_string"} ##~ Lista kluczy słownika, które mogą być użyte z obiektem. Na przykład: `stanza_object['example_tag']` zwraca {"another": "some", "data": "some"}, gdzie `'example_tag'` jest nazwą rozszerzenia ElementBase. #>>>>>>>>>>>> def setup_from_string(self, string): @@ -833,6 +833,7 @@ Do przetestowania tej funkcjonalności, potrzebny jest pliku zawierający xml z self.add_event_handler("example_tag_error_iq", self.example_tag_error_iq) def start(self, event): + # Dwie niewymagane metody pozwalające innym użytkownikom zobaczyć dostępnośc online self.send_presence() self.get_roster() @@ -857,18 +858,21 @@ Do przetestowania tej funkcjonalności, potrzebny jest pliku zawierający xml z self.disconnect() # Przykład rozłączania się aplikacji po uzyskaniu odpowiedniej ilości odpowiedzi. def send_example_iq_tag_from_file(self, to, path): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=2) iq['example_tag'].setup_from_file(path) iq.send() def send_example_iq_tag_from_element_tree(self, to, et): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=3) iq['example_tag'].setup_from_lxml(et) iq.send() def send_example_iq_tag_from_string(self, to, string): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=5) iq['example_tag'].setup_from_string(string) @@ -877,8 +881,8 @@ Do przetestowania tej funkcjonalności, potrzebny jest pliku zawierający xml z Jeśli Responder zwróci wysłane Iq, a Sender wyłączy się po trzech odpowiedziach, wtedy wszystko działa tak, jak powinno. -Łatwość użycia pluginu dla programistów -+++++++++++++++++++++++++++++++++++++++ +Łatwość użycia pluginu dla programistów +-------------------------------------- Każdy plugin powinien posiadać pewne obiektowe metody: wczytanie danych, jak w przypadku metod `setup` z poprzedniego rozdziału, gettery, settery, czy wywoływanie odpowiednich eventów. Potencjalne błędy powinny być przechwytywane z poziomu pluginu i zwracane z odpowiednim opisem błędu w postaci odpowiedzi Iq o tym samym id do wysyłającego. Aby uniknąć sytuacji kiedy plugin nie robi tego co powinien, a wiadomość zwrotna nigdy nie nadchodzi, wysyłający dostaje error z komunikatem timeout. @@ -905,33 +909,34 @@ Poniżej przykład kodu podyktowanego tymi zasadami: class OurPlugin(BasePlugin): def plugin_init(self): - self.description = "OurPluginExtension" - self.xep = "ope" + self.description = "OurPluginExtension" ##~ Tekst czytelny dla człowieka oraz do znalezienia pluginu przez inny plugin. + self.xep = "ope" ##~ Tekst czytelny dla człowieka oraz do znalezienia pluginu przez inny plugin poprzez dodanie go do `slixmpp/plugins/__init__.py` do funkcji `__all__` z 'xep_OPE'. namespace = ExampleTag.namespace self.xmpp.register_handler( - Callback('ExampleGet Event:example_tag', - StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), - self.__handle_get_iq)) + Callback('ExampleGet Event:example_tag', ##~ Nazwa tego Callbacku + StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), ##~ Obsługuje tylko Iq o typie 'get' oraz example_tag + self.__handle_get_iq)) ##~ Metoda przechwytuje odpowiednie Iq, powinna wywołać event u klienta. self.xmpp.register_handler( - Callback('ExampleResult Event:example_tag', - StanzaPath(f"iq@type=result/{{{namespace}}}example_tag"), - self.__handle_result_iq)) + Callback('ExampleGet Event:example_tag', ##~ Nazwa tego Callbacku + StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), ##~ Obsługuje tylko Iq o typie 'result' oraz example_tag + self.__handle_get_iq)) ##~ Metoda przechwytuje odpowiednie Iq, powinna wywołać event u klienta. self.xmpp.register_handler( - Callback('ExampleError Event:example_tag', - StanzaPath(f"iq@type=error/{{{namespace}}}example_tag"), - self.__handle_error_iq)) + Callback('ExampleGet Event:example_tag', ##~ Nazwa tego Callbacku + StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), ##~ Obsługuje tylko Iq o typie 'error' oraz example_tag + self.__handle_get_iq)) ##~ Metoda przechwytuje odpowiednie Iq, powinna wywołać event u klienta. self.xmpp.register_handler( - Callback('ExampleMessage Event:example_tag', - StanzaPath(f'message/{{{namespace}}}example_tag'), - self.__handle_message)) - - register_stanza_plugin(Iq, ExampleTag) - register_stanza_plugin(Message, ExampleTag) - + Callback('ExampleMessage Event:example_tag',##~ Nazwa tego Callbacku + StanzaPath(f'message/{{{namespace}}}example_tag'), ##~ Obsługije tylko Message z example_tag + self.__handle_message)) ##~ Metoda przechwytuje odpowiednie Iq, powinna wywołać event u klienta. + + register_stanza_plugin(Iq, ExampleTag) ##~ Zarejestrowane rozszerzenia tagu dla Iq. Bez tego, iq['example_tag'] będzie polem tekstowym, a nie kontenerem i nie będzie można zmieniać w nim pól i tworzyć podelementów. + register_stanza_plugin(Message, ExampleTag) ##~ Zarejestrowane rozszerzenia tagu dla Message. Bez tego, message['example_tag'] będzie polem tekstowym, a nie kontenerem i nie będzie można zmieniać w nim pól i tworzyć podelementów. + + # Wszystkie możliwe typy iq: get, set, error, result def __handle_get_iq(self, iq): if iq.get_some_string is None: error = iq.reply(clear=False) @@ -939,25 +944,28 @@ Poniżej przykład kodu podyktowanego tymi zasadami: error["error"]["condition"] = "missing-data" error["error"]["text"] = "Without some_string value returns error." error.send() - self.xmpp.event('example_tag_get_iq', iq) + # Zrób coś z otrzymanym Iq + self.xmpp.event('example_tag_get_iq', iq) ##~ Wywołanie eventu, który może być przesłany do klienta lub zmieniony po drodze. def __handle_result_iq(self, iq): - self.xmpp.event('example_tag_result_iq', iq) + # Zrób coś z otrzymanym Iq + self.xmpp.event('example_tag_result_iq', iq) ##~ Wywołanie eventu, który może być przesłany do klienta lub zmieniony po drodze. def __handle_error_iq(self, iq): - # Do something with received iq - self.xmpp.event('example_tag_error_iq', iq) + # Zrób coś z otrzymanym Iq + self.xmpp.event('example_tag_error_iq', iq) ##~ Wywołanie eventu, który może być przesłany do klienta lub zmieniony po drodze. def __handle_message(self, msg): - self.xmpp.event('example_tag_message', msg) + # Zrób coś z otrzymanym Message + self.xmpp.event('example_tag_message', msg) ##~ Wywołanie eventu, który może być przesłany do klienta lub zmieniony po drodze. class ExampleTag(ElementBase): - name = "example_tag" - namespace = "https://example.net/our_extension" + name = "example_tag" ##~ Nazwa głównego pliku XML tego rozszerzenia. + namespace = "https://example.net/our_extension" ##~ Nazwa obiektu, np. . Powinna zostać zmieniona na własną. - plugin_attrib = "example_tag" + plugin_attrib = "example_tag" ##~ Nazwa, którą można odwołać się do obiektu. W szczególności, do zarejestronanego obieksu można odwołać się przez: nazwa_obiektu['tag']. gdzie `'tag'` jest nazwą ElementBase extension. Nazwa powinna być taka sama jak "name" wyżej. - interfaces = {"boolean", "some_string"} + interfaces = {"boolean", "some_string"} ##~ Lista kluczy słownika, które mogą być użyte z obiektem. Na przykład: `stanza_object['example_tag']` zwraca {"another": "some", "data": "some"}, gdzie `'example_tag'` jest nazwą rozszerzenia ElementBase. def setup_from_string(self, string): """Initialize tag element from string""" @@ -978,6 +986,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: self.xml.append(inner_tag) def setup_from_dict(self, data): + #Poprawnośc kluczy słownika powinna być sprawdzona self.xml.attrib.update(data) def get_boolean(self): @@ -999,6 +1008,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: self.xml.text = text def fill_interfaces(self, boolean, some_string): + #Jakaś walidacja, jeśli jest potrzebna self.set_boolean(boolean) self.set_some_string(some_string) @@ -1022,17 +1032,18 @@ Poniżej przykład kodu podyktowanego tymi zasadami: self.add_event_handler("example_tag_message", self.example_tag_message) def start(self, event): + # Dwie niewymagane metody pozwalające innym użytkownikom zobaczyć dostępnośc online self.send_presence() self.get_roster() - def example_tag_get_iq(self, iq): + def example_tag_get_iq(self, iq): # Iq zawsze powinien odpowiedzieć. Jeżeli użytkownik jest offline, zostanie zrwócony error. logging.info(iq) reply = iq.reply() reply["example_tag"].fill_interfaces(True, "Reply_string") reply.send() def example_tag_message(self, msg): - logging.info(msg) + logging.info(msg) # Na Message można odpowiedzieć, ale nie trzeba. if __name__ == '__main__': @@ -1063,7 +1074,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: args.password = getpass("Password: ") xmpp = Responder(args.jid, args.password) - xmpp.register_plugin('OurPlugin', module=example_plugin) + xmpp.register_plugin('OurPlugin', module=example_plugin) # OurPluggin jest nazwa klasy example_plugin xmpp.connect() try: @@ -1073,7 +1084,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: xmpp.disconnect() except: pass - + .. code-block:: python #File: $WORKDIR/example/sender.py @@ -1100,10 +1111,11 @@ Poniżej przykład kodu podyktowanego tymi zasadami: self.add_event_handler("example_tag_error_iq", self.example_tag_error_iq) def start(self, event): + # Dwie niewymagane metody pozwalające innym użytkownikom zobaczyć dostępnośc online self.send_presence() self.get_roster() - self.disconnect_counter = 5 + self.disconnect_counter = 5 # Aplikacja rozłączy się po odebraniu takiej ilości odpowiedzi. self.send_example_iq(self.to) # Info_inside_tag @@ -1132,15 +1144,16 @@ Poniżej przykład kodu podyktowanego tymi zasadami: self.disconnect_counter -= 1 logging.info(str(iq)) if not self.disconnect_counter: - self.disconnect() - + self.disconnect() # Przykład rozłączania się aplikacji po uzyskaniu odpowiedniej ilości odpowiedzi. + def example_tag_error_iq(self, iq): self.disconnect_counter -= 1 logging.info(str(iq)) if not self.disconnect_counter: - self.disconnect() - + self.disconnect() # Przykład rozłączania się aplikacji po uzyskaniu odpowiedniej ilości odpowiedzi. + def send_example_iq(self, to): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get") iq['example_tag'].set_boolean(True) iq['example_tag'].set_some_string("Another_string") @@ -1148,6 +1161,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: iq.send() def send_example_message(self, to): + #~ make_message(mfrom=None, mto=None, mtype=None, mquery=None) msg = self.make_message(mto=to) msg['example_tag'].set_boolean(True) msg['example_tag'].set_some_string("Message string") @@ -1155,23 +1169,27 @@ Poniżej przykład kodu podyktowanego tymi zasadami: msg.send() def send_example_iq_tag_from_file(self, to, path): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=2) iq['example_tag'].setup_from_file(path) iq.send() def send_example_iq_tag_from_element_tree(self, to, et): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=3) iq['example_tag'].setup_from_lxml(et) iq.send() def send_example_iq_to_get_error(self, to): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=4) - iq['example_tag'].set_boolean(True) + iq['example_tag'].set_boolean(True) # Kiedy, aby otrzymać odpowiedż z błędem, potrzebny jest example_tag bez wartości bool. iq.send() def send_example_iq_tag_from_string(self, to, string): + #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=5) iq['example_tag'].setup_from_string(string) @@ -1207,7 +1225,7 @@ Poniżej przykład kodu podyktowanego tymi zasadami: args.password = getpass("Password: ") xmpp = Sender(args.jid, args.password, args.to, args.path) - xmpp.register_plugin('OurPlugin', module=example_plugin) + xmpp.register_plugin('OurPlugin', module=example_plugin) # OurPlugin jest nazwą klasy z example_plugin. xmpp.connect() try: @@ -1217,16 +1235,15 @@ Poniżej przykład kodu podyktowanego tymi zasadami: xmpp.disconnect() except: pass - Tagi i atrybuty zagnieżdżone wewnątrz głównego elementu -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ +-------------------------------------- -Aby stworzyć zagnieżdżony tag, wewnątrz głównego tagu, rozważmy atrybut `'self.xml'` jako Element z ET (ElementTree). +Aby stworzyć zagnieżdżony tag, wewnątrz głównego tagu, rozważmy atrybut `'self.xml'` jako Element z ET (ElementTree). W takim wypadku, aby stworzyć zagnieżdżony element można użyć funkcji 'append'. Można powtórzyć poprzednie działania inicjalizując nowy element jak główny (ExampleTag). Jednak jeśli nie potrzebujemy dodatkowych metod, czy walidacji, a jest to wynik dla innego procesu który i tak będzie parsował xml, wtedy możemy zagnieździć zwyczajny Element z ElementTree za pomocą metody `'append'`. W przypadku przetwarzania typy napisowego, można to zrobić nawet dzięki parsowaniu napisu na Element - kolejne zagnieżdżenia już będą w dodanym Elemencie do głównego. By nie powtarzać metody setup, poniżej przedstawione jest ręczne dodanie zagnieżdżonego taga konstruując ET.Element samodzielnie. - + .. code-block:: python #File: $WORKDIR/example/example_plugin.py @@ -1238,13 +1255,15 @@ Można powtórzyć poprzednie działania inicjalizując nowy element jak główn #(...) def add_inside_tag(self, tag, attributes, text=""): - #Gdy chcemy dodać tagi wewnętrzne do naszego taga, to jest prosty przykład jak to zrobić: + #Można rozszerzyć tag o tagi wewnętrzne do tagu, na przykład tak: itemXML = ET.Element("{{{0:s}}}{1:s}".format(self.namespace, tag)) #~ Inicjalizujemy Element z naszym wewnętrznym tagiem, na przykład: itemXML.attrib.update(attributes) #~ Przypisujemy zdefiniowane atrybuty, na przykład: itemXML.text = text #~ Dodajemy text wewnątrz tego tagu: our_text self.xml.append(itemXML) #~ I tak skonstruowany Element po prostu dodajemy do elementu z naszym tagiem `example_tag`. -Kompletny kod z tutorialu +Można też zrobić to samo używając słownika i nazw jako kluczy zagnieżdżonych elementów. W takim przypadku, pola funkcji powinny zostać przeniesione do ET. + +Kompletny kod tutorialu ------------------------- W poniższym kodzie zostały pozostawione oryginalne komentarze w języku angielskim. @@ -1424,7 +1443,7 @@ W poniższym kodzie zostały pozostawione oryginalne komentarze w języku angiel itemXML = ET.Element("{{{0:s}}}{1:s}".format(self.namespace, tag)) #~ Initialize ET with our tag, for example: itemXML.attrib.update(attributes) #~ There we add some fields inside tag, for example: itemXML.text = text #~ Fill field inside tag, for example: our_text - self.xml.append(itemXML) #~ Add that all what we set, as inner tag inside `example_tag` tag. + self.xml.append(itemXML) #~ Add that all what we set, as inner tag inside `example_tag` tag. ~ @@ -1765,6 +1784,7 @@ W poniższym kodzie zostały pozostawione oryginalne komentarze w języku angiel .. code-block:: python #File: $WORKDIR/test_example_tag.xml + .. code-block:: xml Info_inside_tag diff --git a/docs/howto/make_plugin_extension_for_message_and_iq.rst b/docs/howto/make_plugin_extension_for_message_and_iq.rst index 9c5dae2a..3db7eb03 100644 --- a/docs/howto/make_plugin_extension_for_message_and_iq.rst +++ b/docs/howto/make_plugin_extension_for_message_and_iq.rst @@ -1,5 +1,5 @@ How to make a slixmpp plugins for Messages and IQ extensions -======================================================================= +==================================================================== Introduction and requirements ----------------- @@ -49,7 +49,7 @@ Example output: If some of the libraries throw `'ImportError'` or `'no module named ...'` error, install them with: -On ubuntu linux: +On Ubuntu linux: .. code-block:: bash @@ -395,7 +395,7 @@ To receive this message, the responder should have a proper handler to the signa #<<<<<<<<<<<< Expanding the Message with a new tag -++++++++++++++++++++++++++++ +------------------------- To expand the Message object with a tag, the plugin should be registered as the extension for the Message object: @@ -405,12 +405,12 @@ To expand the Message object with a tag, the plugin should be registered as the class OurPlugin(BasePlugin): def plugin_init(self): - self.description = "OurPluginExtension" ##~ String data to read by humans and to find the plugin by another plugin. - self.xep = "ope" ##~ String data to read by humans and to find the plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. + self.description = "OurPluginExtension" ##~ String data readable by humans and to find plugin by another plugin. + self.xep = "ope" ##~ String data readable by humans and to find plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. namespace = ExampleTag.namespace #>>>>>>>>>>>> - register_stanza_plugin(Message, ExampleTag) ##~ Register the tags extension for Message object, otherwise message['example_tag'] will be string field instead container and whould not be able to manage fields and create sub elements. + register_stanza_plugin(Message, ExampleTag) ##~ Register the tag extension for Message object, otherwise message['example_tag'] will be string field instead container and managing fields and create sub elements would be impossible. #<<<<<<<<<<<< class ExampleTag(ElementBase): @@ -463,9 +463,9 @@ Now, with the registered object, the message can be extended. After running, the logging should print the Message with tag `'example_tag'` stored inside , string `'Work'` and given namespace. Giving the extended message the separate signal -+++++++++++++++++++++++++++++++++++++++++++++++++++ +------------------------- -If the separate event is not defined, then both normal and extended message will be cached by signal `'message'`. In order to have the special event, the handler for the namespace ang tag should be created. Then, make a unique name combination, which allows the handler can catch only the wanted messages (or Iq object). +If the separate event is not defined, then both normal and extended message will be cached by signal `'message'`. In order to have the special event, the handler for the namespace and tag should be created. Then, make a unique name combination, which allows the handler can catch only the wanted messages (or Iq object). .. code-block:: python @@ -473,16 +473,16 @@ If the separate event is not defined, then both normal and extended message will class OurPlugin(BasePlugin): def plugin_init(self): - self.description = "OurPluginExtension" ##~ String data to read by humans and to find the plugin by another plugin. - self.xep = "ope" ##~ String data to read by humans and to find the plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. + self.description = "OurPluginExtension" ##~ String data readable by humans and to find the plugin by another plugin. + self.xep = "ope" ##~ String data readable by humans and to find the plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. namespace = ExampleTag.namespace self.xmpp.register_handler( Callback('ExampleMessage Event:example_tag',##~ Name of this Callback StanzaPath(f'message/{{{namespace}}}example_tag'), ##~ Handles only the Message with good example_tag and namespace. - self.__handle_message)) ##~ Method which catches the proper Message, should raise event for the client. - register_stanza_plugin(Message, ExampleTag) ##~ Register the tags extension for Message object, otherwise message['example_tag'] will be string field instead container and whould not be able to manage fields and create sub elements. + self.__handle_message)) ##~ Method catches the proper Message, should raise event for the client. + register_stanza_plugin(Message, ExampleTag) ##~ Register the tags extension for Message object, otherwise message['example_tag'] will be string field instead container and managing the fields and create sub elements would not be possible. def __handle_message(self, msg): # Here something can be done with received message before it reaches the client. @@ -541,7 +541,7 @@ Now, remember the line: `'self.xmpp.event('example_tag_message', msg)'`. The nam #>>>>>>>>>>>> self.add_event_handler("example_tag_message", self.example_tag_message) #Registration of the handler #<<<<<<<<<<<< - + def start(self, event): # Two, not required methods, but allows another users to see us available, and receive that information. self.send_presence() @@ -549,7 +549,7 @@ Now, remember the line: `'self.xmpp.event('example_tag_message', msg)'`. The nam #>>>>>>>>>>>> def example_tag_message(self, msg): - logging.info(msg) # Message is standalone object, it can be replied, but no error rises if not. + logging.info(msg) # Message is standalone object, it can be replied, but no error is returned if not. #<<<<<<<<<<<< The messages can be replied, but nothing will happen if we don't. @@ -558,8 +558,8 @@ However, when we receive the Iq object, we should always reply. Otherwise, the e Useful methods and misc. ----------------------- -Modifying the example `Message` object to the `Iq` object. -++++++++++++++++++++++++++++++++++++++++ +Modifying the example `Message` object to the `Iq` object +------------------------- To convert the Message into the Iq object, a new handler for the Iq should be registered, in the same maner as in the `,,Extend message with tags''`part. The following example contains several types of Iq different types to catch. It can be used to check the difference between the Iq request and Iq response or to verify the correctness of the objects. All of the Iq messages should be passed to the sender with the same ID parameter, otherwise the sender receives the Iq with the timeout error. @@ -569,8 +569,8 @@ To convert the Message into the Iq object, a new handler for the Iq should be re class OurPlugin(BasePlugin): def plugin_init(self): - self.description = "OurPluginExtension" ##~ String data to read by humans and to find the plugin by another plugin. - self.xep = "ope" ##~ String data to read by humans and to find the plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. + self.description = "OurPluginExtension" ##~ String data readab;e by humans and to find the plugin by another plugin. + self.xep = "ope" ##~ String data readable by humans and to find the plugin by another plugin by adding it into `slixmpp/plugins/__init__.py` to the `__all__` declaration with 'xep_OPE'. namespace = ExampleTag.namespace #>>>>>>>>>>>> @@ -602,21 +602,21 @@ To convert the Message into the Iq object, a new handler for the Iq should be re # All iq types are: get, set, error, result def __handle_get_iq(self, iq): # Do something with received iq - self.xmpp.event('example_tag_get_iq', iq) ##~ Call the event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_get_iq', iq) ##~ Calls the event which can be handled by clients. def __handle_result_iq(self, iq): # Do something with received iq - self.xmpp.event('example_tag_result_iq', iq) ##~ Call the event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_result_iq', iq) ##~ Calls the event which can be handled by clients. def __handle_error_iq(self, iq): # Do something with received iq - self.xmpp.event('example_tag_error_iq', iq) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_error_iq', iq) ##~ Calls the event which can be handled by clients. def __handle_message(self, msg): # Do something with received message - self.xmpp.event('example_tag_message', msg) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_message', msg) ##~ Calls the event which can be handled by clients. -The events called by the example handlers can be caught like in the`'example_tag_message'` example. +The events called by the example handlers can be caught like in the`'example_tag_message'` part. .. code-block:: python @@ -687,7 +687,7 @@ By default, the parameter `'clear'` in the `'Iq.reply'` is set to True. In that #<<<<<<<<<<<< Different ways to access the elements -+++++++++++++++++++++++ +------------------------- There are several ways to access the elements inside the Message or Iq stanza. The first one: the client can access them like a dictionary: @@ -765,7 +765,7 @@ When the proper setters and getters are used, it is easy to check whether some a iq.send() Message setup from the XML files, strings and other objects -+++++++++++++++++++++++++++++++++++++++++++++++++++++++ +------------------------- There are many ways to set up a xml from a string, xml-containing file or lxml (ElementTree) file. One of them is parsing the strings to lxml object, passing the attributes and other information, which may look like this: @@ -781,9 +781,9 @@ There are many ways to set up a xml from a string, xml-containing file or lxml ( name = "example_tag" ##~ The name of the root XML element of that extension. namespace = "https://example.net/our_extension" ##~ The namespace our stanza object lives in, like . You should change it for your own namespace - plugin_attrib = "example_tag" ##~ The name to access this type of stanza. In particular, given a registration stanza, the Registration object can be found using: stanza_object['example_tag'] now `'example_tag'` is name of ours ElementBase extension. And this should be that same as name. + plugin_attrib = "example_tag" ##~ The name to access this type of stanza. In particular, given a registration stanza, the Registration object can be found using: stanza_object['example_tag'] now `'example_tag'` is name of ElementBase extension. And this should be that same as name. - interfaces = {"boolean", "some_string"} ##~ A list of dictionary-like keys that can be used with the stanza object. For example `stanza_object['example_tag']` gives us {"another": "some", "data": "some"}, whenever `'example_tag'` is name of ours ElementBase extension. + interfaces = {"boolean", "some_string"} ##~ A list of dictionary-like keys that can be used with the stanza object. For example `stanza_object['example_tag']` gives us {"another": "some", "data": "some"}, whenever `'example_tag'` is name of ElementBase extension. #>>>>>>>>>>>> def setup_from_string(self, string): @@ -855,7 +855,7 @@ To test this, we need an example file with xml, example xml string and example l self.disconnect_counter -= 1 logging.info(str(iq)) if not self.disconnect_counter: - self.disconnect() # Example disconnect after receiving the maximum number of recponses. + self.disconnect() # Example disconnect after receiving the maximum number of responses. def send_example_iq_tag_from_file(self, to, path): #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) @@ -882,7 +882,7 @@ To test this, we need an example file with xml, example xml string and example l If the Responder returns the proper `'Iq'` and the Sender disconnects after three answers, then everything works okay. Dev friendly methods for plugin usage -+++++++++++++++++++++++++++++++++++++ +-------------------------------------- Any plugin should have some sort of object-like methods, that was setup for elements: reading the data, getters, setters and signals, to make them easy to use. During handling, the correctness of the data should be checked and the eventual errors returned back to the sender. In order to avoid the situation where the answer message is never send, the sender gets the timeout error. @@ -915,17 +915,17 @@ The following code presents exactly this: namespace = ExampleTag.namespace self.xmpp.register_handler( Callback('ExampleGet Event:example_tag', ##~ Name of this Callback - StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), ##~ Handle only Iq with type get and example_tag + StanzaPath(f"iq@type=get/{{{namespace}}}example_tag"), ##~ Handle only Iq with type 'get' and example_tag self.__handle_get_iq)) ##~ Method which catch proper Iq, should raise proper event for client. self.xmpp.register_handler( Callback('ExampleResult Event:example_tag', ##~ Name of this Callback - StanzaPath(f"iq@type=result/{{{namespace}}}example_tag"), ##~ Handle only Iq with type result and example_tag + StanzaPath(f"iq@type=result/{{{namespace}}}example_tag"), ##~ Handle only Iq with type 'result' and example_tag self.__handle_result_iq)) ##~ Method which catch proper Iq, should raise proper event for client. self.xmpp.register_handler( Callback('ExampleError Event:example_tag', ##~ Name of this Callback - StanzaPath(f"iq@type=error/{{{namespace}}}example_tag"), ##~ Handle only Iq with type error and example_tag + StanzaPath(f"iq@type=error/{{{namespace}}}example_tag"), ##~ Handle only Iq with type 'error' and example_tag self.__handle_error_iq)) ##~ Method which catch proper Iq, should raise proper event for client. self.xmpp.register_handler( @@ -935,7 +935,7 @@ The following code presents exactly this: register_stanza_plugin(Iq, ExampleTag) ##~ Register tags extension for Iq object, otherwise iq['example_tag'] will be string field instead of container, where we can manage our fields and create sub elements. register_stanza_plugin(Message, ExampleTag) ##~ Register tags extension for Message object, otherwise message['example_tag'] will be string field instead of container, where we can manage our fields and create sub elements. - + # All iq types are: get, set, error, result def __handle_get_iq(self, iq): if iq.get_some_string is None: @@ -945,27 +945,27 @@ The following code presents exactly this: error["error"]["text"] = "Without some_string value returns error." error.send() # Do something with received iq - self.xmpp.event('example_tag_get_iq', iq) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_get_iq', iq) ##~ Call event which can be handled by clients to send or something else. def __handle_result_iq(self, iq): # Do something with received iq - self.xmpp.event('example_tag_result_iq', iq) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_result_iq', iq) ##~ Call event which can be handled by clients to send or something else. def __handle_error_iq(self, iq): # Do something with received iq - self.xmpp.event('example_tag_error_iq', iq) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_error_iq', iq) ##~ Call event which can be handled by clients to send or something else. def __handle_message(self, msg): # Do something with received message - self.xmpp.event('example_tag_message', msg) ##~ Call event which can be handled by clients to send or something other. + self.xmpp.event('example_tag_message', msg) ##~ Call event which can be handled by clients to send or something else. class ExampleTag(ElementBase): name = "example_tag" ##~ The name of the root XML element of that extension. namespace = "https://example.net/our_extension" ##~ The namespace stanza object lives in, like . You should change it for your own namespace. - plugin_attrib = "example_tag" ##~ The name to access this type of stanza. In particular, given a registration stanza, the Registration object can be found using: stanza_object['example_tag'] now `'example_tag'` is name of ours ElementBase extension. And this should be that same as name. + plugin_attrib = "example_tag" ##~ The name to access this type of stanza. In particular, given a registration stanza, the Registration object can be found using: stanza_object['example_tag'] now `'example_tag'` is name of ElementBase extension. And this should be that same as name. - interfaces = {"boolean", "some_string"} ##~ A list of dictionary-like keys that can be used with the stanza object. For example `stanza_object['example_tag']` gives us {"another": "some", "data": "some"}, whenever `'example_tag'` is name of ours ElementBase extension. + interfaces = {"boolean", "some_string"} ##~ A list of dictionary-like keys that can be used with the stanza object. For example `stanza_object['example_tag']` gives us {"another": "some", "data": "some"}, whenever `'example_tag'` is name of ElementBase extension. def setup_from_string(self, string): """Initialize tag element from string""" @@ -984,7 +984,7 @@ The following code presents exactly this: self.xml.tail = lxml.tail for inner_tag in lxml: self.xml.append(inner_tag) - + def setup_from_dict(self, data): #There keys from dict should be also validated self.xml.attrib.update(data) @@ -1008,14 +1008,14 @@ The following code presents exactly this: self.xml.text = text def fill_interfaces(self, boolean, some_string): - #Some validation if it is necessary + #Some validation, if necessary self.set_boolean(boolean) self.set_some_string(some_string) .. code-block:: python #File: $WORKDIR/example/responder.py - + import logging from argparse import ArgumentParser from getpass import getpass @@ -1088,7 +1088,7 @@ The following code presents exactly this: .. code-block:: python #File: $WORKDIR/example/sender.py - + import logging from argparse import ArgumentParser from getpass import getpass @@ -1115,42 +1115,42 @@ The following code presents exactly this: self.send_presence() self.get_roster() - self.disconnect_counter = 5 # This is only for disconnect when we receive all replies for sended Iq + self.disconnect_counter = 5 # # Disconnect after receiving the maximum number of responses. self.send_example_iq(self.to) - # Info_inside_tag + # Info_inside_tag self.send_example_message(self.to) - # Info_inside_tag_message + # Info_inside_tag_message self.send_example_iq_tag_from_file(self.to, self.path) - # Info_inside_tag + # Info_inside_tag - string = 'Info_inside_tag' + string = 'Info_inside_tag' et = ET.fromstring(string) self.send_example_iq_tag_from_element_tree(self.to, et) - # Info_inside_tag + # Info_inside_tag self.send_example_iq_to_get_error(self.to) - # - # OUR ERROR Without boolean value returns error. - # OFFLINE ERROR User session not found + # + # OUR ERROR Without boolean value returns error. + # OFFLINE ERROR User session not found self.send_example_iq_tag_from_string(self.to, string) - # Info_inside_tag + # Info_inside_tag def example_tag_result_iq(self, iq): self.disconnect_counter -= 1 logging.info(str(iq)) if not self.disconnect_counter: - self.disconnect() # Example disconnect after first received iq stanza extended by example_tag with result type. + self.disconnect() # Example disconnect after receiving the maximum number of responses. def example_tag_error_iq(self, iq): self.disconnect_counter -= 1 logging.info(str(iq)) if not self.disconnect_counter: - self.disconnect() # Example disconnect after first received iq stanza extended by example_tag with result type. + self.disconnect() # Example disconnect after receiving the maximum number of responses. def send_example_iq(self, to): #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) @@ -1185,7 +1185,7 @@ The following code presents exactly this: def send_example_iq_to_get_error(self, to): #~ make_iq(id=0, ifrom=None, ito=None, itype=None, iquery=None) iq = self.make_iq(ito=to, itype="get", id=4) - iq['example_tag'].set_boolean(True) # For example, our condition to receive error respond is example_tag without boolean value. + iq['example_tag'].set_boolean(True) # For example, our condition to receive error respond is example_tag the without boolean value. iq.send() def send_example_iq_tag_from_string(self, to, string): @@ -1225,7 +1225,7 @@ The following code presents exactly this: args.password = getpass("Password: ") xmpp = Sender(args.jid, args.password, args.to, args.path) - xmpp.register_plugin('OurPlugin', module=example_plugin) # OurPlugin is a class name from example_plugin + xmpp.register_plugin('OurPlugin', module=example_plugin) # OurPlugin is a class name from example_plugin. xmpp.connect() try: @@ -1237,14 +1237,16 @@ The following code presents exactly this: pass Tags and strings nested inside our tag -++++++++++++++++++++++++++++++++++++++ +-------------------------------------- -To make the nested element inside our IQ tag, we need to consider `self.xml` field as an Element from ET (ElementTree). So, adding the nested elements is just appending the Element. +To create the nested element inside IQ tag, `self.xml` field can be considered as an Element from ET (ElementTree). Therefore adding the nested Elements is appending the Element. + +As shown in the previous examples, it is possible to create a new element as main (ExampleTag). However, when the additional methods or validation is not needed and the result will be parsed to xml anyway, it may be better to nest the Element from ElementTree with method 'append'. In order to not use the 'setup' method again, the code below shows way of the manual addition of the nested tag and creation of ET Element. .. code-block:: python #File: $WORKDIR/example/example_plugin.py - + #(...) class ExampleTag(ElementBase): -- cgit v1.2.3