The ifrom parameter doesn't need special treatment.
[slixmpp.git] / examples / config_component.py
blob7a28739f8632ae755b65b605911c5ddb3e9d635d
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 """
5 SleekXMPP: The Sleek XMPP Library
6 Copyright (C) 2010 Nathanael C. Fritz
7 This file is part of SleekXMPP.
9 See the file LICENSE for copying permission.
10 """
12 import sys
13 import logging
14 import time
15 from optparse import OptionParser
17 import sleekxmpp
18 from sleekxmpp.componentxmpp import ComponentXMPP
19 from sleekxmpp.stanza.roster import Roster
20 from sleekxmpp.xmlstream import ElementBase
21 from sleekxmpp.xmlstream.stanzabase import ET, registerStanzaPlugin
23 # Python versions before 3.0 do not use UTF-8 encoding
24 # by default. To ensure that Unicode is handled properly
25 # throughout SleekXMPP, we will set the default encoding
26 # ourselves to UTF-8.
27 if sys.version_info < (3, 0):
28 reload(sys)
29 sys.setdefaultencoding('utf8')
30 else:
31 raw_input = input
34 class Config(ElementBase):
36 """
37 In order to make loading and manipulating an XML config
38 file easier, we will create a custom stanza object for
39 our config XML file contents. See the documentation
40 on stanza objects for more information on how to create
41 and use stanza objects and stanza plugins.
43 We will reuse the IQ roster query stanza to store roster
44 information since it already exists.
46 Example config XML:
47 <config xmlns="sleekxmpp:config">
48 <jid>component.localhost</jid>
49 <secret>ssshh</secret>
50 <server>localhost</server>
51 <port>8888</port>
53 <query xmlns="jabber:iq:roster">
54 <item jid="user@example.com" subscription="both" />
55 </query>
56 </config>
57 """
59 name = "config"
60 namespace = "sleekxmpp:config"
61 interfaces = set(('jid', 'secret', 'server', 'port'))
62 sub_interfaces = interfaces
65 registerStanzaPlugin(Config, Roster)
68 class ConfigComponent(ComponentXMPP):
70 """
71 A simple SleekXMPP component that uses an external XML
72 file to store its configuration data. To make testing
73 that the component works, it will also echo messages sent
74 to it.
75 """
77 def __init__(self, config):
78 """
79 Create a ConfigComponent.
81 Arguments:
82 config -- The XML contents of the config file.
83 config_file -- The XML config file object itself.
84 """
85 ComponentXMPP.__init__(self, config['jid'],
86 config['secret'],
87 config['server'],
88 config['port'])
90 # Store the roster information.
91 self.roster = config['roster']['items']
93 # The session_start event will be triggered when
94 # the component establishes its connection with the
95 # server and the XML streams are ready for use. We
96 # want to listen for this event so that we we can
97 # broadcast any needed initial presence stanzas.
98 self.add_event_handler("session_start", self.start)
100 # The message event is triggered whenever a message
101 # stanza is received. Be aware that that includes
102 # MUC messages and error messages.
103 self.add_event_handler("message", self.message)
105 def start(self, event):
107 Process the session_start event.
109 The typical action for the session_start event in a component
110 is to broadcast presence stanzas to all subscribers to the
111 component. Note that the component does not have a roster
112 provided by the XMPP server. In this case, we have possibly
113 saved a roster in the component's configuration file.
115 Since the component may use any number of JIDs, you should
116 also include the JID that is sending the presence.
118 Arguments:
119 event -- An empty dictionary. The session_start
120 event does not provide any additional
121 data.
123 for jid in self.roster:
124 if self.roster[jid]['subscription'] != 'none':
125 self.sendPresence(pfrom=self.jid, pto=jid)
127 def message(self, msg):
129 Process incoming message stanzas. Be aware that this also
130 includes MUC messages and error messages. It is usually
131 a good idea to check the messages's type before processing
132 or sending replies.
134 Since a component may send messages from any number of JIDs,
135 it is best to always include a from JID.
137 Arguments:
138 msg -- The received message stanza. See the documentation
139 for stanza objects and the Message stanza to see
140 how it may be used.
142 # The reply method will use the messages 'to' JID as the
143 # outgoing reply's 'from' JID.
144 msg.reply("Thanks for sending\n%(body)s" % msg).send()
147 if __name__ == '__main__':
148 # Setup the command line arguments.
149 optp = OptionParser()
151 # Output verbosity options.
152 optp.add_option('-q', '--quiet', help='set logging to ERROR',
153 action='store_const', dest='loglevel',
154 const=logging.ERROR, default=logging.INFO)
155 optp.add_option('-d', '--debug', help='set logging to DEBUG',
156 action='store_const', dest='loglevel',
157 const=logging.DEBUG, default=logging.INFO)
158 optp.add_option('-v', '--verbose', help='set logging to COMM',
159 action='store_const', dest='loglevel',
160 const=5, default=logging.INFO)
162 # Component name and secret options.
163 optp.add_option("-c", "--config", help="path to config file",
164 dest="config", default="config.xml")
166 opts, args = optp.parse_args()
168 # Setup logging.
169 logging.basicConfig(level=opts.loglevel,
170 format='%(levelname)-8s %(message)s')
172 # Load configuration data.
173 config_file = open(opts.config, 'r+')
174 config_data = "\n".join([line for line in config_file])
175 config = Config(xml=ET.fromstring(config_data))
176 config_file.close()
178 # Setup the ConfigComponent and register plugins. Note that while plugins
179 # may have interdependencies, the order in which you register them does
180 # not matter.
181 xmpp = ConfigComponent(config)
182 xmpp.registerPlugin('xep_0030') # Service Discovery
183 xmpp.registerPlugin('xep_0004') # Data Forms
184 xmpp.registerPlugin('xep_0060') # PubSub
185 xmpp.registerPlugin('xep_0199') # XMPP Ping
187 # Connect to the XMPP server and start processing XMPP stanzas.
188 if xmpp.connect():
189 xmpp.process(threaded=False)
190 print("Done")
191 else:
192 print("Unable to connect.")