Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / core / howto / clients.html
1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
2   <head>
3 <title>Twisted Documentation: Writing Clients</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
5   </head>
6
7   <body bgcolor="white">
8     <h1 class="title">Writing Clients</h1>
9     <div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Protocol</a></li><li><a href="#auto2">Simple, single-use clients</a></li><li><a href="#auto3">ClientFactory</a></li><ul><li><a href="#auto4">Reconnection</a></li></ul><li><a href="#auto5">A Higher-Level Example: ircLogBot</a></li><ul><li><a href="#auto6">Overview of ircLogBot</a></li><li><a href="#auto7">Persistent Data in the Factory</a></li></ul><li><a href="#auto8">Further Reading</a></li></ol></div>
10     <div class="content">
11     <span/>
12
13     <h2>Overview<a name="auto0"/></h2>
14
15     <p>Twisted is a framework designed to be very flexible, and let you write
16     powerful clients. The cost of this flexibility is a few layers in the way
17     to writing your client. This document covers creating clients that can be
18     used for TCP, SSL and Unix sockets. UDP is covered <a href="udp.html" shape="rect">in
19     a different document</a>.</p>
20
21     <p>At the base, the place where you actually implement the protocol parsing
22     and handling, is the <code>Protocol</code> class. This class will usually be
23     descended
24     from <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.Protocol.html" title="twisted.internet.protocol.Protocol">twisted.internet.protocol.Protocol</a></code>. Most
25     protocol handlers inherit either from this class or from one of its
26     convenience children. An instance of the protocol class will be instantiated
27     when you connect to the server and will go away when the connection is
28     finished.  This means that persistent configuration is not saved in the
29     <code>Protocol</code>.</p>
30
31     <p>The persistent configuration is kept in a <code>Factory</code>
32     class, which usually inherits from <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.Factory.html" title="twisted.internet.protocol.Factory">twisted.internet.protocol.Factory</a></code>
33     (or <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientFactory.html" title="twisted.internet.protocol.ClientFactory">twisted.internet.protocol.ClientFactory</a></code>: see
34     below). The default factory class just instantiates the <code>Protocol</code>
35     and then sets the protocol's <code>factory</code> attribute to point to
36     itself (the factory). This lets the <code>Protocol</code> access, and
37     possibly modify, the persistent configuration.</p>
38
39     <h2>Protocol<a name="auto1"/></h2>
40
41     <p>As mentioned above, this and auxiliary classes and functions are where
42     most of the code is. A Twisted protocol handles data in an asynchronous
43     manner. This means that the protocol never waits for an event, but rather
44     responds to events as they arrive from the network.</p>
45
46     <p>Here is a simple example:</p>
47
48     <pre class="python"><p class="py-linenumber">1
49 2
50 3
51 4
52 5
53 6
54 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Protocol</span>
55 <span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
56
57 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
58     <span class="py-src-keyword">def</span> <span class="py-src-identifier">dataReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>):
59         <span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
60 </pre>
61
62     <p>This is one of the simplest protocols.  It just writes whatever it reads
63     from the connection to standard output. There are many events it does not
64     respond to. Here is an example of a <code>Protocol</code> responding to
65     another event:</p>
66
67     <pre class="python"><p class="py-linenumber">1
68 2
69 3
70 4
71 5
72 6
73 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Protocol</span>
74
75 <span class="py-src-keyword">class</span> <span class="py-src-identifier">WelcomeMessage</span>(<span class="py-src-parameter">Protocol</span>):
76     <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
77         <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;Hello server, I am the client!\r\n&quot;</span>)
78         <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>()
79 </pre>
80
81     <p>This protocol connects to the server, sends it a welcome message, and
82     then terminates the connection.</p>
83
84     <p>The <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.BaseProtocol.connectionMade.html" title="twisted.internet.protocol.BaseProtocol.connectionMade">connectionMade</a></code> event is
85     usually where set up of the <code>Protocol</code> object happens, as well as
86     any initial greetings (as in the
87     <code>WelcomeMessage</code> protocol above). Any tearing down of
88     <code>Protocol</code>-specific objects is done in <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.Protocol.connectionLost.html" title="twisted.internet.protocol.Protocol.connectionLost">connectionLost</a></code>.</p>
89
90     <h2>Simple, single-use clients<a name="auto2"/></h2>
91
92     <p>In many cases, the protocol only needs to connect to the server once, and
93     the code just wants to get a connected instance of the protocol. In those
94     cases <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.endpoints.html" title="twisted.internet.endpoints">twisted.internet.endpoints</a></code> provides the
95     appropriate API.</p>
96
97     <pre class="python"><p class="py-linenumber"> 1
98  2
99  3
100  4
101  5
102  6
103  7
104  8
105  9
106 10
107 11
108 12
109 13
110 14
111 15
112 16
113 17
114 18
115 19
116 20
117 21
118 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
119 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Factory</span>, <span class="py-src-variable">Protocol</span>
120 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">endpoints</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">TCP4ClientEndpoint</span>
121
122 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Greeter</span>(<span class="py-src-parameter">Protocol</span>):
123     <span class="py-src-keyword">def</span> <span class="py-src-identifier">sendMessage</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">msg</span>):
124         <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">&quot;MESSAGE %s\n&quot;</span> % <span class="py-src-variable">msg</span>)
125
126 <span class="py-src-keyword">class</span> <span class="py-src-identifier">GreeterFactory</span>(<span class="py-src-parameter">Factory</span>):
127     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
128         <span class="py-src-keyword">return</span> <span class="py-src-variable">Greeter</span>()
129
130 <span class="py-src-keyword">def</span> <span class="py-src-identifier">gotProtocol</span>(<span class="py-src-parameter">p</span>):
131     <span class="py-src-variable">p</span>.<span class="py-src-variable">sendMessage</span>(<span class="py-src-string">&quot;Hello&quot;</span>)
132     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">callLater</span>(<span class="py-src-number">1</span>, <span class="py-src-variable">p</span>.<span class="py-src-variable">sendMessage</span>, <span class="py-src-string">&quot;This is sent in a second&quot;</span>)
133     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">callLater</span>(<span class="py-src-number">2</span>, <span class="py-src-variable">p</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>)
134
135 <span class="py-src-variable">point</span> = <span class="py-src-variable">TCP4ClientEndpoint</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">1234</span>)
136 <span class="py-src-variable">d</span> = <span class="py-src-variable">point</span>.<span class="py-src-variable">connect</span>(<span class="py-src-variable">GreeterFactory</span>())
137 <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">gotProtocol</span>)
138 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
139 </pre>
140
141     <p>Regardless of the type of client endpoint, the way to set up a new
142     connection is simply to call the <code>connect</code> method on it and pass
143     in a factory.  This means it's easy to change the mechanism you're using to
144     connect, without changing the rest of your program.  For example, to run
145     the greeter example over SSL, the only change required is to instantiate an
146     <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.endpoints.SSL4ClientEndpoint.html" title="twisted.internet.endpoints.SSL4ClientEndpoint">SSL4ClientEndpoint</a></code> instead of a
147     <code>TCP4ClientEndpoint</code>.  To take advantage of this, functions and
148     methods which initiates a new connection should generally accept an
149     endpoint as an argument and let the caller construct it, rather than taking
150     arguments like 'host' and 'port' and constructing its own before calling
151     <code>connect</code>.</p>
152
153     <p>For more information on different ways you can make outgoing connections
154     to different types of endpoints, as well as parsing strings into endpoints,
155     see <a href="endpoints.html" shape="rect">the documentation for the endpoints
156     API</a>.</p>
157
158     <div class="note"><strong>Note: </strong>If you've used <code>ClientFactory</code> before,
159     make sure you remember that the <code>connect</code> method takes a
160     <code>Factory</code>, not a <code>ClientFactory</code>.  Even if you pass a
161     <code>ClientFactory</code> to <code>endpoint.connect</code>, its
162     <code>clientConnectionFailed</code> and <code>clientConnectionLost</code>
163     methods will not be called.</div>
164
165     <p>You may come across code using <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientCreator.html" title="twisted.internet.protocol.ClientCreator">ClientCreator</a></code>, an older API which is not as flexible as
166     the endpoint API.  Rather than calling <code>connect</code> on an endpoint,
167     such code will look like this:</p>
168
169     <pre class="python"><p class="py-linenumber">1
170 2
171 3
172 4
173 5
174 6
175 7
176 8
177 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">ClientCreator</span>
178
179 ...
180
181 <span class="py-src-variable">creator</span> = <span class="py-src-variable">ClientCreator</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-variable">Greeter</span>)
182 <span class="py-src-variable">d</span> = <span class="py-src-variable">creator</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">1234</span>)
183 <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">gotProtocol</span>)
184 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
185 </pre>
186
187     <p>In general, the endpoint API should be preferred in new code, as it lets
188     the caller select the method of connecting.</p>
189
190     <h2>ClientFactory<a name="auto3"/></h2>
191
192     <p>Still, there's plenty of code out there that uses lower-level APIs, and
193     a few features (such as automatic reconnection) have not been
194     re-implemented with endpoints yet, so in some cases they may be more
195     convenient to use.</p>
196
197     <p>To use the lower-level connection APIs, you will need to call one of the
198     <em>reactor.connect*</em> methods directly.  For these cases, you need a
199     <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientFactory.html" title="twisted.internet.protocol.ClientFactory">ClientFactory</a></code>.
200     The <code>ClientFactory</code> is in charge of creating the
201     <code>Protocol</code> and also receives events relating to the connection
202     state. This allows it to do things like reconnect in the event of a
203     connection error. Here is an example of a simple <code>ClientFactory</code>
204     that uses the <code>Echo</code> protocol (above) and also prints what state
205     the connection is in.</p>
206
207     <pre class="python"><p class="py-linenumber"> 1
208  2
209  3
210  4
211  5
212  6
213  7
214  8
215  9
216 10
217 11
218 12
219 13
220 14
221 15
222 16
223 17
224 18
225 19
226 20
227 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Protocol</span>, <span class="py-src-variable">ClientFactory</span>
228 <span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
229
230 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
231     <span class="py-src-keyword">def</span> <span class="py-src-identifier">dataReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>):
232         <span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
233
234 <span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ClientFactory</span>):
235     <span class="py-src-keyword">def</span> <span class="py-src-identifier">startedConnecting</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>):
236         <span class="py-src-keyword">print</span> <span class="py-src-string">'Started to connect.'</span>
237
238     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
239         <span class="py-src-keyword">print</span> <span class="py-src-string">'Connected.'</span>
240         <span class="py-src-keyword">return</span> <span class="py-src-variable">Echo</span>()
241
242     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionLost</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
243         <span class="py-src-keyword">print</span> <span class="py-src-string">'Lost connection.  Reason:'</span>, <span class="py-src-variable">reason</span>
244
245     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionFailed</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
246         <span class="py-src-keyword">print</span> <span class="py-src-string">'Connection failed. Reason:'</span>, <span class="py-src-variable">reason</span>
247 </pre>
248
249     <p>To connect this <code>EchoClientFactory</code> to a server, you could use
250     this code:</p>
251
252     <pre class="python"><p class="py-linenumber">1
253 2
254 3
255 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
256 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-variable">host</span>, <span class="py-src-variable">port</span>, <span class="py-src-variable">EchoClientFactory</span>())
257 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
258 </pre>
259
260     <p>Note that <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientFactory.clientConnectionFailed.html" title="twisted.internet.protocol.ClientFactory.clientConnectionFailed">clientConnectionFailed</a></code>
261     is called when a connection could not be established, and that <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ClientFactory.clientConnectionLost.html" title="twisted.internet.protocol.ClientFactory.clientConnectionLost">clientConnectionLost</a></code>
262     is called when a connection was made and then disconnected.</p>
263
264     <h3>Reconnection<a name="auto4"/></h3>
265
266     <p>Often, the connection of a client will be lost unintentionally due to
267     network problems. One way to reconnect after a disconnection would be to
268     call <code>connector.connect()</code> when the connection is lost:</p>
269
270     <pre class="python"><p class="py-linenumber">1
271 2
272 3
273 4
274 5
275 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">ClientFactory</span>
276
277 <span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ClientFactory</span>):
278     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionLost</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
279         <span class="py-src-variable">connector</span>.<span class="py-src-variable">connect</span>()
280 </pre>
281
282     <p>The connector passed as the first argument is the interface between a
283     connection and a protocol. When the connection fails and the factory
284     receives the <code>clientConnectionLost</code> event, the factory can
285     call <code>connector.connect()</code> to start the connection over again
286     from scratch.</p>
287
288     <p>
289     However, most programs that want this functionality should
290     implement <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.ReconnectingClientFactory.html" title="twisted.internet.protocol.ReconnectingClientFactory">ReconnectingClientFactory</a></code> instead,
291     which tries to reconnect if a connection is lost or fails and which
292     exponentially delays repeated reconnect attempts.
293     </p>
294
295     <p>
296     Here is the <code>Echo</code> protocol implemented with
297     a <code>ReconnectingClientFactory</code>:
298     </p>
299
300     <pre class="python"><p class="py-linenumber"> 1
301  2
302  3
303  4
304  5
305  6
306  7
307  8
308  9
309 10
310 11
311 12
312 13
313 14
314 15
315 16
316 17
317 18
318 19
319 20
320 21
321 22
322 23
323 24
324 25
325 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span>.<span class="py-src-variable">protocol</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Protocol</span>, <span class="py-src-variable">ReconnectingClientFactory</span>
326 <span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
327
328 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
329     <span class="py-src-keyword">def</span> <span class="py-src-identifier">dataReceived</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">data</span>):
330         <span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
331
332 <span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ReconnectingClientFactory</span>):
333     <span class="py-src-keyword">def</span> <span class="py-src-identifier">startedConnecting</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>):
334         <span class="py-src-keyword">print</span> <span class="py-src-string">'Started to connect.'</span>
335
336     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
337         <span class="py-src-keyword">print</span> <span class="py-src-string">'Connected.'</span>
338         <span class="py-src-keyword">print</span> <span class="py-src-string">'Resetting reconnection delay'</span>
339         <span class="py-src-variable">self</span>.<span class="py-src-variable">resetDelay</span>()
340         <span class="py-src-keyword">return</span> <span class="py-src-variable">Echo</span>()
341
342     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionLost</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
343         <span class="py-src-keyword">print</span> <span class="py-src-string">'Lost connection.  Reason:'</span>, <span class="py-src-variable">reason</span>
344         <span class="py-src-variable">ReconnectingClientFactory</span>.<span class="py-src-variable">clientConnectionLost</span>(<span class="py-src-variable">self</span>, <span class="py-src-variable">connector</span>, <span class="py-src-variable">reason</span>)
345
346     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionFailed</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
347         <span class="py-src-keyword">print</span> <span class="py-src-string">'Connection failed. Reason:'</span>, <span class="py-src-variable">reason</span>
348         <span class="py-src-variable">ReconnectingClientFactory</span>.<span class="py-src-variable">clientConnectionFailed</span>(<span class="py-src-variable">self</span>, <span class="py-src-variable">connector</span>,
349                                                          <span class="py-src-variable">reason</span>)
350 </pre>
351
352     <h2>A Higher-Level Example: ircLogBot<a name="auto5"/></h2>
353
354     <h3>Overview of ircLogBot<a name="auto6"/></h3>
355
356     <p>The clients so far have been fairly simple.  A more complicated example
357     comes with Twisted Words in the <code>doc/words/examples</code>
358     directory.</p>
359
360     <div class="py-listing"><pre><p class="py-linenumber">  1
361   2
362   3
363   4
364   5
365   6
366   7
367   8
368   9
369  10
370  11
371  12
372  13
373  14
374  15
375  16
376  17
377  18
378  19
379  20
380  21
381  22
382  23
383  24
384  25
385  26
386  27
387  28
388  29
389  30
390  31
391  32
392  33
393  34
394  35
395  36
396  37
397  38
398  39
399  40
400  41
401  42
402  43
403  44
404  45
405  46
406  47
407  48
408  49
409  50
410  51
411  52
412  53
413  54
414  55
415  56
416  57
417  58
418  59
419  60
420  61
421  62
422  63
423  64
424  65
425  66
426  67
427  68
428  69
429  70
430  71
431  72
432  73
433  74
434  75
435  76
436  77
437  78
438  79
439  80
440  81
441  82
442  83
443  84
444  85
445  86
446  87
447  88
448  89
449  90
450  91
451  92
452  93
453  94
454  95
455  96
456  97
457  98
458  99
459 100
460 101
461 102
462 103
463 104
464 105
465 106
466 107
467 108
468 109
469 110
470 111
471 112
472 113
473 114
474 115
475 116
476 117
477 118
478 119
479 120
480 121
481 122
482 123
483 124
484 125
485 126
486 127
487 128
488 129
489 130
490 131
491 132
492 133
493 134
494 </p><span class="py-src-comment"># twisted imports</span>
495 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">words</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">irc</span>
496 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>, <span class="py-src-variable">protocol</span>
497 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
498
499 <span class="py-src-comment"># system imports</span>
500 <span class="py-src-keyword">import</span> <span class="py-src-variable">time</span>, <span class="py-src-variable">sys</span>
501
502
503 <span class="py-src-keyword">class</span> <span class="py-src-identifier">MessageLogger</span>:
504     <span class="py-src-string">&quot;&quot;&quot;
505     An independent logger class (because separation of application
506     and protocol logic is a good thing).
507     &quot;&quot;&quot;</span>
508     <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">file</span>):
509         <span class="py-src-variable">self</span>.<span class="py-src-variable">file</span> = <span class="py-src-variable">file</span>
510
511     <span class="py-src-keyword">def</span> <span class="py-src-identifier">log</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
512         <span class="py-src-string">&quot;&quot;&quot;Write a message to the file.&quot;&quot;&quot;</span>
513         <span class="py-src-variable">timestamp</span> = <span class="py-src-variable">time</span>.<span class="py-src-variable">strftime</span>(<span class="py-src-string">&quot;[%H:%M:%S]&quot;</span>, <span class="py-src-variable">time</span>.<span class="py-src-variable">localtime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">time</span>()))
514         <span class="py-src-variable">self</span>.<span class="py-src-variable">file</span>.<span class="py-src-variable">write</span>(<span class="py-src-string">'%s %s\n'</span> % (<span class="py-src-variable">timestamp</span>, <span class="py-src-variable">message</span>))
515         <span class="py-src-variable">self</span>.<span class="py-src-variable">file</span>.<span class="py-src-variable">flush</span>()
516
517     <span class="py-src-keyword">def</span> <span class="py-src-identifier">close</span>(<span class="py-src-parameter">self</span>):
518         <span class="py-src-variable">self</span>.<span class="py-src-variable">file</span>.<span class="py-src-variable">close</span>()
519
520
521 <span class="py-src-keyword">class</span> <span class="py-src-identifier">LogBot</span>(<span class="py-src-parameter">irc</span>.<span class="py-src-parameter">IRCClient</span>):
522     <span class="py-src-string">&quot;&quot;&quot;A logging IRC bot.&quot;&quot;&quot;</span>
523
524     <span class="py-src-variable">nickname</span> = <span class="py-src-string">&quot;twistedbot&quot;</span>
525
526     <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
527         <span class="py-src-variable">irc</span>.<span class="py-src-variable">IRCClient</span>.<span class="py-src-variable">connectionMade</span>(<span class="py-src-variable">self</span>)
528         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span> = <span class="py-src-variable">MessageLogger</span>(<span class="py-src-variable">open</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">factory</span>.<span class="py-src-variable">filename</span>, <span class="py-src-string">&quot;a&quot;</span>))
529         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;[connected at %s]&quot;</span> %
530                         <span class="py-src-variable">time</span>.<span class="py-src-variable">asctime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">localtime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">time</span>())))
531
532     <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionLost</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">reason</span>):
533         <span class="py-src-variable">irc</span>.<span class="py-src-variable">IRCClient</span>.<span class="py-src-variable">connectionLost</span>(<span class="py-src-variable">self</span>, <span class="py-src-variable">reason</span>)
534         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;[disconnected at %s]&quot;</span> %
535                         <span class="py-src-variable">time</span>.<span class="py-src-variable">asctime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">localtime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">time</span>())))
536         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">close</span>()
537
538
539     <span class="py-src-comment"># callbacks for events</span>
540
541     <span class="py-src-keyword">def</span> <span class="py-src-identifier">signedOn</span>(<span class="py-src-parameter">self</span>):
542         <span class="py-src-string">&quot;&quot;&quot;Called when bot has succesfully signed on to server.&quot;&quot;&quot;</span>
543         <span class="py-src-variable">self</span>.<span class="py-src-variable">join</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">factory</span>.<span class="py-src-variable">channel</span>)
544
545     <span class="py-src-keyword">def</span> <span class="py-src-identifier">joined</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">channel</span>):
546         <span class="py-src-string">&quot;&quot;&quot;This will get called when the bot joins the channel.&quot;&quot;&quot;</span>
547         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;[I have joined %s]&quot;</span> % <span class="py-src-variable">channel</span>)
548
549     <span class="py-src-keyword">def</span> <span class="py-src-identifier">privmsg</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>, <span class="py-src-parameter">channel</span>, <span class="py-src-parameter">msg</span>):
550         <span class="py-src-string">&quot;&quot;&quot;This will get called when the bot receives a message.&quot;&quot;&quot;</span>
551         <span class="py-src-variable">user</span> = <span class="py-src-variable">user</span>.<span class="py-src-variable">split</span>(<span class="py-src-string">'!'</span>, <span class="py-src-number">1</span>)[<span class="py-src-number">0</span>]
552         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;&lt;%s&gt; %s&quot;</span> % (<span class="py-src-variable">user</span>, <span class="py-src-variable">msg</span>))
553
554         <span class="py-src-comment"># Check to see if they're sending me a private message</span>
555         <span class="py-src-keyword">if</span> <span class="py-src-variable">channel</span> == <span class="py-src-variable">self</span>.<span class="py-src-variable">nickname</span>:
556             <span class="py-src-variable">msg</span> = <span class="py-src-string">&quot;It isn't nice to whisper!  Play nice with the group.&quot;</span>
557             <span class="py-src-variable">self</span>.<span class="py-src-variable">msg</span>(<span class="py-src-variable">user</span>, <span class="py-src-variable">msg</span>)
558             <span class="py-src-keyword">return</span>
559
560         <span class="py-src-comment"># Otherwise check to see if it is a message directed at me</span>
561         <span class="py-src-keyword">if</span> <span class="py-src-variable">msg</span>.<span class="py-src-variable">startswith</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">nickname</span> + <span class="py-src-string">&quot;:&quot;</span>):
562             <span class="py-src-variable">msg</span> = <span class="py-src-string">&quot;%s: I am a log bot&quot;</span> % <span class="py-src-variable">user</span>
563             <span class="py-src-variable">self</span>.<span class="py-src-variable">msg</span>(<span class="py-src-variable">channel</span>, <span class="py-src-variable">msg</span>)
564             <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;&lt;%s&gt; %s&quot;</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">nickname</span>, <span class="py-src-variable">msg</span>))
565
566     <span class="py-src-keyword">def</span> <span class="py-src-identifier">action</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>, <span class="py-src-parameter">channel</span>, <span class="py-src-parameter">msg</span>):
567         <span class="py-src-string">&quot;&quot;&quot;This will get called when the bot sees someone do an action.&quot;&quot;&quot;</span>
568         <span class="py-src-variable">user</span> = <span class="py-src-variable">user</span>.<span class="py-src-variable">split</span>(<span class="py-src-string">'!'</span>, <span class="py-src-number">1</span>)[<span class="py-src-number">0</span>]
569         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;* %s %s&quot;</span> % (<span class="py-src-variable">user</span>, <span class="py-src-variable">msg</span>))
570
571     <span class="py-src-comment"># irc callbacks</span>
572
573     <span class="py-src-keyword">def</span> <span class="py-src-identifier">irc_NICK</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">prefix</span>, <span class="py-src-parameter">params</span>):
574         <span class="py-src-string">&quot;&quot;&quot;Called when an IRC user changes their nickname.&quot;&quot;&quot;</span>
575         <span class="py-src-variable">old_nick</span> = <span class="py-src-variable">prefix</span>.<span class="py-src-variable">split</span>(<span class="py-src-string">'!'</span>)[<span class="py-src-number">0</span>]
576         <span class="py-src-variable">new_nick</span> = <span class="py-src-variable">params</span>[<span class="py-src-number">0</span>]
577         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;%s is now known as %s&quot;</span> % (<span class="py-src-variable">old_nick</span>, <span class="py-src-variable">new_nick</span>))
578
579
580     <span class="py-src-comment"># For fun, override the method that determines how a nickname is changed on</span>
581     <span class="py-src-comment"># collisions. The default method appends an underscore.</span>
582     <span class="py-src-keyword">def</span> <span class="py-src-identifier">alterCollidedNick</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">nickname</span>):
583         <span class="py-src-string">&quot;&quot;&quot;
584         Generate an altered version of a nickname that caused a collision in an
585         effort to create an unused related name for subsequent registration.
586         &quot;&quot;&quot;</span>
587         <span class="py-src-keyword">return</span> <span class="py-src-variable">nickname</span> + <span class="py-src-string">'^'</span>
588
589
590
591 <span class="py-src-keyword">class</span> <span class="py-src-identifier">LogBotFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">ClientFactory</span>):
592     <span class="py-src-string">&quot;&quot;&quot;A factory for LogBots.
593
594     A new protocol instance will be created each time we connect to the server.
595     &quot;&quot;&quot;</span>
596
597     <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">channel</span>, <span class="py-src-parameter">filename</span>):
598         <span class="py-src-variable">self</span>.<span class="py-src-variable">channel</span> = <span class="py-src-variable">channel</span>
599         <span class="py-src-variable">self</span>.<span class="py-src-variable">filename</span> = <span class="py-src-variable">filename</span>
600
601     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
602         <span class="py-src-variable">p</span> = <span class="py-src-variable">LogBot</span>()
603         <span class="py-src-variable">p</span>.<span class="py-src-variable">factory</span> = <span class="py-src-variable">self</span>
604         <span class="py-src-keyword">return</span> <span class="py-src-variable">p</span>
605
606     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionLost</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
607         <span class="py-src-string">&quot;&quot;&quot;If we get disconnected, reconnect to server.&quot;&quot;&quot;</span>
608         <span class="py-src-variable">connector</span>.<span class="py-src-variable">connect</span>()
609
610     <span class="py-src-keyword">def</span> <span class="py-src-identifier">clientConnectionFailed</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">connector</span>, <span class="py-src-parameter">reason</span>):
611         <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;connection failed:&quot;</span>, <span class="py-src-variable">reason</span>
612         <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
613
614
615 <span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
616     <span class="py-src-comment"># initialize logging</span>
617     <span class="py-src-variable">log</span>.<span class="py-src-variable">startLogging</span>(<span class="py-src-variable">sys</span>.<span class="py-src-variable">stdout</span>)
618
619     <span class="py-src-comment"># create factory protocol and application</span>
620     <span class="py-src-variable">f</span> = <span class="py-src-variable">LogBotFactory</span>(<span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">1</span>], <span class="py-src-variable">sys</span>.<span class="py-src-variable">argv</span>[<span class="py-src-number">2</span>])
621
622     <span class="py-src-comment"># connect factory to this host and port</span>
623     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;irc.freenode.net&quot;</span>, <span class="py-src-number">6667</span>, <span class="py-src-variable">f</span>)
624
625     <span class="py-src-comment"># run bot</span>
626     <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
627 </pre><div class="caption">Source listing - <a href="../../words/examples/ircLogBot.py"><span class="filename">../../words/examples/ircLogBot.py</span></a></div></div>
628
629     <p><code>ircLogBot.py</code> connects to an IRC server, joins a channel, and
630     logs all traffic on it to a file. It demonstrates some of the
631     connection-level logic of reconnecting on a lost connection, as well as
632     storing persistent data in the <code>Factory</code>.</p>
633
634     <h3>Persistent Data in the Factory<a name="auto7"/></h3>
635
636     <p>Since the <code>Protocol</code> instance is recreated each time the
637     connection is made, the client needs some way to keep track of data that
638     should be persisted. In the case of the logging bot, it needs to know which
639     channel it is logging, and where to log it.</p>
640
641     <pre class="python"><p class="py-linenumber"> 1
642  2
643  3
644  4
645  5
646  6
647  7
648  8
649  9
650 10
651 11
652 12
653 13
654 14
655 15
656 16
657 17
658 18
659 19
660 20
661 21
662 22
663 23
664 24
665 25
666 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">words</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">irc</span>
667 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>
668
669 <span class="py-src-keyword">class</span> <span class="py-src-identifier">LogBot</span>(<span class="py-src-parameter">irc</span>.<span class="py-src-parameter">IRCClient</span>):
670
671     <span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
672         <span class="py-src-variable">irc</span>.<span class="py-src-variable">IRCClient</span>.<span class="py-src-variable">connectionMade</span>(<span class="py-src-variable">self</span>)
673         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span> = <span class="py-src-variable">MessageLogger</span>(<span class="py-src-variable">open</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">factory</span>.<span class="py-src-variable">filename</span>, <span class="py-src-string">&quot;a&quot;</span>))
674         <span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">log</span>(<span class="py-src-string">&quot;[connected at %s]&quot;</span> %
675                         <span class="py-src-variable">time</span>.<span class="py-src-variable">asctime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">localtime</span>(<span class="py-src-variable">time</span>.<span class="py-src-variable">time</span>())))
676
677     <span class="py-src-keyword">def</span> <span class="py-src-identifier">signedOn</span>(<span class="py-src-parameter">self</span>):
678         <span class="py-src-variable">self</span>.<span class="py-src-variable">join</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">factory</span>.<span class="py-src-variable">channel</span>)
679
680
681 <span class="py-src-keyword">class</span> <span class="py-src-identifier">LogBotFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">ClientFactory</span>):
682
683     <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">channel</span>, <span class="py-src-parameter">filename</span>):
684         <span class="py-src-variable">self</span>.<span class="py-src-variable">channel</span> = <span class="py-src-variable">channel</span>
685         <span class="py-src-variable">self</span>.<span class="py-src-variable">filename</span> = <span class="py-src-variable">filename</span>
686
687     <span class="py-src-keyword">def</span> <span class="py-src-identifier">buildProtocol</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">addr</span>):
688         <span class="py-src-variable">p</span> = <span class="py-src-variable">LogBot</span>()
689         <span class="py-src-variable">p</span>.<span class="py-src-variable">factory</span> = <span class="py-src-variable">self</span>
690         <span class="py-src-keyword">return</span> <span class="py-src-variable">p</span>
691 </pre>
692
693     <p>When the protocol is created, it gets a reference to the factory as
694     <code>self.factory</code>. It can then access attributes of the factory in
695     its logic. In the case of <code>LogBot</code>, it opens the file and
696     connects to the channel stored in the factory.</p>
697
698     <p>Factories have a default implementation of <code>buildProtocol</code>
699     that does the same thing the example above does, using
700     the <code>protocol</code> attribute of the factory to create the protocol
701     instance. In the example above, the factory could be rewritten to look
702     like this:</p>
703
704     <pre class="python"><p class="py-linenumber">1
705 2
706 3
707 4
708 5
709 6
710 </p><span class="py-src-keyword">class</span> <span class="py-src-identifier">LogBotFactory</span>(<span class="py-src-parameter">protocol</span>.<span class="py-src-parameter">ClientFactory</span>):
711     <span class="py-src-variable">protocol</span> = <span class="py-src-variable">LogBot</span>
712
713     <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">channel</span>, <span class="py-src-parameter">filename</span>):
714         <span class="py-src-variable">self</span>.<span class="py-src-variable">channel</span> = <span class="py-src-variable">channel</span>
715         <span class="py-src-variable">self</span>.<span class="py-src-variable">filename</span> = <span class="py-src-variable">filename</span>
716 </pre>
717
718     <h2>Further Reading<a name="auto8"/></h2>
719
720     <p>The <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.protocol.Protocol.html" title="twisted.internet.protocol.Protocol">Protocol</a></code>
721     class used throughout this document is a base implementation
722     of <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.interfaces.IProtocol.html" title="twisted.internet.interfaces.IProtocol">IProtocol</a></code>
723     used in most Twisted applications for convenience. To learn about the
724     complete <code>IProtocol</code> interface, see the API documentation for
725     <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.interfaces.IProtocol.html" title="twisted.internet.interfaces.IProtocol">IProtocol</a></code>.</p>
726
727     <p>The <code>transport</code> attribute used in some examples in this
728     document provides the <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.interfaces.ITCPTransport.html" title="twisted.internet.interfaces.ITCPTransport">ITCPTransport</a></code> interface. To learn
729     about the complete interface, see the API documentation
730     for <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.internet.interfaces.ITCPTransport.html" title="twisted.internet.interfaces.ITCPTransport">ITCPTransport</a></code>.</p>
731
732     <p>Interface classes are a way of specifying what methods and attributes an
733     object has and how they behave. See the <a href="components.html" shape="rect">
734     Components: Interfaces and Adapters</a> document for more information on
735     using interfaces in Twisted.</p>
736   </div>
737
738     <p><a href="index.html">Index</a></p>
739     <span class="version">Version: 12.1.0</span>
740   </body>
741 </html>