Mercurial > libervia-backend
comparison sat/plugins/plugin_misc_text_commands.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | dbfedde9bc61 |
children | 9d0df638c8b4 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SàT plugin for managing text commands | 4 # SàT plugin for managing text commands |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
55 # FIXME: doc strings for commands have to be translatable | 55 # FIXME: doc strings for commands have to be translatable |
56 # plugins need a dynamic translation system (translation | 56 # plugins need a dynamic translation system (translation |
57 # should be downloadable independently) | 57 # should be downloadable independently) |
58 | 58 |
59 HELP_SUGGESTION = _( | 59 HELP_SUGGESTION = _( |
60 u"Type '/help' to get a list of the available commands. If you didn't want to " | 60 "Type '/help' to get a list of the available commands. If you didn't want to " |
61 u"use a command, please start your message with '//' to escape the slash." | 61 "use a command, please start your message with '//' to escape the slash." |
62 ) | 62 ) |
63 | 63 |
64 def __init__(self, host): | 64 def __init__(self, host): |
65 log.info(_("Text commands initialization")) | 65 log.info(_("Text commands initialization")) |
66 self.host = host | 66 self.host = host |
85 - "doc_arg_[name]": the doc of [name] argument | 85 - "doc_arg_[name]": the doc of [name] argument |
86 """ | 86 """ |
87 data = OrderedDict([("doc_short_help", ""), ("type", "all"), ("args", "")]) | 87 data = OrderedDict([("doc_short_help", ""), ("type", "all"), ("args", "")]) |
88 docstring = cmd.__doc__ | 88 docstring = cmd.__doc__ |
89 if docstring is None: | 89 if docstring is None: |
90 log.warning(u"No docstring found for command {}".format(cmd_name)) | 90 log.warning("No docstring found for command {}".format(cmd_name)) |
91 docstring = "" | 91 docstring = "" |
92 | 92 |
93 doc_data = docstring.split("\n") | 93 doc_data = docstring.split("\n") |
94 data["doc_short_help"] = doc_data[0] | 94 data["doc_short_help"] = doc_data[0] |
95 | 95 |
135 | 135 |
136 # args | 136 # args |
137 data["args"] = stripped[colon_idx + 1 :].strip() | 137 data["args"] = stripped[colon_idx + 1 :].strip() |
138 except InvalidCommandSyntax as e: | 138 except InvalidCommandSyntax as e: |
139 log.warning( | 139 log.warning( |
140 u"Invalid command syntax for command {command}: {message}".format( | 140 "Invalid command syntax for command {command}: {message}".format( |
141 command=cmd_name, message=e.message | 141 command=cmd_name, message=e.message |
142 ) | 142 ) |
143 ) | 143 ) |
144 | 144 |
145 return data | 145 return data |
151 """ | 151 """ |
152 for attr in dir(instance): | 152 for attr in dir(instance): |
153 if attr.startswith("cmd_"): | 153 if attr.startswith("cmd_"): |
154 cmd = getattr(instance, attr) | 154 cmd = getattr(instance, attr) |
155 if not callable(cmd): | 155 if not callable(cmd): |
156 log.warning(_(u"Skipping not callable [%s] attribute") % attr) | 156 log.warning(_("Skipping not callable [%s] attribute") % attr) |
157 continue | 157 continue |
158 cmd_name = attr[4:] | 158 cmd_name = attr[4:] |
159 if not cmd_name: | 159 if not cmd_name: |
160 log.warning(_("Skipping cmd_ method")) | 160 log.warning(_("Skipping cmd_ method")) |
161 if cmd_name in self._commands: | 161 if cmd_name in self._commands: |
163 while (cmd_name + str(suff)) in self._commands: | 163 while (cmd_name + str(suff)) in self._commands: |
164 suff += 1 | 164 suff += 1 |
165 new_name = cmd_name + str(suff) | 165 new_name = cmd_name + str(suff) |
166 log.warning( | 166 log.warning( |
167 _( | 167 _( |
168 u"Conflict for command [{old_name}], renaming it to [{new_name}]" | 168 "Conflict for command [{old_name}], renaming it to [{new_name}]" |
169 ).format(old_name=cmd_name, new_name=new_name) | 169 ).format(old_name=cmd_name, new_name=new_name) |
170 ) | 170 ) |
171 cmd_name = new_name | 171 cmd_name = new_name |
172 self._commands[cmd_name] = cmd_data = OrderedDict( | 172 self._commands[cmd_name] = cmd_data = OrderedDict( |
173 {"callback": cmd} | 173 {"callback": cmd} |
214 msg = mess_data["message"][""] | 214 msg = mess_data["message"][""] |
215 msg_lang = "" | 215 msg_lang = "" |
216 except KeyError: | 216 except KeyError: |
217 try: | 217 try: |
218 # we have not default message, we try to take the first found | 218 # we have not default message, we try to take the first found |
219 msg_lang, msg = mess_data["message"].iteritems().next() | 219 msg_lang, msg = next(iter(mess_data["message"].items())) |
220 except StopIteration: | 220 except StopIteration: |
221 log.debug(u"No message found, skipping text commands") | 221 log.debug("No message found, skipping text commands") |
222 return mess_data | 222 return mess_data |
223 | 223 |
224 try: | 224 try: |
225 if msg[:2] == "//": | 225 if msg[:2] == "//": |
226 # we have a double '/', it's the escape sequence | 226 # we have a double '/', it's the escape sequence |
242 else, abord message sending | 242 else, abord message sending |
243 """ | 243 """ |
244 if ret: | 244 if ret: |
245 return mess_data | 245 return mess_data |
246 else: | 246 else: |
247 log.debug(u"text command detected ({})".format(command)) | 247 log.debug("text command detected ({})".format(command)) |
248 raise failure.Failure(exceptions.CancelError()) | 248 raise failure.Failure(exceptions.CancelError()) |
249 | 249 |
250 def genericErrback(failure): | 250 def genericErrback(failure): |
251 try: | 251 try: |
252 msg = u"with condition {}".format(failure.value.condition) | 252 msg = "with condition {}".format(failure.value.condition) |
253 except AttributeError: | 253 except AttributeError: |
254 msg = u"with error {}".format(failure.value) | 254 msg = "with error {}".format(failure.value) |
255 self.feedBack(client, u"Command failed {}".format(msg), mess_data) | 255 self.feedBack(client, "Command failed {}".format(msg), mess_data) |
256 return False | 256 return False |
257 | 257 |
258 mess_data["unparsed"] = msg[ | 258 mess_data["unparsed"] = msg[ |
259 1 + len(command) : | 259 1 + len(command) : |
260 ] # part not yet parsed of the message | 260 ] # part not yet parsed of the message |
278 ) | 278 ) |
279 feedback = _("/{command} command only applies in {context}.").format( | 279 feedback = _("/{command} command only applies in {context}.").format( |
280 command=command, context=context_txt | 280 command=command, context=context_txt |
281 ) | 281 ) |
282 self.feedBack( | 282 self.feedBack( |
283 client, u"{} {}".format(feedback, self.HELP_SUGGESTION), mess_data | 283 client, "{} {}".format(feedback, self.HELP_SUGGESTION), mess_data |
284 ) | 284 ) |
285 log.debug("text command invalid message") | 285 log.debug("text command invalid message") |
286 raise failure.Failure(exceptions.CancelError()) | 286 raise failure.Failure(exceptions.CancelError()) |
287 else: | 287 else: |
288 d = defer.maybeDeferred(cmd_data["callback"], client, mess_data) | 288 d = defer.maybeDeferred(cmd_data["callback"], client, mess_data) |
316 nb_arobas = arg.count("@") | 316 nb_arobas = arg.count("@") |
317 if nb_arobas == 1: | 317 if nb_arobas == 1: |
318 if arg[-1] != "@": | 318 if arg[-1] != "@": |
319 return jid.JID(arg) | 319 return jid.JID(arg) |
320 return jid.JID(arg + service_jid) | 320 return jid.JID(arg + service_jid) |
321 return jid.JID(u"%s@%s" % (arg, service_jid)) | 321 return jid.JID("%s@%s" % (arg, service_jid)) |
322 | 322 |
323 def feedBack(self, client, message, mess_data, info_type=FEEDBACK_INFO_TYPE): | 323 def feedBack(self, client, message, mess_data, info_type=FEEDBACK_INFO_TYPE): |
324 """Give a message back to the user""" | 324 """Give a message back to the user""" |
325 if mess_data["type"] == "groupchat": | 325 if mess_data["type"] == "groupchat": |
326 to_ = mess_data["to"].userhostJID() | 326 to_ = mess_data["to"].userhostJID() |
348 | 348 |
349 if mess_data["type"] == "groupchat": | 349 if mess_data["type"] == "groupchat": |
350 room = mess_data["to"].userhostJID() | 350 room = mess_data["to"].userhostJID() |
351 try: | 351 try: |
352 if self.host.plugins["XEP-0045"].isNickInRoom(client, room, entity): | 352 if self.host.plugins["XEP-0045"].isNickInRoom(client, room, entity): |
353 entity = u"%s/%s" % (room, entity) | 353 entity = "%s/%s" % (room, entity) |
354 except KeyError: | 354 except KeyError: |
355 log.warning("plugin XEP-0045 is not present") | 355 log.warning("plugin XEP-0045 is not present") |
356 | 356 |
357 if not entity: | 357 if not entity: |
358 target_jid = mess_data["to"] | 358 target_jid = mess_data["to"] |
366 return False | 366 return False |
367 | 367 |
368 if not target_jid.resource: | 368 if not target_jid.resource: |
369 target_jid.resource = self.host.memory.getMainResource(client, target_jid) | 369 target_jid.resource = self.host.memory.getMainResource(client, target_jid) |
370 | 370 |
371 whois_msg = [_(u"whois for %(jid)s") % {"jid": target_jid}] | 371 whois_msg = [_("whois for %(jid)s") % {"jid": target_jid}] |
372 | 372 |
373 d = defer.succeed(None) | 373 d = defer.succeed(None) |
374 for ignore, callback in self._whois: | 374 for ignore, callback in self._whois: |
375 d.addCallback( | 375 d.addCallback( |
376 lambda ignore: callback(client, whois_msg, mess_data, target_jid) | 376 lambda ignore: callback(client, whois_msg, mess_data, target_jid) |
377 ) | 377 ) |
378 | 378 |
379 def feedBack(ignore): | 379 def feedBack(ignore): |
380 self.feedBack(client, u"\n".join(whois_msg), mess_data) | 380 self.feedBack(client, "\n".join(whois_msg), mess_data) |
381 return False | 381 return False |
382 | 382 |
383 d.addCallback(feedBack) | 383 d.addCallback(feedBack) |
384 return d | 384 return d |
385 | 385 |
388 | 388 |
389 @param cmd_data: command data | 389 @param cmd_data: command data |
390 @return (list[unicode]): help strings | 390 @return (list[unicode]): help strings |
391 """ | 391 """ |
392 strings = [] | 392 strings = [] |
393 for doc_name, doc_help in cmd_data.iteritems(): | 393 for doc_name, doc_help in cmd_data.items(): |
394 if doc_name.startswith("doc_arg_"): | 394 if doc_name.startswith("doc_arg_"): |
395 arg_name = doc_name[8:] | 395 arg_name = doc_name[8:] |
396 strings.append( | 396 strings.append( |
397 u"- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help)) | 397 "- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help)) |
398 ) | 398 ) |
399 | 399 |
400 return strings | 400 return strings |
401 | 401 |
402 def cmd_me(self, client, mess_data): | 402 def cmd_me(self, client, mess_data): |
422 cmd_name = mess_data["unparsed"].strip() | 422 cmd_name = mess_data["unparsed"].strip() |
423 if cmd_name and cmd_name[0] == "/": | 423 if cmd_name and cmd_name[0] == "/": |
424 cmd_name = cmd_name[1:] | 424 cmd_name = cmd_name[1:] |
425 if cmd_name and cmd_name not in self._commands: | 425 if cmd_name and cmd_name not in self._commands: |
426 self.feedBack( | 426 self.feedBack( |
427 client, _(u"Invalid command name [{}]\n".format(cmd_name)), mess_data | 427 client, _("Invalid command name [{}]\n".format(cmd_name)), mess_data |
428 ) | 428 ) |
429 cmd_name = "" | 429 cmd_name = "" |
430 if not cmd_name: | 430 if not cmd_name: |
431 # we show the global help | 431 # we show the global help |
432 longuest = max([len(command) for command in self._commands]) | 432 longuest = max([len(command) for command in self._commands]) |
443 spaces=spaces, | 443 spaces=spaces, |
444 short_help=cmd_data["doc_short_help"], | 444 short_help=cmd_data["doc_short_help"], |
445 ) | 445 ) |
446 ) | 446 ) |
447 | 447 |
448 help_mess = _(u"Text commands available:\n%s") % (u"\n".join(help_cmds),) | 448 help_mess = _("Text commands available:\n%s") % ("\n".join(help_cmds),) |
449 else: | 449 else: |
450 # we show detailled help for a command | 450 # we show detailled help for a command |
451 cmd_data = self._commands[cmd_name] | 451 cmd_data = self._commands[cmd_name] |
452 syntax = cmd_data["args"] | 452 syntax = cmd_data["args"] |
453 help_mess = _(u"/{name}: {short_help}\n{syntax}{args_help}").format( | 453 help_mess = _("/{name}: {short_help}\n{syntax}{args_help}").format( |
454 name=cmd_name, | 454 name=cmd_name, |
455 short_help=cmd_data["doc_short_help"], | 455 short_help=cmd_data["doc_short_help"], |
456 syntax=_(" " * 4 + "syntax: {}\n").format(syntax) if syntax else "", | 456 syntax=_(" " * 4 + "syntax: {}\n").format(syntax) if syntax else "", |
457 args_help=u"\n".join( | 457 args_help="\n".join( |
458 [u" " * 8 + "{}".format(line) for line in self._getArgsHelp(cmd_data)] | 458 [" " * 8 + "{}".format(line) for line in self._getArgsHelp(cmd_data)] |
459 ), | 459 ), |
460 ) | 460 ) |
461 | 461 |
462 self.feedBack(client, help_mess, mess_data) | 462 self.feedBack(client, help_mess, mess_data) |