[telepathy-ashes/master] Added support for !commands in text chats.
David Laban
david.laban at collabora.co.uk
Tue Oct 13 11:57:53 PDT 2009
Currently implemented are:
!callme !delay !help !videome
---
ashes/tools/commands.py | 148 ++++++++++++++++++++++++++++++++++++++++++++
ashes/tools/dispatchers.py | 8 ++-
ashes/tools/echo_bot.py | 3 +-
ashes/tools/presence.py | 3 +-
4 files changed, 158 insertions(+), 4 deletions(-)
create mode 100644 ashes/tools/commands.py
diff --git a/ashes/tools/commands.py b/ashes/tools/commands.py
new file mode 100644
index 0000000..1b2f8fe
--- /dev/null
+++ b/ashes/tools/commands.py
@@ -0,0 +1,148 @@
+import re
+import time
+import traceback
+import inspect
+import types
+
+import gobject
+import telepathy
+
+from bases import ChannelListener
+from helper_functions import (get_connections, get_property,
+ green, red, printer, rpartial)
+
+from text import TextChannelEchoer
+
+class CommandExecutor(TextChannelEchoer):
+ """
+ Handles !commands.
+
+ FIXME: The flow control in this whole class is really quite ugly.
+ """
+
+ def Received(self, id, timestamp, sender, type, flags, text):
+ """Handles Channel.Type.Text.Received() and sends it back."""
+ self.handle_message_and_reply(id, timestamp, sender, type, flags, text)
+ self.channel[telepathy.CHANNEL_TYPE_TEXT].AcknowledgePendingMessages([id])
+
+ def handle_message_and_reply(self, id, timestamp, sender, type, flags, text):
+ """
+ Dispatches the command to the correct function and sends back an appropriate reply.
+ """
+ print "Message received:", text
+ if text.startswith('!'):
+ reply = self.execute_command(id, timestamp, sender, type, flags, text)
+ else:
+ reply = self.identify(timestamp, text)
+ print "Responding:", reply
+ self.channel[telepathy.CHANNEL_TYPE_TEXT].Send(type, reply)
+
+ def execute_command(self, id, timestamp, sender, type, flags, text):
+ """
+ Dispatches the command to the correct function.
+ """
+ command_name = text.partition(" ")[0][1:]
+ method = getattr(self, "do_" + command_name, None)
+ if isinstance(method, types.MethodType):
+ try:
+ reply = method(id, timestamp, sender, type, flags, text)
+ except:
+ reply = "An error occurred:\n" + traceback.format_exc()
+ else:
+ reply = 'Invalid command: "%s".' % command_name
+
+ if not reply:
+ reply = self.identify(timestamp, text)
+ return reply
+
+ def get_help_for(self, command_name):
+ """
+ Returns the specific help for command_name.
+ """
+ method = getattr(self, "do_" + command_name, None)
+ if isinstance(method, types.MethodType):
+ return inspect.getdoc(method)
+ else:
+ return 'Invalid command: "%s".\n%s' % (command_name, self.get_help())
+
+ def get_help(self):
+ """
+ Returns the first non-blank line of help for each command.
+ """
+ help_strings = ["The following commands are valid.",
+ "Type !help command for more details"]
+ command_names = [name for name in dir(self) if name.startswith('do_')]
+ for name in command_names:
+ method = getattr(self, name)
+ if isinstance(method, types.MethodType):
+ help = inspect.getdoc(method).strip().split('\n')[0]
+ help_strings.append(help)
+ return '\n'.join(help_strings)
+
+ def do_help(self, id, timestamp, sender, type, flags, text):
+ """
+ !help [command]
+
+ Prints help on command or the list of all commands.
+ """
+ help_, _, arg = text.partition(' ')
+ assert help_ == "!help"
+ if arg.strip(): # ignore whitespace
+ return self.get_help_for(arg)
+ else:
+ return self.get_help()
+
+ def do_delay(self, id, timestamp, sender, type, flags, text):
+ """
+ !delay timeout message
+
+ Pretends that message actually arrived timeout seconds later than it did.
+ """
+ delay_, _, args = text.partition(' ')
+ assert delay_ == "!delay"
+ timeout, _, message = args.strip().partition(' ')
+ timeout = int(timeout)
+ gobject.timeout_add_seconds(timeout, self.handle_message_and_reply,
+ id, timestamp+timeout, sender, type, flags, message)
+
+
+ def do_callme(self, id, timestamp, sender, type, flags, text):
+ """
+ !callme
+
+ Opens a voice call to whoever sent this command.
+ """
+ self.make_call(target, True, False)
+ return "Please wait."
+
+ def do_videome(self, id, timestamp, sender, type, flags, text):
+ """
+ !videome
+
+ Opens a video call to whoever sent this command.
+ """
+ self.make_call(target, True, True)
+ return "Please wait."
+
+
+
+ def make_call(self, target, want_audio, want_video):
+ """
+ Makes a call to target with audio and video if wanted.
+ """
+ request = {
+ 'org.freedesktop.Telepathy.Channel.ChannelType':
+ 'org.freedesktop.Telepathy.Channel.Type.StreamedMedia',
+ 'org.freedesktop.Telepathy.Channel.TargetHandleType':
+ 1,
+ 'org.freedesktop.Telepathy.Channel.TargetHandle':
+ target,
+ 'org.freedesktop.Telepathy.Channel.Type.StreamedMedia.FUTURE.InitialAudio':
+ want_audio,
+ 'org.freedesktop.Telepathy.Channel.Type.StreamedMedia.FUTURE.InitialVideo':
+ want_video,
+ }
+ self.connection[telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS
+ ].CreateChannel(request)
+
+
diff --git a/ashes/tools/dispatchers.py b/ashes/tools/dispatchers.py
index 0e2055a..43b9ad9 100644
--- a/ashes/tools/dispatchers.py
+++ b/ashes/tools/dispatchers.py
@@ -121,8 +121,12 @@ class ChannelDispatcher(ConnectionListener):
# Don't be putting it in any libraries.
if handle_type == telepathy.HANDLE_TYPE_CONTACT:
if re.match(self.contact_regexp, id):
- assert not requested, "We shouldn't be creating any new channels."
- print "incoming channel", id, 'matching', self.contact_regexp
+ #assert not requested, "We shouldn't be creating any new channels."
+ if requested:
+ print "Outgoing channel",
+ else:
+ print "Incoming channel",
+ print id, 'matching', self.contact_regexp
else:
print id, 'does not match', self.contact_regexp
return
diff --git a/ashes/tools/echo_bot.py b/ashes/tools/echo_bot.py
index f44f893..5594714 100644
--- a/ashes/tools/echo_bot.py
+++ b/ashes/tools/echo_bot.py
@@ -19,6 +19,7 @@ from media_echoer import MediaChannelEchoer, IceEchoer
from presence import PresenceEchoer, Onlineifier
from groups import ContactSubscriber
from text import TextChannelEchoer
+from commands import CommandExecutor
from file_transfer import FileTransferEchoer
#TODO: wrap everything to 80 chars so that I can hack on my eeepc.
@@ -36,7 +37,7 @@ class EchoBot( ContactSubscriber,
"""
# Using python's name mangling to avoid name collisions.
__channel_handler_classes = [MediaChannelEchoer,
- TextChannelEchoer, FileTransferEchoer]
+ CommandExecutor, FileTransferEchoer]
diff --git a/ashes/tools/presence.py b/ashes/tools/presence.py
index 90db60b..51c3897 100644
--- a/ashes/tools/presence.py
+++ b/ashes/tools/presence.py
@@ -25,7 +25,8 @@ class Onlineifier(ConnectionListener):
def StatusChanged(self, status, reason=0):
if status == 0: #Connected
print "Setting status to online"
- self.connection[self.SIMPLE_PRESENCE].SetPresence('available', '')
+ self.connection[self.SIMPLE_PRESENCE].SetPresence('available',
+ 'Type !help in a conversation for more info.')
# We know that ConnectionListener listens to this signal to detect
# disconnects. Other mixin classes MAY also override this.
super(Onlineifier, self).StatusChanged(status, reason)
--
1.5.6.5
More information about the telepathy-commits
mailing list