comparison sat/plugins/plugin_misc_text_commands.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children d715d912afac
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core import exceptions 22 from sat.core import exceptions
23 from twisted.words.protocols.jabber import jid 23 from twisted.words.protocols.jabber import jid
24 from twisted.internet import defer 24 from twisted.internet import defer
25 from sat.core.log import getLogger 25 from sat.core.log import getLogger
26
26 log = getLogger(__name__) 27 log = getLogger(__name__)
27 from twisted.python import failure 28 from twisted.python import failure
28 from collections import OrderedDict 29 from collections import OrderedDict
29 30
30 PLUGIN_INFO = { 31 PLUGIN_INFO = {
33 C.PI_TYPE: "Misc", 34 C.PI_TYPE: "Misc",
34 C.PI_PROTOCOLS: ["XEP-0245"], 35 C.PI_PROTOCOLS: ["XEP-0245"],
35 C.PI_DEPENDENCIES: [], 36 C.PI_DEPENDENCIES: [],
36 C.PI_MAIN: "TextCommands", 37 C.PI_MAIN: "TextCommands",
37 C.PI_HANDLER: "no", 38 C.PI_HANDLER: "no",
38 C.PI_DESCRIPTION: _("""IRC like text commands""") 39 C.PI_DESCRIPTION: _("""IRC like text commands"""),
39 } 40 }
40 41
41 42
42 class InvalidCommandSyntax(Exception): 43 class InvalidCommandSyntax(Exception):
43 """Throwed while parsing @command in docstring if syntax is invalid""" 44 """Throwed while parsing @command in docstring if syntax is invalid"""
45
44 pass 46 pass
45 47
46 48
47 CMD_KEY = "@command" 49 CMD_KEY = "@command"
48 CMD_TYPES = ('group', 'one2one', 'all') 50 CMD_TYPES = ("group", "one2one", "all")
49 FEEDBACK_INFO_TYPE = "TEXT_CMD" 51 FEEDBACK_INFO_TYPE = "TEXT_CMD"
50 52
51 53
52 class TextCommands(object): 54 class TextCommands(object):
53 #FIXME: doc strings for commands have to be translatable 55 # FIXME: doc strings for commands have to be translatable
54 # plugins need a dynamic translation system (translation 56 # plugins need a dynamic translation system (translation
55 # should be downloadable independently) 57 # should be downloadable independently)
56 58
57 HELP_SUGGESTION = _("Type '/help' to get a list of the available commands. If you didn't want to use a command, please start your message with '//' to escape the slash.") 59 HELP_SUGGESTION = _(
60 "Type '/help' to get a list of the available commands. If you didn't want to use a command, please start your message with '//' to escape the slash."
61 )
58 62
59 def __init__(self, host): 63 def __init__(self, host):
60 log.info(_("Text commands initialization")) 64 log.info(_("Text commands initialization"))
61 self.host = host 65 self.host = host
62 # this is internal command, so we set high priority 66 # this is internal command, so we set high priority
76 - "doc_short_help" (default: ""): the untranslated short documentation 80 - "doc_short_help" (default: ""): the untranslated short documentation
77 - "type" (default "all"): the command type as specified in documentation 81 - "type" (default "all"): the command type as specified in documentation
78 - "args" (default: ""): the arguments available, using syntax specified in documentation. 82 - "args" (default: ""): the arguments available, using syntax specified in documentation.
79 - "doc_arg_[name]": the doc of [name] argument 83 - "doc_arg_[name]": the doc of [name] argument
80 """ 84 """
81 data = OrderedDict([('doc_short_help', ""), 85 data = OrderedDict([("doc_short_help", ""), ("type", "all"), ("args", "")])
82 ('type', 'all'),
83 ('args', '')])
84 docstring = cmd.__doc__ 86 docstring = cmd.__doc__
85 if docstring is None: 87 if docstring is None:
86 log.warning(u"Not docstring found for command {}".format(cmd_name)) 88 log.warning(u"Not docstring found for command {}".format(cmd_name))
87 docstring = "" 89 docstring = ""
88 90
89 doc_data = docstring.split("\n") 91 doc_data = docstring.split("\n")
90 data["doc_short_help"] = doc_data[0] 92 data["doc_short_help"] = doc_data[0]
91 93
92 try: 94 try:
93 cmd_indent = 0 # >0 when @command is found are we are parsing it 95 cmd_indent = 0 # >0 when @command is found are we are parsing it
94 96
95 for line in doc_data: 97 for line in doc_data:
96 stripped = line.strip() 98 stripped = line.strip()
97 if cmd_indent and line[cmd_indent:cmd_indent+5] == " -": 99 if cmd_indent and line[cmd_indent : cmd_indent + 5] == " -":
98 colon_idx = line.find(":") 100 colon_idx = line.find(":")
99 if colon_idx == -1: 101 if colon_idx == -1:
100 raise InvalidCommandSyntax("No colon found in argument description") 102 raise InvalidCommandSyntax(
101 arg_name = line[cmd_indent+6:colon_idx].strip() 103 "No colon found in argument description"
104 )
105 arg_name = line[cmd_indent + 6 : colon_idx].strip()
102 if not arg_name: 106 if not arg_name:
103 raise InvalidCommandSyntax("No name found in argument description") 107 raise InvalidCommandSyntax(
104 arg_help = line[colon_idx+1:].strip() 108 "No name found in argument description"
109 )
110 arg_help = line[colon_idx + 1 :].strip()
105 data["doc_arg_{}".format(arg_name)] = arg_help 111 data["doc_arg_{}".format(arg_name)] = arg_help
106 elif cmd_indent: 112 elif cmd_indent:
107 # we are parsing command and indent level is not good, it's finished 113 # we are parsing command and indent level is not good, it's finished
108 break 114 break
109 elif stripped.startswith(CMD_KEY): 115 elif stripped.startswith(CMD_KEY):
111 117
112 # type 118 # type
113 colon_idx = stripped.find(":") 119 colon_idx = stripped.find(":")
114 if colon_idx == -1: 120 if colon_idx == -1:
115 raise InvalidCommandSyntax("missing colon") 121 raise InvalidCommandSyntax("missing colon")
116 type_data = stripped[len(CMD_KEY):colon_idx].strip() 122 type_data = stripped[len(CMD_KEY) : colon_idx].strip()
117 if len(type_data) == 0: 123 if len(type_data) == 0:
118 type_data="(all)" 124 type_data = "(all)"
119 elif len(type_data) <= 2 or type_data[0] != '(' or type_data[-1] != ')': 125 elif (
126 len(type_data) <= 2 or type_data[0] != "(" or type_data[-1] != ")"
127 ):
120 raise InvalidCommandSyntax("Bad type data syntax") 128 raise InvalidCommandSyntax("Bad type data syntax")
121 type_ = type_data[1:-1] 129 type_ = type_data[1:-1]
122 if type_ not in CMD_TYPES: 130 if type_ not in CMD_TYPES:
123 raise InvalidCommandSyntax("Unknown type {}".format(type_)) 131 raise InvalidCommandSyntax("Unknown type {}".format(type_))
124 data["type"] = type_ 132 data["type"] = type_
125 133
126 #args 134 # args
127 data["args"] = stripped[colon_idx+1:].strip() 135 data["args"] = stripped[colon_idx + 1 :].strip()
128 except InvalidCommandSyntax as e: 136 except InvalidCommandSyntax as e:
129 log.warning(u"Invalid command syntax for command {command}: {message}".format(command=cmd_name, message=e.message)) 137 log.warning(
138 u"Invalid command syntax for command {command}: {message}".format(
139 command=cmd_name, message=e.message
140 )
141 )
130 142
131 return data 143 return data
132
133 144
134 def registerTextCommands(self, instance): 145 def registerTextCommands(self, instance):
135 """ Add a text command 146 """ Add a text command
136 147
137 @param instance: instance of a class containing text commands 148 @param instance: instance of a class containing text commands
138 """ 149 """
139 for attr in dir(instance): 150 for attr in dir(instance):
140 if attr.startswith('cmd_'): 151 if attr.startswith("cmd_"):
141 cmd = getattr(instance, attr) 152 cmd = getattr(instance, attr)
142 if not callable(cmd): 153 if not callable(cmd):
143 log.warning(_(u"Skipping not callable [%s] attribute") % attr) 154 log.warning(_(u"Skipping not callable [%s] attribute") % attr)
144 continue 155 continue
145 cmd_name = attr[4:] 156 cmd_name = attr[4:]
146 if not cmd_name: 157 if not cmd_name:
147 log.warning(_("Skipping cmd_ method")) 158 log.warning(_("Skipping cmd_ method"))
148 if cmd_name in self._commands: 159 if cmd_name in self._commands:
149 suff=2 160 suff = 2
150 while (cmd_name + str(suff)) in self._commands: 161 while (cmd_name + str(suff)) in self._commands:
151 suff+=1 162 suff += 1
152 new_name = cmd_name + str(suff) 163 new_name = cmd_name + str(suff)
153 log.warning(_(u"Conflict for command [{old_name}], renaming it to [{new_name}]").format(old_name=cmd_name, new_name=new_name)) 164 log.warning(
165 _(
166 u"Conflict for command [{old_name}], renaming it to [{new_name}]"
167 ).format(old_name=cmd_name, new_name=new_name)
168 )
154 cmd_name = new_name 169 cmd_name = new_name
155 self._commands[cmd_name] = cmd_data = OrderedDict({'callback':cmd}) # We use an Ordered dict to keep documenation order 170 self._commands[cmd_name] = cmd_data = OrderedDict(
171 {"callback": cmd}
172 ) # We use an Ordered dict to keep documenation order
156 cmd_data.update(self._parseDocString(cmd, cmd_name)) 173 cmd_data.update(self._parseDocString(cmd, cmd_name))
157 log.info(_("Registered text command [%s]") % cmd_name) 174 log.info(_("Registered text command [%s]") % cmd_name)
158 175
159 def addWhoIsCb(self, callback, priority=0): 176 def addWhoIsCb(self, callback, priority=0):
160 """Add a callback which give information to the /whois command 177 """Add a callback which give information to the /whois command
166 @param priority: priority of the information to show (the highest priority will be displayed first) 183 @param priority: priority of the information to show (the highest priority will be displayed first)
167 """ 184 """
168 self._whois.append((priority, callback)) 185 self._whois.append((priority, callback))
169 self._whois.sort(key=lambda item: item[0], reverse=True) 186 self._whois.sort(key=lambda item: item[0], reverse=True)
170 187
171 def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments): 188 def sendMessageTrigger(
189 self, client, mess_data, pre_xml_treatments, post_xml_treatments
190 ):
172 """Install SendMessage command hook """ 191 """Install SendMessage command hook """
173 pre_xml_treatments.addCallback(self._sendMessageCmdHook, client) 192 pre_xml_treatments.addCallback(self._sendMessageCmdHook, client)
174 return True 193 return True
175 194
176 def _sendMessageCmdHook(self, mess_data, client): 195 def _sendMessageCmdHook(self, mess_data, client):
183 commands can be deferred or not 202 commands can be deferred or not
184 @param mess_data(dict): data comming from sendMessage trigger 203 @param mess_data(dict): data comming from sendMessage trigger
185 @param profile: %(doc_profile)s 204 @param profile: %(doc_profile)s
186 """ 205 """
187 try: 206 try:
188 msg = mess_data["message"][''] 207 msg = mess_data["message"][""]
189 msg_lang = '' 208 msg_lang = ""
190 except KeyError: 209 except KeyError:
191 try: 210 try:
192 # we have not default message, we try to take the first found 211 # we have not default message, we try to take the first found
193 msg_lang, msg = mess_data["message"].iteritems().next() 212 msg_lang, msg = mess_data["message"].iteritems().next()
194 except StopIteration: 213 except StopIteration:
195 log.debug(u"No message found, skipping text commands") 214 log.debug(u"No message found, skipping text commands")
196 return mess_data 215 return mess_data
197 216
198 try: 217 try:
199 if msg[:2] == '//': 218 if msg[:2] == "//":
200 # we have a double '/', it's the escape sequence 219 # we have a double '/', it's the escape sequence
201 mess_data["message"][msg_lang] = msg[1:] 220 mess_data["message"][msg_lang] = msg[1:]
202 return mess_data 221 return mess_data
203 if msg[0] != '/': 222 if msg[0] != "/":
204 return mess_data 223 return mess_data
205 except IndexError: 224 except IndexError:
206 return mess_data 225 return mess_data
207 226
208 # we have a command 227 # we have a command
209 d = None 228 d = None
210 command = msg[1:].partition(' ')[0].lower() 229 command = msg[1:].partition(" ")[0].lower()
211 if command.isalpha(): 230 if command.isalpha():
212 # looks like an actual command, we try to call the corresponding method 231 # looks like an actual command, we try to call the corresponding method
213 def retHandling(ret): 232 def retHandling(ret):
214 """ Handle command return value: 233 """ Handle command return value:
215 if ret is True, normally send message (possibly modified by command) 234 if ret is True, normally send message (possibly modified by command)
227 except AttributeError: 246 except AttributeError:
228 msg = u"with error {}".format(failure.value) 247 msg = u"with error {}".format(failure.value)
229 self.feedBack(client, u"Command failed {}".format(msg), mess_data) 248 self.feedBack(client, u"Command failed {}".format(msg), mess_data)
230 return False 249 return False
231 250
232 mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message 251 mess_data["unparsed"] = msg[
252 1 + len(command) :
253 ] # part not yet parsed of the message
233 try: 254 try:
234 cmd_data = self._commands[command] 255 cmd_data = self._commands[command]
235 except KeyError: 256 except KeyError:
236 self.feedBack(client, _("Unknown command /%s. ") % command + self.HELP_SUGGESTION, mess_data) 257 self.feedBack(
258 client,
259 _("Unknown command /%s. ") % command + self.HELP_SUGGESTION,
260 mess_data,
261 )
237 log.debug("text command help message") 262 log.debug("text command help message")
238 raise failure.Failure(exceptions.CancelError()) 263 raise failure.Failure(exceptions.CancelError())
239 else: 264 else:
240 if not self._contextValid(mess_data, cmd_data): 265 if not self._contextValid(mess_data, cmd_data):
241 # The command is not launched in the right context, we throw a message with help instructions 266 # The command is not launched in the right context, we throw a message with help instructions
242 context_txt = _("group discussions") if cmd_data["type"] == "group" else _("one to one discussions") 267 context_txt = (
243 feedback = _("/{command} command only applies in {context}.").format(command=command, context=context_txt) 268 _("group discussions")
244 self.feedBack(client, u"{} {}".format(feedback, self.HELP_SUGGESTION), mess_data) 269 if cmd_data["type"] == "group"
270 else _("one to one discussions")
271 )
272 feedback = _("/{command} command only applies in {context}.").format(
273 command=command, context=context_txt
274 )
275 self.feedBack(
276 client, u"{} {}".format(feedback, self.HELP_SUGGESTION), mess_data
277 )
245 log.debug("text command invalid message") 278 log.debug("text command invalid message")
246 raise failure.Failure(exceptions.CancelError()) 279 raise failure.Failure(exceptions.CancelError())
247 else: 280 else:
248 d = defer.maybeDeferred(cmd_data["callback"], client, mess_data) 281 d = defer.maybeDeferred(cmd_data["callback"], client, mess_data)
249 d.addErrback(genericErrback) 282 d.addErrback(genericErrback)
250 d.addCallback(retHandling) 283 d.addCallback(retHandling)
251 284
252 return d or mess_data # if a command is detected, we should have a deferred, else we send the message normally 285 return (
286 d or mess_data
287 ) # if a command is detected, we should have a deferred, else we send the message normally
253 288
254 def _contextValid(self, mess_data, cmd_data): 289 def _contextValid(self, mess_data, cmd_data):
255 """Tell if a command can be used in the given context 290 """Tell if a command can be used in the given context
256 291
257 @param mess_data(dict): message data as given in sendMessage trigger 292 @param mess_data(dict): message data as given in sendMessage trigger
258 @param cmd_data(dict): command data as returned by self._parseDocString 293 @param cmd_data(dict): command data as returned by self._parseDocString
259 @return (bool): True if command can be used in this context 294 @return (bool): True if command can be used in this context
260 """ 295 """
261 if ((cmd_data['type'] == "group" and mess_data["type"] != "groupchat") or 296 if (cmd_data["type"] == "group" and mess_data["type"] != "groupchat") or (
262 (cmd_data['type'] == 'one2one' and mess_data["type"] == "groupchat")): 297 cmd_data["type"] == "one2one" and mess_data["type"] == "groupchat"
298 ):
263 return False 299 return False
264 return True 300 return True
265 301
266 def getRoomJID(self, arg, service_jid): 302 def getRoomJID(self, arg, service_jid):
267 """Return a room jid with a shortcut 303 """Return a room jid with a shortcut
268 304
269 @param arg: argument: can be a full room jid (e.g.: sat@chat.jabberfr.org) 305 @param arg: argument: can be a full room jid (e.g.: sat@chat.jabberfr.org)
270 or a shortcut (e.g.: sat or sat@ for sat on current service) 306 or a shortcut (e.g.: sat or sat@ for sat on current service)
271 @param service_jid: jid of the current service (e.g.: chat.jabberfr.org) 307 @param service_jid: jid of the current service (e.g.: chat.jabberfr.org)
272 """ 308 """
273 nb_arobas = arg.count('@') 309 nb_arobas = arg.count("@")
274 if nb_arobas == 1: 310 if nb_arobas == 1:
275 if arg[-1] != '@': 311 if arg[-1] != "@":
276 return jid.JID(arg) 312 return jid.JID(arg)
277 return jid.JID(arg + service_jid) 313 return jid.JID(arg + service_jid)
278 return jid.JID(u"%s@%s" % (arg, service_jid)) 314 return jid.JID(u"%s@%s" % (arg, service_jid))
279 315
280 def feedBack(self, client, message, mess_data, info_type=FEEDBACK_INFO_TYPE): 316 def feedBack(self, client, message, mess_data, info_type=FEEDBACK_INFO_TYPE):
281 """Give a message back to the user""" 317 """Give a message back to the user"""
282 if mess_data["type"] == 'groupchat': 318 if mess_data["type"] == "groupchat":
283 to_ = mess_data["to"].userhostJID() 319 to_ = mess_data["to"].userhostJID()
284 else: 320 else:
285 to_ = client.jid 321 to_ = client.jid
286 322
287 # we need to invert send message back, so sender need to original destinee 323 # we need to invert send message back, so sender need to original destinee
288 mess_data["from"] = mess_data["to"] 324 mess_data["from"] = mess_data["to"]
289 mess_data["to"] = to_ 325 mess_data["to"] = to_
290 mess_data["type"] = C.MESS_TYPE_INFO 326 mess_data["type"] = C.MESS_TYPE_INFO
291 mess_data["message"] = {'': message} 327 mess_data["message"] = {"": message}
292 mess_data["extra"]["info_type"] = info_type 328 mess_data["extra"]["info_type"] = info_type
293 client.messageSendToBridge(mess_data) 329 client.messageSendToBridge(mess_data)
294 330
295 def cmd_whois(self, client, mess_data): 331 def cmd_whois(self, client, mess_data):
296 """show informations on entity 332 """show informations on entity
301 """ 337 """
302 log.debug("Catched whois command") 338 log.debug("Catched whois command")
303 339
304 entity = mess_data["unparsed"].strip() 340 entity = mess_data["unparsed"].strip()
305 341
306 if mess_data['type'] == "groupchat": 342 if mess_data["type"] == "groupchat":
307 room = mess_data["to"].userhostJID() 343 room = mess_data["to"].userhostJID()
308 try: 344 try:
309 if self.host.plugins["XEP-0045"].isNickInRoom(client, room, entity): 345 if self.host.plugins["XEP-0045"].isNickInRoom(client, room, entity):
310 entity = u"%s/%s" % (room, entity) 346 entity = u"%s/%s" % (room, entity)
311 except KeyError: 347 except KeyError:
323 return False 359 return False
324 360
325 if not target_jid.resource: 361 if not target_jid.resource:
326 target_jid.resource = self.host.memory.getMainResource(client, target_jid) 362 target_jid.resource = self.host.memory.getMainResource(client, target_jid)
327 363
328 whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}] 364 whois_msg = [_(u"whois for %(jid)s") % {"jid": target_jid}]
329 365
330 d = defer.succeed(None) 366 d = defer.succeed(None)
331 for ignore, callback in self._whois: 367 for ignore, callback in self._whois:
332 d.addCallback(lambda ignore: callback(client, whois_msg, mess_data, target_jid)) 368 d.addCallback(
369 lambda ignore: callback(client, whois_msg, mess_data, target_jid)
370 )
333 371
334 def feedBack(ignore): 372 def feedBack(ignore):
335 self.feedBack(client, u"\n".join(whois_msg), mess_data) 373 self.feedBack(client, u"\n".join(whois_msg), mess_data)
336 return False 374 return False
337 375
346 """ 384 """
347 strings = [] 385 strings = []
348 for doc_name, doc_help in cmd_data.iteritems(): 386 for doc_name, doc_help in cmd_data.iteritems():
349 if doc_name.startswith("doc_arg_"): 387 if doc_name.startswith("doc_arg_"):
350 arg_name = doc_name[8:] 388 arg_name = doc_name[8:]
351 strings.append(u"- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help))) 389 strings.append(
390 u"- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help))
391 )
352 392
353 return strings 393 return strings
354 394
355 def cmd_me(self, client, mess_data): 395 def cmd_me(self, client, mess_data):
356 """display a message at third person 396 """display a message at third person
374 """ 414 """
375 cmd_name = mess_data["unparsed"].strip() 415 cmd_name = mess_data["unparsed"].strip()
376 if cmd_name and cmd_name[0] == "/": 416 if cmd_name and cmd_name[0] == "/":
377 cmd_name = cmd_name[1:] 417 cmd_name = cmd_name[1:]
378 if cmd_name and cmd_name not in self._commands: 418 if cmd_name and cmd_name not in self._commands:
379 self.feedBack(client, _(u"Invalid command name [{}]\n".format(cmd_name)), mess_data) 419 self.feedBack(
420 client, _(u"Invalid command name [{}]\n".format(cmd_name)), mess_data
421 )
380 cmd_name = "" 422 cmd_name = ""
381 if not cmd_name: 423 if not cmd_name:
382 # we show the global help 424 # we show the global help
383 longuest = max([len(command) for command in self._commands]) 425 longuest = max([len(command) for command in self._commands])
384 help_cmds = [] 426 help_cmds = []
385 427
386 for command in sorted(self._commands): 428 for command in sorted(self._commands):
387 cmd_data = self._commands[command] 429 cmd_data = self._commands[command]
388 if not self._contextValid(mess_data, cmd_data): 430 if not self._contextValid(mess_data, cmd_data):
389 continue 431 continue
390 spaces = (longuest - len(command)) * ' ' 432 spaces = (longuest - len(command)) * " "
391 help_cmds.append(" /{command}: {spaces} {short_help}".format( 433 help_cmds.append(
392 command=command, 434 " /{command}: {spaces} {short_help}".format(
393 spaces=spaces, 435 command=command,
394 short_help=cmd_data["doc_short_help"] 436 spaces=spaces,
395 )) 437 short_help=cmd_data["doc_short_help"],
396 438 )
397 help_mess = _(u"Text commands available:\n%s") % (u'\n'.join(help_cmds), ) 439 )
440
441 help_mess = _(u"Text commands available:\n%s") % (u"\n".join(help_cmds),)
398 else: 442 else:
399 # we show detailled help for a command 443 # we show detailled help for a command
400 cmd_data = self._commands[cmd_name] 444 cmd_data = self._commands[cmd_name]
401 syntax = cmd_data["args"] 445 syntax = cmd_data["args"]
402 help_mess = _(u"/{name}: {short_help}\n{syntax}{args_help}").format( 446 help_mess = _(u"/{name}: {short_help}\n{syntax}{args_help}").format(
403 name=cmd_name, 447 name=cmd_name,
404 short_help=cmd_data['doc_short_help'], 448 short_help=cmd_data["doc_short_help"],
405 syntax=_(" "*4+"syntax: {}\n").format(syntax) if syntax else "", 449 syntax=_(" " * 4 + "syntax: {}\n").format(syntax) if syntax else "",
406 args_help=u'\n'.join([u" "*8+"{}".format(line) for line in self._getArgsHelp(cmd_data)])) 450 args_help=u"\n".join(
451 [u" " * 8 + "{}".format(line) for line in self._getArgsHelp(cmd_data)]
452 ),
453 )
407 454
408 self.feedBack(client, help_mess, mess_data) 455 self.feedBack(client, help_mess, mess_data)