comparison src/plugins/plugin_misc_text_commands.py @ 921:8dd168c7741c

plugin text commands: refactoring: - commands can now return a deferred - if commands return True, message is sent, else it is cancelled - the escape sequence is now '//' instead of '\/' - added software version to /whois command
author Goffi <goffi@goffi.org>
date Sat, 22 Mar 2014 13:47:33 +0100
parents 1fe00f0c9a91
children c897c8d321b3
comparison
equal deleted inserted replaced
920:45dffd67a18a 921:8dd168c7741c
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core.sat_main import MessageSentAndStored
21 from twisted.words.protocols.jabber import jid 22 from twisted.words.protocols.jabber import jid
23 from twisted.internet import defer
24 from twisted.python.failure import Failure
22 from logging import debug, info, warning, error 25 from logging import debug, info, warning, error
23 26
24 PLUGIN_INFO = { 27 PLUGIN_INFO = {
25 "name": "Text commands", 28 "name": "Text commands",
26 "import_name": "TEXT-COMMANDS", 29 "import_name": "TEXT-COMMANDS",
27 "type": "Misc", 30 "type": "Misc",
28 "protocols": [], 31 "protocols": [],
29 "dependencies": ["XEP-0045", "EXP-PARROT"], 32 "dependencies": ["XEP-0045", "EXP-PARROT", "XEP-0092"],
30 "main": "TextCommands", 33 "main": "TextCommands",
31 "handler": "no", 34 "handler": "no",
32 "description": _("""IRC like text commands""") 35 "description": _("""IRC like text commands""")
33 } 36 }
34 37
42 info(_("Text commands initialization")) 45 info(_("Text commands initialization"))
43 self.host = host 46 self.host = host
44 host.trigger.add("sendMessage", self.sendMessageTrigger) 47 host.trigger.add("sendMessage", self.sendMessageTrigger)
45 48
46 def sendMessageTrigger(self, mess_data, treatments, profile): 49 def sendMessageTrigger(self, mess_data, treatments, profile):
47 """ Check text commands in message, and react consequently """ 50 """ Install SendMessage command hook """
51 treatments.addCallback(self._sendMessageCmdHook, profile)
52 return True
53
54 def _sendMessageCmdHook(self, mess_data, profile):
55 """ Check text commands in message, and react consequently
56 msg starting with / are potential command. If a command is found, it is executed, else message is sent normally
57 msg starting with // are escaped: they are sent with a single /
58 commands can abord message sending (if they return anything evaluating to False), or continue it (if they return True), eventually after modifying the message
59 an "unparsed" key is added to message, containing part of the message not yet parsed
60 commands can be deferred or not
61
62 """
48 msg = mess_data["message"] 63 msg = mess_data["message"]
49 if msg: 64 try:
50 if msg[0] == '/': 65 if msg[:2] == '//':
51 command = msg[1:].partition(' ')[0].lower() 66 # we have a double '/', it's the escape sequence
52 if command.isalpha(): 67 mess_data["message"] = msg[1:]
53 # looks like an actual command, we try to call the corresponding method 68 return mess_data
54 try: 69 if msg[0] != '/':
55 mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message 70 return mess_data
56 return getattr(self, "cmd_%s" % command)(mess_data, profile) 71 except IndexError:
57 except AttributeError: 72 return mess_data
58 pass 73
59 elif msg[0] == '\\': # we have escape char 74 # we have a command
60 try: 75 d = None
61 if msg[1] in ('/', '\\'): # we have '\/' or '\\', we escape to '/' or '\' 76 command = msg[1:].partition(' ')[0].lower()
62 mess_data["message"] = msg[1:] 77 if command.isalpha():
63 except IndexError: 78 # looks like an actual command, we try to call the corresponding method
64 pass 79 def retHandling(ret):
65 return True 80 """ Handle command return value:
81 if ret is True, normally send message (possibly modified by command)
82 else, abord message sending
83
84 """
85 if ret:
86 return mess_data
87 else:
88 return Failure(MessageSentAndStored("text commands took over", mess_data))
89
90 try:
91 mess_data["unparsed"] = msg[1 + len(command):] # part not yet parsed of the message
92 d = defer.maybeDeferred(getattr(self, "cmd_%s" % command), mess_data, profile)
93 d.addCallback(retHandling)
94 except AttributeError:
95 pass
96
97 return d or mess_data # if a command is detected, we should have a deferred, else be send the message normally
66 98
67 def _getRoomJID(self, arg, service_jid): 99 def _getRoomJID(self, arg, service_jid):
68 """Return a room jid with a shortcut 100 """Return a room jid with a shortcut
69 @param arg: argument: can be a full room jid (e.g.: sat@chat.jabberfr.org) 101 @param arg: argument: can be a full room jid (e.g.: sat@chat.jabberfr.org)
70 or a shortcut (e.g.: sat or sat@ for sat on current service) 102 or a shortcut (e.g.: sat or sat@ for sat on current service)
228 raise jid.InvalidFormat 260 raise jid.InvalidFormat
229 except (jid.InvalidFormat, RuntimeError): 261 except (jid.InvalidFormat, RuntimeError):
230 self._feedBack(_("Invalid jid, can't whois"), mess_data, profile) 262 self._feedBack(_("Invalid jid, can't whois"), mess_data, profile)
231 return False 263 return False
232 264
265 if not target_jid.resource:
266 target_jid.resource = self.host.memory.getLastResource(target_jid, profile)
267
233 whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}] 268 whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}]
234 #TODO: add informations here (client version, vcard, etc) 269
235 270 # version
236 self._feedBack(u"\n".join(whois_msg), mess_data, profile) 271 def versionCb(version_data):
237 272 name, version, os = version_data
238 return False 273 if name:
274 whois_msg.append(_("Client name: %s") % name)
275 if version:
276 whois_msg.append(_("Client version: %s") % version)
277 if os:
278 whois_msg.append(_("Operating system: %s") % os)
279
280 d = self.host.plugins['XEP-0092'].getVersion(target_jid, profile)
281 d.addCallback(versionCb)
282
283 #TODO: add informations here (vcard, etc)
284
285 def feedBack(ignore):
286 self._feedBack(u"\n".join(whois_msg), mess_data, profile)
287 return False
288
289 d.addCallback(feedBack)
290 return d
239 291
240 def cmd_help(self, mess_data, profile): 292 def cmd_help(self, mess_data, profile):
241 """show help on available commands""" 293 """show help on available commands"""
242 commands = filter(lambda method: method.startswith('cmd_'), dir(self)) 294 commands = filter(lambda method: method.startswith('cmd_'), dir(self))
243 longuest = max([len(command) for command in commands]) 295 longuest = max([len(command) for command in commands])