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">
3 <title>Twisted Documentation: Writing Clients</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
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>
13 <h2>Overview<a name="auto0"/></h2>
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>
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
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>
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>
39 <h2>Protocol<a name="auto1"/></h2>
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>
46 <p>Here is a simple example:</p>
48 <pre class="python"><p class="py-linenumber">1
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>
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>)
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
67 <pre class="python"><p class="py-linenumber">1
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>
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">"Hello server, I am the client!\r\n"</span>)
78 <span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>()
81 <p>This protocol connects to the server, sends it a welcome message, and
82 then terminates the connection.</p>
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>
90 <h2>Simple, single-use clients<a name="auto2"/></h2>
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
97 <pre class="python"><p class="py-linenumber"> 1
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>
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">"MESSAGE %s\n"</span> % <span class="py-src-variable">msg</span>)
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>()
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">"Hello"</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">"This is sent in a second"</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>)
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">"localhost"</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>()
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>
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
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>
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>
169 <pre class="python"><p class="py-linenumber">1
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>
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">"localhost"</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>()
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>
190 <h2>ClientFactory<a name="auto3"/></h2>
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>
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>
207 <pre class="python"><p class="py-linenumber"> 1
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>
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>)
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>
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>()
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>
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>
249 <p>To connect this <code>EchoClientFactory</code> to a server, you could use
252 <pre class="python"><p class="py-linenumber">1
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>()
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>
264 <h3>Reconnection<a name="auto4"/></h3>
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>
270 <pre class="python"><p class="py-linenumber">1
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>
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>()
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
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.
296 Here is the <code>Echo</code> protocol implemented with
297 a <code>ReconnectingClientFactory</code>:
300 <pre class="python"><p class="py-linenumber"> 1
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>
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>)
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>
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>()
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>)
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>)
352 <h2>A Higher-Level Example: ircLogBot<a name="auto5"/></h2>
354 <h3>Overview of ircLogBot<a name="auto6"/></h3>
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>
360 <div class="py-listing"><pre><p class="py-linenumber"> 1
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>
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>
503 <span class="py-src-keyword">class</span> <span class="py-src-identifier">MessageLogger</span>:
504 <span class="py-src-string">"""
505 An independent logger class (because separation of application
506 and protocol logic is a good thing).
507 """</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>
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">"""Write a message to the file."""</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">"[%H:%M:%S]"</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>()
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>()
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">"""A logging IRC bot."""</span>
524 <span class="py-src-variable">nickname</span> = <span class="py-src-string">"twistedbot"</span>
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">"a"</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">"[connected at %s]"</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>())))
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">"[disconnected at %s]"</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>()
539 <span class="py-src-comment"># callbacks for events</span>
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">"""Called when bot has succesfully signed on to server."""</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>)
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">"""This will get called when the bot joins the channel."""</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">"[I have joined %s]"</span> % <span class="py-src-variable">channel</span>)
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">"""This will get called when the bot receives a message."""</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">"<%s> %s"</span> % (<span class="py-src-variable">user</span>, <span class="py-src-variable">msg</span>))
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">"It isn't nice to whisper! Play nice with the group."</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>
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">":"</span>):
562 <span class="py-src-variable">msg</span> = <span class="py-src-string">"%s: I am a log bot"</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">"<%s> %s"</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">nickname</span>, <span class="py-src-variable">msg</span>))
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">"""This will get called when the bot sees someone do an action."""</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">"* %s %s"</span> % (<span class="py-src-variable">user</span>, <span class="py-src-variable">msg</span>))
571 <span class="py-src-comment"># irc callbacks</span>
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">"""Called when an IRC user changes their nickname."""</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">"%s is now known as %s"</span> % (<span class="py-src-variable">old_nick</span>, <span class="py-src-variable">new_nick</span>))
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">"""
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 """</span>
587 <span class="py-src-keyword">return</span> <span class="py-src-variable">nickname</span> + <span class="py-src-string">'^'</span>
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">"""A factory for LogBots.
594 A new protocol instance will be created each time we connect to the server.
595 """</span>
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>
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>
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">"""If we get disconnected, reconnect to server."""</span>
608 <span class="py-src-variable">connector</span>.<span class="py-src-variable">connect</span>()
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">"connection failed:"</span>, <span class="py-src-variable">reason</span>
612 <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
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>)
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>])
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">"irc.freenode.net"</span>, <span class="py-src-number">6667</span>, <span class="py-src-variable">f</span>)
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>
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>
634 <h3>Persistent Data in the Factory<a name="auto7"/></h3>
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>
641 <pre class="python"><p class="py-linenumber"> 1
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>
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>):
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">"a"</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">"[connected at %s]"</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>())))
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>)
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>):
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>
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>
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>
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
704 <pre class="python"><p class="py-linenumber">1
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>
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>
718 <h2>Further Reading<a name="auto8"/></h2>
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>
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>
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>
738 <p><a href="index.html">Index</a></p>
739 <span class="version">Version: 12.1.0</span>