From c004f042f9976e90cfd42a0698ba83872fe00825 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Thu, 20 May 2010 23:17:22 +0800 Subject: Added del_event_handler to remove handler functions for a given event. All registered handlers for the event which use the given function will be removed. Using this method allows agents to reconfigure their behaviour on the fly without needing to add extra state information to event handling functions. --- sleekxmpp/basexmpp.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 13fe2100..83431bd7 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -185,6 +185,19 @@ class basexmpp(object): self.event_handlers[name] = [] self.event_handlers[name].append((pointer, threaded, disposable)) + def del_event_handler(self, name, pointer): + """Remove a handler for an event.""" + if not name in self.event_handlers: + return + + # Need to keep handlers that do not use + # the given function pointer + def filter_pointers(handler): + return handler[0] != pointer + + self.event_handlers[name] = filter(filter_pointers, + self.event_handlers[name]) + def event(self, name, eventdata = {}): # called on an event for handler in self.event_handlers.get(name, []): if handler[1]: #if threaded -- cgit v1.2.3 From feaa7539affa9a451b568b5b1502630cd3833ca3 Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Thu, 20 May 2010 13:09:04 -0700 Subject: added test_events and testing new del_event_handler --- tests/test_events.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/test_events.py diff --git a/tests/test_events.py b/tests/test_events.py new file mode 100644 index 00000000..11821dbb --- /dev/null +++ b/tests/test_events.py @@ -0,0 +1,35 @@ +import unittest + +class testevents(unittest.TestCase): + + def setUp(self): + import sleekxmpp.stanza.presence as p + self.p = p + + def testEventHappening(self): + "Test handler working" + import sleekxmpp + c = sleekxmpp.ClientXMPP('crap@wherever', 'password') + happened = [] + def handletestevent(event): + happened.append(True) + c.add_event_handler("test_event", handletestevent) + c.event("test_event", {}) + c.event("test_event", {}) + self.failUnless(happened == [True, True], "event did not get triggered twice") + + def testDelEvent(self): + "Test handler working, then deleted and not triggered" + import sleekxmpp + c = sleekxmpp.ClientXMPP('crap@wherever', 'password') + happened = [] + def handletestevent(event): + happened.append(True) + c.add_event_handler("test_event", handletestevent) + c.event("test_event", {}) + c.del_event_handler("test_event", handletestevent) + c.event("test_event", {}) # should not trigger because it was deleted + self.failUnless(happened == [True], "event did not get triggered the correct number of times") + + +suite = unittest.TestLoader().loadTestsFromTestCase(testevents) -- cgit v1.2.3 From 3920ee394183d95aa6f5b4ee3c8843c2d34ad4cb Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Mon, 24 May 2010 14:27:13 -0700 Subject: added plugin indexing to components --- sleekxmpp/componentxmpp.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py index 9c7a6125..89953687 100755 --- a/sleekxmpp/componentxmpp.py +++ b/sleekxmpp/componentxmpp.py @@ -54,6 +54,16 @@ class ComponentXMPP(basexmpp, XMLStream): self.secret = secret self.registerHandler(Callback('Handshake', MatchXPath('{jabber:component:accept}handshake'), self._handleHandshake)) + def __getitem__(self, key): + if key in self.plugin: + return self.plugin[key] + else: + logging.warning("""Plugin "%s" is not loaded.""" % key) + return False + + def get(self, key, default): + return self.plugin.get(key, default) + def incoming_filter(self, xmlobj): if xmlobj.tag.startswith('{jabber:client}'): xmlobj.tag = xmlobj.tag.replace('jabber:client', self.default_ns) -- cgit v1.2.3 From 828cba875fa5466493e3ea3f804bb349743a0615 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 22 May 2010 22:39:35 +0800 Subject: Added the error attribute 'code' to the Error object interface. --- sleekxmpp/stanza/error.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleekxmpp/stanza/error.py b/sleekxmpp/stanza/error.py index 15af6624..f87b6490 100644 --- a/sleekxmpp/stanza/error.py +++ b/sleekxmpp/stanza/error.py @@ -12,7 +12,7 @@ class Error(ElementBase): name = 'error' plugin_attrib = 'error' conditions = set(('bad-request', 'conflict', 'feature-not-implemented', 'forbidden', 'gone', 'item-not-found', 'jid-malformed', 'not-acceptable', 'not-allowed', 'not-authorized', 'payment-required', 'recipient-unavailable', 'redirect', 'registration-required', 'remote-server-not-found', 'remote-server-timeout', 'service-unavailable', 'subscription-required', 'undefined-condition', 'unexpected-request')) - interfaces = set(('condition', 'text', 'type')) + interfaces = set(('code', 'condition', 'text', 'type')) types = set(('cancel', 'continue', 'modify', 'auth', 'wait')) sub_interfaces = set(('text',)) condition_ns = 'urn:ietf:params:xml:ns:xmpp-stanzas' -- cgit v1.2.3 From 35f4ef3452015d7f9ef1e300bc104793de12f1e8 Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sat, 22 May 2010 22:40:30 +0800 Subject: Modified the return values for several methods so that they can be chained. For example: iq.reply().error().setPayload(something.xml).send() --- sleekxmpp/stanza/iq.py | 1 + sleekxmpp/xmlstream/stanzabase.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py index 4969b703..ded7515f 100644 --- a/sleekxmpp/stanza/iq.py +++ b/sleekxmpp/stanza/iq.py @@ -37,6 +37,7 @@ class Iq(RootStanza): def setPayload(self, value): self.clear() StanzaBase.setPayload(self, value) + return self def setQuery(self, value): query = self.xml.find("{%s}query" % value) diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py index 018e81c3..3f3f5e08 100644 --- a/sleekxmpp/xmlstream/stanzabase.py +++ b/sleekxmpp/xmlstream/stanzabase.py @@ -332,7 +332,7 @@ class StanzaBase(ElementBase): def setType(self, value): if value in self.types: - self.xml.attrib['type'] = value + self.xml.attrib['type'] = value return self def getPayload(self): @@ -340,15 +340,18 @@ class StanzaBase(ElementBase): def setPayload(self, value): self.xml.append(value) + return self def delPayload(self): self.clear() + return self def clear(self): for child in self.xml.getchildren(): self.xml.remove(child) for plugin in list(self.plugins.keys()): del self.plugins[plugin] + return self def reply(self): self['from'], self['to'] = self['to'], self['from'] @@ -357,6 +360,7 @@ class StanzaBase(ElementBase): def error(self): self['type'] = 'error' + return self def getTo(self): return JID(self._getAttr('to')) -- cgit v1.2.3 From 5ca4ede5ac1ddbc56e97010a2f39ed6ada37817e Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Sun, 23 May 2010 01:30:49 +0800 Subject: Added a flag to registerPlugin to control calling the plugin's post_init method. --- sleekxmpp/basexmpp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 83431bd7..292a2088 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -86,7 +86,7 @@ class basexmpp(object): self.username = jid.split('@', 1)[0] self.server = jid.split('@',1)[-1].split('/', 1)[0] - def registerPlugin(self, plugin, pconfig = {}): + def registerPlugin(self, plugin, pconfig = {}, run_post=True): """Register a plugin not in plugins.__init__.__all__ but in the plugins directory.""" # discover relative "path" to the plugins module from the main app, and import it. @@ -100,6 +100,8 @@ class basexmpp(object): if hasattr(self.plugin[plugin], 'xep'): xep = "(XEP-%s) " % self.plugin[plugin].xep logging.debug("Loaded Plugin %s%s" % (xep, self.plugin[plugin].description)) + if run_post: + self.plugin[plugin].post_init() def register_plugins(self): """Initiates all plugins in the plugins/__init__.__all__""" @@ -109,7 +111,7 @@ class basexmpp(object): plugin_list = plugins.__all__ for plugin in plugin_list: if plugin in plugins.__all__: - self.registerPlugin(plugin, self.plugin_config.get(plugin, {})) + self.registerPlugin(plugin, self.plugin_config.get(plugin, {}), False) else: raise NameError("No plugin by the name of %s listed in plugins.__all__." % plugin) # run post_init() for cross-plugin interaction -- cgit v1.2.3 From 7ebc0065167189186ddf53e81a47dab887c87a72 Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Mon, 24 May 2010 19:33:24 -0700 Subject: updated README, index fix for component --- README | 8 ++++++-- sleekxmpp/componentxmpp.py | 4 ++-- sleekxmpp/plugins/stanza_pubsub.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README b/README index a6989090..670ce3cd 100644 --- a/README +++ b/README @@ -1,5 +1,8 @@ -SleekXMPP is an XMPP library written for Python 3.x (with 2.6 compatibility). +SleekXMPP is an XMPP library written for Python 3.1+ (with 2.6 compatibility). +Hosted at http://wiki.github.com/fritzy/SleekXMPP/ + Featured in examples in XMPP: The Definitive Guide by Kevin Smith, Remko Tronçon, and Peter Saint-Andre +If you're coming here from The Definitive Guide, please read http://wiki.github.com/fritzy/SleekXMPP/xmpp-the-definitive-guide SleekXMPP has several design goals/philosophies: - Low number of dependencies. @@ -31,7 +34,8 @@ Since 0.2, here's the Changelog: Credits ---------------- Main Author: Nathan Fritz fritz@netflint.net -XEP-0045 original implementation: Kevin Smith +Contributors: Kevin Smith & Lance Stout Patches: Remko Tronçon Feel free to add fritzy@netflint.net to your roster for direct support and comments. +Join sleekxmpp-discussion@googlegroups.com / http://groups.google.com/group/sleekxmpp-discussion for discussion. diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py index 89953687..de125814 100755 --- a/sleekxmpp/componentxmpp.py +++ b/sleekxmpp/componentxmpp.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2.5 +#!/usr/bin/python2.6 """ SleekXMPP: The Sleek XMPP Library @@ -54,7 +54,7 @@ class ComponentXMPP(basexmpp, XMLStream): self.secret = secret self.registerHandler(Callback('Handshake', MatchXPath('{jabber:component:accept}handshake'), self._handleHandshake)) - def __getitem__(self, key): + def __getitem__(self, key): if key in self.plugin: return self.plugin[key] else: diff --git a/sleekxmpp/plugins/stanza_pubsub.py b/sleekxmpp/plugins/stanza_pubsub.py index 4187d49c..1dd73d99 100644 --- a/sleekxmpp/plugins/stanza_pubsub.py +++ b/sleekxmpp/plugins/stanza_pubsub.py @@ -281,7 +281,7 @@ class DefaultConfig(ElementBase): def getType(self): t = self._getAttr('type') - if not t: t == 'leaf' + if not t: t = 'leaf' return t stanzaPlugin(PubsubOwner, DefaultConfig) -- cgit v1.2.3 From f18c79082490b9b25c72d8c8870dcbf63770ffb6 Mon Sep 17 00:00:00 2001 From: Hernan E Grecco Date: Wed, 26 May 2010 06:10:34 +0800 Subject: Fixed error registering a plugin. To add a feature to another plugin, it should look into xmpp.plugin dict --- sleekxmpp/plugins/xep_0004.py | 2 +- sleekxmpp/plugins/xep_0009.py | 4 ++-- sleekxmpp/plugins/xep_0092.py | 2 +- sleekxmpp/plugins/xep_0199.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sleekxmpp/plugins/xep_0004.py b/sleekxmpp/plugins/xep_0004.py index ec859252..24ffa978 100644 --- a/sleekxmpp/plugins/xep_0004.py +++ b/sleekxmpp/plugins/xep_0004.py @@ -31,7 +31,7 @@ class xep_0004(base.base_plugin): self.xmpp.add_handler("", self.handler_message_xform) def post_init(self): - self.xmpp['xep_0030'].add_feature('jabber:x:data') + self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') def handler_message_xform(self, xml): object = self.handle_form(xml) diff --git a/sleekxmpp/plugins/xep_0009.py b/sleekxmpp/plugins/xep_0009.py index e0da8296..403f94b9 100644 --- a/sleekxmpp/plugins/xep_0009.py +++ b/sleekxmpp/plugins/xep_0009.py @@ -185,8 +185,8 @@ class xep_0009(base.base_plugin): self.activeCalls = [] def post_init(self): - self.xmpp['xep_0030'].add_feature('jabber:iq:rpc') - self.xmpp['xep_0030'].add_identity('automatition','rpc') + self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:rpc') + self.xmpp.plugin['xep_0030'].add_identity('automatition','rpc') def register_call(self, method, name=None): #@returns an string that can be used in acl commands. diff --git a/sleekxmpp/plugins/xep_0092.py b/sleekxmpp/plugins/xep_0092.py index 3d026382..ca0d7e17 100644 --- a/sleekxmpp/plugins/xep_0092.py +++ b/sleekxmpp/plugins/xep_0092.py @@ -33,7 +33,7 @@ class xep_0092(base.base_plugin): self.xmpp.add_handler("" % self.xmpp.default_ns, self.report_version) def post_init(self): - self.xmpp['xep_0030'].add_feature('jabber:iq:version') + self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:version') def report_version(self, xml): iq = self.xmpp.makeIqResult(xml.get('id', 'unknown')) diff --git a/sleekxmpp/plugins/xep_0199.py b/sleekxmpp/plugins/xep_0199.py index 989e6450..9b10b927 100644 --- a/sleekxmpp/plugins/xep_0199.py +++ b/sleekxmpp/plugins/xep_0199.py @@ -35,7 +35,7 @@ class xep_0199(base.base_plugin): #self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True) def post_init(self): - self.xmpp['xep_0030'].add_feature('http://www.xmpp.org/extensions/xep-0199.html#ns') + self.xmpp.plugin['xep_0030'].add_feature('http://www.xmpp.org/extensions/xep-0199.html#ns') def handler_pingserver(self, xml): if not self.running: -- cgit v1.2.3 From 9cfe19c1e14f7045684ed523b65cdbdf3ab48b2e Mon Sep 17 00:00:00 2001 From: Hernan E Grecco Date: Wed, 26 May 2010 06:28:08 +0800 Subject: Changed example.py to register first Xep_0030. This a simple fix to prevent getting a key error as many plugins add features to Xep_0030. A better fix would be to call pos_init after all plugins are loaded. An even better fix would be to define dependencies for each plugin and registering on demand. --- example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.py b/example.py index 1ffe724a..c9b6559b 100644 --- a/example.py +++ b/example.py @@ -37,8 +37,8 @@ if __name__ == '__main__': logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s') xmpp = Example('user@gmail.com/sleekxmpp', 'password') + xmpp.registerPlugin('xep_0030') xmpp.registerPlugin('xep_0004') - xmpp.registerPlugin('xep_0030') xmpp.registerPlugin('xep_0060') xmpp.registerPlugin('xep_0199') if xmpp.connect(('talk.google.com', 5222)): -- cgit v1.2.3 From f4bc9d9722ea57b5f5d22efeb6bc65cb990e1053 Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Wed, 26 May 2010 10:51:51 -0700 Subject: plugins now are checked for post_init having ran when process() is called --- sleekxmpp/basexmpp.py | 9 ++++++--- sleekxmpp/plugins/base.py | 3 ++- sleekxmpp/plugins/xep_0004.py | 1 + sleekxmpp/plugins/xep_0009.py | 1 + sleekxmpp/plugins/xep_0050.py | 1 + sleekxmpp/plugins/xep_0092.py | 1 + sleekxmpp/plugins/xep_0199.py | 1 + 7 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py index 292a2088..907067fa 100644 --- a/sleekxmpp/basexmpp.py +++ b/sleekxmpp/basexmpp.py @@ -85,8 +85,13 @@ class basexmpp(object): self.jid = self.getjidbare(jid) self.username = jid.split('@', 1)[0] self.server = jid.split('@',1)[-1].split('/', 1)[0] + + def process(self, *args, **kwargs): + for idx in self.plugin: + if not self.plugin[idx].post_inited: self.plugin[idx].post_init() + return super(basexmpp, self).process(*args, **kwargs) - def registerPlugin(self, plugin, pconfig = {}, run_post=True): + def registerPlugin(self, plugin, pconfig = {}): """Register a plugin not in plugins.__init__.__all__ but in the plugins directory.""" # discover relative "path" to the plugins module from the main app, and import it. @@ -100,8 +105,6 @@ class basexmpp(object): if hasattr(self.plugin[plugin], 'xep'): xep = "(XEP-%s) " % self.plugin[plugin].xep logging.debug("Loaded Plugin %s%s" % (xep, self.plugin[plugin].description)) - if run_post: - self.plugin[plugin].post_init() def register_plugins(self): """Initiates all plugins in the plugins/__init__.__all__""" diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py index 685833f4..4223646a 100644 --- a/sleekxmpp/plugins/base.py +++ b/sleekxmpp/plugins/base.py @@ -24,6 +24,7 @@ class base_plugin(object): self.description = 'Base Plugin' self.xmpp = xmpp self.config = config + self.post_inited = False self.enable = config.get('enable', True) if self.enable: self.plugin_init() @@ -32,4 +33,4 @@ class base_plugin(object): pass def post_init(self): - pass + self.post_inited = True diff --git a/sleekxmpp/plugins/xep_0004.py b/sleekxmpp/plugins/xep_0004.py index 24ffa978..56d18929 100644 --- a/sleekxmpp/plugins/xep_0004.py +++ b/sleekxmpp/plugins/xep_0004.py @@ -31,6 +31,7 @@ class xep_0004(base.base_plugin): self.xmpp.add_handler("", self.handler_message_xform) def post_init(self): + base.base_plugin.post_init(self) self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') def handler_message_xform(self, xml): diff --git a/sleekxmpp/plugins/xep_0009.py b/sleekxmpp/plugins/xep_0009.py index 403f94b9..49ffac41 100644 --- a/sleekxmpp/plugins/xep_0009.py +++ b/sleekxmpp/plugins/xep_0009.py @@ -185,6 +185,7 @@ class xep_0009(base.base_plugin): self.activeCalls = [] def post_init(self): + base.base_plugin.post_init(self) self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:rpc') self.xmpp.plugin['xep_0030'].add_identity('automatition','rpc') diff --git a/sleekxmpp/plugins/xep_0050.py b/sleekxmpp/plugins/xep_0050.py index 0ca66ddb..2f356e17 100644 --- a/sleekxmpp/plugins/xep_0050.py +++ b/sleekxmpp/plugins/xep_0050.py @@ -42,6 +42,7 @@ class xep_0050(base.base_plugin): self.sd = self.xmpp.plugin['xep_0030'] def post_init(self): + base.base_plugin.post_init(self) self.sd.add_feature('http://jabber.org/protocol/commands') def addCommand(self, node, name, form, pointer=None, multi=False): diff --git a/sleekxmpp/plugins/xep_0092.py b/sleekxmpp/plugins/xep_0092.py index ca0d7e17..aeebbe0c 100644 --- a/sleekxmpp/plugins/xep_0092.py +++ b/sleekxmpp/plugins/xep_0092.py @@ -33,6 +33,7 @@ class xep_0092(base.base_plugin): self.xmpp.add_handler("" % self.xmpp.default_ns, self.report_version) def post_init(self): + base.base_plugin.post_init(self) self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:version') def report_version(self, xml): diff --git a/sleekxmpp/plugins/xep_0199.py b/sleekxmpp/plugins/xep_0199.py index 9b10b927..ccaf0b3a 100644 --- a/sleekxmpp/plugins/xep_0199.py +++ b/sleekxmpp/plugins/xep_0199.py @@ -35,6 +35,7 @@ class xep_0199(base.base_plugin): #self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True) def post_init(self): + base.base_plugin.post_init(self) self.xmpp.plugin['xep_0030'].add_feature('http://www.xmpp.org/extensions/xep-0199.html#ns') def handler_pingserver(self, xml): -- cgit v1.2.3 From fa92bc866beea0476def1f6bf258b6f3bed4c56f Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Wed, 26 May 2010 11:37:01 -0700 Subject: fixed dns unicode problem --- sleekxmpp/__init__.py | 2 +- tests/test_pubsubstanzas.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sleekxmpp/__init__.py b/sleekxmpp/__init__.py index 773f7936..263f1f99 100644 --- a/sleekxmpp/__init__.py +++ b/sleekxmpp/__init__.py @@ -96,7 +96,7 @@ class ClientXMPP(basexmpp, XMLStream): else: logging.debug("Since no address is supplied, attempting SRV lookup.") try: - answers = dns.resolver.query("_xmpp-client._tcp.%s" % self.server, "SRV") + answers = dns.resolver.query("_xmpp-client._tcp.%s" % self.server, dns.rdatatype.SRV) except dns.resolver.NXDOMAIN: logging.debug("No appropriate SRV record found. Using JID server name.") else: diff --git a/tests/test_pubsubstanzas.py b/tests/test_pubsubstanzas.py index 5353f907..55407c16 100644 --- a/tests/test_pubsubstanzas.py +++ b/tests/test_pubsubstanzas.py @@ -103,10 +103,11 @@ class testpubsubstanzas(unittest.TestCase): iq = self.ps.Iq() iq['pubsub_owner']['default'] iq['pubsub_owner']['default']['node'] = 'mynode' + iq['pubsub_owner']['default']['type'] = 'leaf' form = xep_0004.Form() form.addField('pubsub#title', ftype='text-single', value='This thing is awesome') iq['pubsub_owner']['default']['config'] = form - xmlstring = """This thing is awesome""" + xmlstring = """This thing is awesome""" iq2 = self.ps.Iq(None, self.ps.ET.fromstring(xmlstring)) iq3 = self.ps.Iq() values = iq2.getValues() -- cgit v1.2.3 From 1e3a6e1b5f06d307295be6b4ccaf75bf2a48399d Mon Sep 17 00:00:00 2001 From: Nathan Fritz Date: Wed, 26 May 2010 11:46:56 -0700 Subject: added muc room to readme --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index 670ce3cd..abc2d090 100644 --- a/README +++ b/README @@ -38,4 +38,5 @@ Contributors: Kevin Smith & Lance Stout Patches: Remko Tronçon Feel free to add fritzy@netflint.net to your roster for direct support and comments. -Join sleekxmpp-discussion@googlegroups.com / http://groups.google.com/group/sleekxmpp-discussion for discussion. +Join sleekxmpp-discussion@googlegroups.com / http://groups.google.com/group/sleekxmpp-discussion for email discussion. +Join sleek@conference.jabber.org for groupchat discussion. -- cgit v1.2.3