Mercurial > libervia-backend
comparison src/plugins/plugin_misc_text_commands.py @ 1963:a2bc5089c2eb
backend, frontends: message refactoring (huge commit):
/!\ several features are temporarily disabled, like notifications in frontends
next step in refactoring, with the following changes:
- jp: updated jp message to follow changes in backend/bridge
- jp: added --lang, --subject, --subject_lang, and --type options to jp message + fixed unicode handling for jid
- quick_frontend (QuickApp, QuickChat):
- follow backend changes
- refactored chat, message are now handled in OrderedDict and uid are kept so they can be updated
- Message and Occupant classes handle metadata, so frontend just have to display them
- Primitivus (Chat):
- follow backend/QuickFrontend changes
- info & standard messages are handled in the same MessageWidget class
- improved/simplified handling of messages, removed update() method
- user joined/left messages are merged when next to each other
- a separator is shown when message is received while widget is out of focus, so user can quickly see the new messages
- affiliation/role are shown (in a basic way for now) in occupants panel
- removed "/me" messages handling, as it will be done by a backend plugin
- message language is displayed when available (only one language per message for now)
- fixed :history and :search commands
- core (constants): new constants for messages type, XML namespace, entity type
- core: *Message methods renamed to follow new code sytle (e.g. sendMessageToBridge => messageSendToBridge)
- core (messages handling): fixed handling of language
- core (messages handling): mes_data['from'] and ['to'] are now jid.JID
- core (core.xmpp): reorganised message methods, added getNick() method to client.roster
- plugin text commands: fixed plugin and adapted to new messages behaviour. client is now used in arguments instead of profile
- plugins: added information for cancellation reason in CancelError calls
- plugin XEP-0045: various improvments, but this plugin still need work:
- trigger is used to avoid message already handled by the plugin to be handled a second time
- changed the way to handle history, the last message from DB is checked and we request only messages since this one, in seconds (thanks Poezio folks :))
- subject reception is waited before sending the roomJoined signal, this way we are sure that everything including history is ready
- cmd_* method now follow the new convention with client instead of profile
- roomUserJoined and roomUserLeft messages are removed, the events are now handled with info message with a "ROOM_USER_JOINED" info subtype
- probably other forgotten stuffs :p
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 20 Jun 2016 18:41:53 +0200 |
parents | 633b5c21aefd |
children | 200cd707a46d |
comparison
equal
deleted
inserted
replaced
1962:a45235d8dc93 | 1963:a2bc5089c2eb |
---|---|
44 pass | 44 pass |
45 | 45 |
46 | 46 |
47 CMD_KEY = "@command" | 47 CMD_KEY = "@command" |
48 CMD_TYPES = ('group', 'one2one', 'all') | 48 CMD_TYPES = ('group', 'one2one', 'all') |
49 FEEDBACK_INFO_TYPE = "TEXT_CMD" | |
49 | 50 |
50 | 51 |
51 class TextCommands(object): | 52 class TextCommands(object): |
52 #FIXME: doc strings for commands have to be translatable | 53 #FIXME: doc strings for commands have to be translatable |
53 # plugins need a dynamic translation system (translation | 54 # plugins need a dynamic translation system (translation |
146 if cmd_name in self._commands: | 147 if cmd_name in self._commands: |
147 suff=2 | 148 suff=2 |
148 while (cmd_name + str(suff)) in self._commands: | 149 while (cmd_name + str(suff)) in self._commands: |
149 suff+=1 | 150 suff+=1 |
150 new_name = cmd_name + str(suff) | 151 new_name = cmd_name + str(suff) |
151 log.warning(_(u"Conflict for command [%(old_name)s], renaming it to [%(new_name)s]") % {'old_name': cmd_name, 'new_name': new_name}) | 152 log.warning(_(u"Conflict for command [{old_name}], renaming it to [{new_name}]").format(old_name=cmd_name, new_name=new_name)) |
152 cmd_name = new_name | 153 cmd_name = new_name |
153 self._commands[cmd_name] = cmd_data = OrderedDict({'callback':cmd}) # We use an Ordered dict to keep documenation order | 154 self._commands[cmd_name] = cmd_data = OrderedDict({'callback':cmd}) # We use an Ordered dict to keep documenation order |
154 cmd_data.update(self._parseDocString(cmd, cmd_name)) | 155 cmd_data.update(self._parseDocString(cmd, cmd_name)) |
155 log.info(_("Registered text command [%s]") % cmd_name) | 156 log.info(_("Registered text command [%s]") % cmd_name) |
156 | 157 |
180 an "unparsed" key is added to message, containing part of the message not yet parsed | 181 an "unparsed" key is added to message, containing part of the message not yet parsed |
181 commands can be deferred or not | 182 commands can be deferred or not |
182 @param mess_data(dict): data comming from messageSend trigger | 183 @param mess_data(dict): data comming from messageSend trigger |
183 @param profile: %(doc_profile)s | 184 @param profile: %(doc_profile)s |
184 """ | 185 """ |
185 profile = client.profile | |
186 try: | 186 try: |
187 msg = mess_data["message"][''] | 187 msg = mess_data["message"][''] |
188 msg_lang = '' | 188 msg_lang = '' |
189 except KeyError: | 189 except KeyError: |
190 try: | 190 try: |
215 else, abord message sending | 215 else, abord message sending |
216 """ | 216 """ |
217 if ret: | 217 if ret: |
218 return mess_data | 218 return mess_data |
219 else: | 219 else: |
220 log.debug("text commands took over") | 220 log.debug(u"text command detected ({})".format(command)) |
221 raise failure.Failure(exceptions.CancelError()) | 221 raise failure.Failure(exceptions.CancelError()) |
222 | 222 |
223 def genericErrback(failure): | 223 def genericErrback(failure): |
224 try: | 224 try: |
225 msg = u"with condition {}".format(failure.value.condition) | 225 msg = u"with condition {}".format(failure.value.condition) |
226 except AttributeError: | 226 except AttributeError: |
227 msg = u"with error {}".format(failure.value) | 227 msg = u"with error {}".format(failure.value) |
228 self.feedBack(u"Command failed {}".format(msg), mess_data, profile) | 228 self.feedBack(client, u"Command failed {}".format(msg), mess_data) |
229 return False | 229 return False |
230 | 230 |
231 mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message | 231 mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message |
232 try: | 232 try: |
233 cmd_data = self._commands[command] | 233 cmd_data = self._commands[command] |
234 except KeyError: | 234 except KeyError: |
235 self.feedBack(_("Unknown command /%s. ") % command + self.HELP_SUGGESTION, mess_data, profile) | 235 self.feedBack(client, _("Unknown command /%s. ") % command + self.HELP_SUGGESTION, mess_data) |
236 log.debug("text commands took over") | 236 log.debug("text command help message") |
237 raise failure.Failure(exceptions.CancelError()) | 237 raise failure.Failure(exceptions.CancelError()) |
238 else: | 238 else: |
239 if not self._contextValid(mess_data, cmd_data): | 239 if not self._contextValid(mess_data, cmd_data): |
240 # The command is not launched in the right context, we throw a message with help instructions | 240 # The command is not launched in the right context, we throw a message with help instructions |
241 context_txt = _("group discussions") if cmd_data["type"] == "group" else _("one to one discussions") | 241 context_txt = _("group discussions") if cmd_data["type"] == "group" else _("one to one discussions") |
242 feedback = _("/{command} command only applies in {context}.").format(command=command, context=context_txt) | 242 feedback = _("/{command} command only applies in {context}.").format(command=command, context=context_txt) |
243 self.feedBack(u"{} {}".format(feedback, self.HELP_SUGGESTION), mess_data, profile) | 243 self.feedBack(client, u"{} {}".format(feedback, self.HELP_SUGGESTION), mess_data) |
244 log.debug("text commands took over") | 244 log.debug("text command invalid message") |
245 raise failure.Failure(exceptions.CancelError()) | 245 raise failure.Failure(exceptions.CancelError()) |
246 else: | 246 else: |
247 d = defer.maybeDeferred(cmd_data["callback"], mess_data, profile) | 247 d = defer.maybeDeferred(cmd_data["callback"], client, mess_data) |
248 d.addErrback(genericErrback) | 248 d.addErrback(genericErrback) |
249 d.addCallback(retHandling) | 249 d.addCallback(retHandling) |
250 | 250 |
251 return d or mess_data # if a command is detected, we should have a deferred, else we send the message normally | 251 return d or mess_data # if a command is detected, we should have a deferred, else we send the message normally |
252 | 252 |
274 if arg[-1] != '@': | 274 if arg[-1] != '@': |
275 return jid.JID(arg) | 275 return jid.JID(arg) |
276 return jid.JID(arg + service_jid) | 276 return jid.JID(arg + service_jid) |
277 return jid.JID(u"%s@%s" % (arg, service_jid)) | 277 return jid.JID(u"%s@%s" % (arg, service_jid)) |
278 | 278 |
279 def feedBack(self, message, mess_data, profile): | 279 def feedBack(self, client, message, mess_data, info_type=FEEDBACK_INFO_TYPE): |
280 """Give a message back to the user""" | 280 """Give a message back to the user""" |
281 if mess_data["type"] == 'groupchat': | 281 if mess_data["type"] == 'groupchat': |
282 _from = mess_data["to"].userhostJID() | 282 to_ = mess_data["to"].userhostJID() |
283 else: | 283 else: |
284 _from = self.host.getJidNStream(profile)[0] | 284 to_ = client.jid |
285 | 285 |
286 self.host.bridge.messageNew(unicode(mess_data["to"]), {'': message}, {}, C.MESS_TYPE_INFO, unicode(_from), {}, profile=profile) | 286 # we need to invert send message back, so sender need to original destinee |
287 | 287 mess_data["from"] = mess_data["to"] |
288 def cmd_whois(self, mess_data, profile): | 288 mess_data["to"] = to_ |
289 mess_data["type"] = C.MESS_TYPE_INFO | |
290 mess_data["message"] = {'': message} | |
291 mess_data["extra"]["info_type"] = info_type | |
292 self.host.messageSendToBridge(mess_data, client) | |
293 | |
294 def cmd_whois(self, client, mess_data): | |
289 """show informations on entity | 295 """show informations on entity |
290 | 296 |
291 @command: [JID|ROOM_NICK] | 297 @command: [JID|ROOM_NICK] |
292 - JID: entity to request | 298 - JID: entity to request |
293 - ROOM_NICK: nick of the room to request | 299 - ROOM_NICK: nick of the room to request |
297 entity = mess_data["unparsed"].strip() | 303 entity = mess_data["unparsed"].strip() |
298 | 304 |
299 if mess_data['type'] == "groupchat": | 305 if mess_data['type'] == "groupchat": |
300 room = mess_data["to"].userhostJID() | 306 room = mess_data["to"].userhostJID() |
301 try: | 307 try: |
302 if self.host.plugins["XEP-0045"].isNickInRoom(room, entity, profile): | 308 if self.host.plugins["XEP-0045"].isNickInRoom(room, entity, client.profile): |
303 entity = u"%s/%s" % (room, entity) | 309 entity = u"%s/%s" % (room, entity) |
304 except KeyError: | 310 except KeyError: |
305 log.warning("plugin XEP-0045 is not present") | 311 log.warning("plugin XEP-0045 is not present") |
306 | 312 |
307 if not entity: | 313 if not entity: |
310 try: | 316 try: |
311 target_jid = jid.JID(entity) | 317 target_jid = jid.JID(entity) |
312 if not target_jid.user or not target_jid.host: | 318 if not target_jid.user or not target_jid.host: |
313 raise jid.InvalidFormat | 319 raise jid.InvalidFormat |
314 except (RuntimeError, jid.InvalidFormat, AttributeError): | 320 except (RuntimeError, jid.InvalidFormat, AttributeError): |
315 self.feedBack(_("Invalid jid, can't whois"), mess_data, profile) | 321 self.feedBack(client, _("Invalid jid, can't whois"), mess_data) |
316 return False | 322 return False |
317 | 323 |
318 if not target_jid.resource: | 324 if not target_jid.resource: |
319 target_jid.resource = self.host.memory.getMainResource(target_jid, profile) | 325 target_jid.resource = self.host.memory.getMainResource(target_jid, client.profile) |
320 | 326 |
321 whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}] | 327 whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}] |
322 | 328 |
323 d = defer.succeed(None) | 329 d = defer.succeed(None) |
324 for ignore, callback in self._whois: | 330 for ignore, callback in self._whois: |
325 d.addCallback(lambda ignore: callback(whois_msg, mess_data, target_jid, profile)) | 331 d.addCallback(lambda ignore: callback(client, whois_msg, mess_data, target_jid)) |
326 | 332 |
327 def feedBack(ignore): | 333 def feedBack(ignore): |
328 self.feedBack(u"\n".join(whois_msg), mess_data, profile) | 334 self.feedBack(client, u"\n".join(whois_msg), mess_data) |
329 return False | 335 return False |
330 | 336 |
331 d.addCallback(feedBack) | 337 d.addCallback(feedBack) |
332 return d | 338 return d |
333 | 339 |
343 arg_name = doc_name[8:] | 349 arg_name = doc_name[8:] |
344 strings.append(u"- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help))) | 350 strings.append(u"- {name}: {doc_help}".format(name=arg_name, doc_help=_(doc_help))) |
345 | 351 |
346 return strings | 352 return strings |
347 | 353 |
348 def cmd_help(self, mess_data, profile): | 354 def cmd_help(self, client, mess_data): |
349 """show help on available commands | 355 """show help on available commands |
350 | 356 |
351 @command: [cmd_name] | 357 @command: [cmd_name] |
352 - cmd_name: name of the command for detailed help | 358 - cmd_name: name of the command for detailed help |
353 """ | 359 """ |
354 cmd_name = mess_data["unparsed"].strip() | 360 cmd_name = mess_data["unparsed"].strip() |
355 if cmd_name and cmd_name[0] == "/": | 361 if cmd_name and cmd_name[0] == "/": |
356 cmd_name = cmd_name[1:] | 362 cmd_name = cmd_name[1:] |
357 if cmd_name and cmd_name not in self._commands: | 363 if cmd_name and cmd_name not in self._commands: |
358 self.feedBack(_(u"Invalid command name [{}]\n".format(cmd_name)), mess_data, profile) | 364 self.feedBack(client, _(u"Invalid command name [{}]\n".format(cmd_name)), mess_data) |
359 cmd_name = "" | 365 cmd_name = "" |
360 if not cmd_name: | 366 if not cmd_name: |
361 # we show the global help | 367 # we show the global help |
362 longuest = max([len(command) for command in self._commands]) | 368 longuest = max([len(command) for command in self._commands]) |
363 help_cmds = [] | 369 help_cmds = [] |
382 name=cmd_name, | 388 name=cmd_name, |
383 short_help=cmd_data['doc_short_help'], | 389 short_help=cmd_data['doc_short_help'], |
384 syntax=_(" "*4+"syntax: {}\n").format(syntax) if syntax else "", | 390 syntax=_(" "*4+"syntax: {}\n").format(syntax) if syntax else "", |
385 args_help=u'\n'.join([u" "*8+"{}".format(line) for line in self._getArgsHelp(cmd_data)])) | 391 args_help=u'\n'.join([u" "*8+"{}".format(line) for line in self._getArgsHelp(cmd_data)])) |
386 | 392 |
387 self.feedBack(help_mess, mess_data, profile) | 393 self.feedBack(client, help_mess, mess_data) |
388 | |
389 def cmd_me(self, mess_data, profile): | |
390 """Display a message at third person | |
391 | |
392 @command: message | |
393 - message: message to display at the third person | |
394 """ | |
395 # We just catch the method and continue it as the frontends should manage /me display | |
396 return True |