1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Writing Clients</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Writing Clients</h1><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></ol></div><div class="content"><span></span><h2>Overview<a name="auto0"></a></h2><p>Twisted is a framework designed to be very flexible, and let you write
powerful clients. The cost of this flexibility is a few layers in the way
to writing your client. This document covers creating clients that can be
used for TCP, SSL and Unix sockets, UDP is covered <a href="udp.html">in
a different document</a>.</p><p>At the base, the place where you actually implement the protocol parsing
and handling, is the Protocol class. This class will usually be decended
from <code class="API">twisted.internet.protocol.Protocol</code>. Most
protocol handlers inherit either from this class or from one of its
convenience children. An instance of the protocol class will be
instantiated when you connect to the server, and will go away when the
connection is finished. This means that persistent configuration is not
saved in the Protocol.</p><p>The persistent configuration is kept in a Factory class, which usually
inherits from <code class="API">twisted.internet.protocol.ClientFactory</code>. The default
factory class just instantiate the Protocol, and then sets on it an
attribute called <code>factory</code> which points to itself. This let
the Protocol access, and possibly modify, the persistent
configuration.</p><h2>Protocol<a name="auto1"></a></h2><p>As mentioned above, this, and auxiliary classes and functions, is where
most of the code is. A Twisted protocol handles data in an asynchronous
manner. What this means is that the protocol never waits for an event, but
rather responds to events as they arrive from the network.</p><p>Here is a simple example:</p><pre class="python">
<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-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
<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>):
<span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
</pre><p>This is one of the simplest protocols. It simply writes to standard
output whatever it reads from the connection. There are many events it
does not respond to. Here is an example of a Protocol responding to
another event.</p><pre class="python">
<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-keyword">class</span> <span class="py-src-identifier">WelcomeMessage</span>(<span class="py-src-parameter">Protocol</span>):
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
<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>)
<span class="py-src-variable">self</span>.<span class="py-src-variable">transport</span>.<span class="py-src-variable">loseConnection</span>()
</pre><p>This protocol connects to the server, sends it a welcome message, and
then terminates the connection.</p><p>The connectionMade event is usually where set up of the Protocol
object happens, as well as any initial greetings (as in the
WelcomeMessage protocol above). Any tearing down of Protocol-specific
objects is done in connectionLost.</p><h2>Simple, single-use clients<a name="auto2"></a></h2><p>In many cases, the protocl only needs to connect to the server once,
and the code just wants to get a connected instance of the protocol. In
those cases <code class="API">twisted.internet.protocol.ClientCreator</code> provides the
appropriate API.</p><pre class="python">
<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-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">ClientCreator</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Greeter</span>(<span class="py-src-parameter">Protocol</span>):
<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>):
<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>)
<span class="py-src-keyword">def</span> <span class="py-src-identifier">gotProtocol</span>(<span class="py-src-parameter">p</span>):
<span class="py-src-variable">p</span>.<span class="py-src-variable">sendMessage</span>(<span class="py-src-string">"Hello"</span>)
<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>)
<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>)
<span class="py-src-variable">c</span> = <span class="py-src-variable">ClientCreator</span>(<span class="py-src-variable">reactor</span>, <span class="py-src-variable">Greeter</span>)
<span class="py-src-variable">c</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">"localhost"</span>, <span class="py-src-number">1234</span>).<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">gotProtocol</span>)
</pre><h2>ClientFactory<a name="auto3"></a></h2><p>We use reactor.connect* and a ClientFactory. The ClientFactory is in
charge of creating the Protocol, and also receives events relating to the
connection state. This allows it to do things like reconnect on the event
of a connection error. Here is an example of a simple ClientFactory that
uses the Echo protocol (above) and also prints what state the connection
is in.</p><pre class="python">
<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>
<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>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
<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>):
<span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
<span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ClientFactory</span>):
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Started to connect.'</span>
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Connected.'</span>
<span class="py-src-keyword">return</span> <span class="py-src-variable">Echo</span>()
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Lost connection. Reason:'</span>, <span class="py-src-variable">reason</span>
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Connection failed. Reason:'</span>, <span class="py-src-variable">reason</span>
</pre><p>To connect this EchoClientFactory to a server, you could use this
code:</p><pre class="python">
<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">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>())
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><p>Note that <code base="twisted.internet.protocol.ClientFactory" class="API">clientConnectionFailed</code>
is called when a connection could not be established, and that <code base="twisted.internet.protocol.ClientFactory" class="API">clientConnectionLost</code>
is called when a connection was made and then disconnected.</p><h3>Reconnection<a name="auto4"></a></h3><p>Many times, the connection of a client will be lost unintentionally due
to network errors. One way to reconnect after a disconnection would be to
call <code class="python">connector.connect()</code> when the
connection is lost:
</p><pre class="python">
<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>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ClientFactory</span>):
<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>):
<span class="py-src-variable">connector</span>.<span class="py-src-variable">connect</span>()
</pre><p>The connector passed as the first argument is the interface between a
connection and a protocol. When the connection fails and the factory
receives the clientConnectionLost event, the factory can call <code class="python">connector.connect()</code> to start the connection over
again from scratch.</p><p>
However, most programs that want this functionality should implement <code base="twisted.internet.protocol" class="API">ReconnectingClientFactory</code> instead,
which tries to reconnect if a connection is lost or fails, and which
exponentially delays repeated reconnect attempts.
</p><p>
Here is the Echo protocol implemented with a ReconnectingClientFactory:
</p><pre class="python">
<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>
<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>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Echo</span>(<span class="py-src-parameter">Protocol</span>):
<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>):
<span class="py-src-variable">stdout</span>.<span class="py-src-variable">write</span>(<span class="py-src-variable">data</span>)
<span class="py-src-keyword">class</span> <span class="py-src-identifier">EchoClientFactory</span>(<span class="py-src-parameter">ReconnectingClientFactory</span>):
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Started to connect.'</span>
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Connected.'</span>
<span class="py-src-keyword">print</span> <span class="py-src-string">'Resetting reconnection delay'</span>
<span class="py-src-variable">self</span>.<span class="py-src-variable">resetDelay</span>()
<span class="py-src-keyword">return</span> <span class="py-src-variable">Echo</span>()
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Lost connection. Reason:'</span>, <span class="py-src-variable">reason</span>
<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>)
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">'Connection failed. Reason:'</span>, <span class="py-src-variable">reason</span>
<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>,
<span class="py-src-variable">reason</span>)
</pre><h2>A Higher-Level Example: ircLogBot<a name="auto5"></a></h2><h3>Overview of ircLogBot<a name="auto6"></a></h3><p>The clients so far have been fairly simple. A more complicated
example comes with Twisted Words in the doc/examples directory.</p><div class="py-listing"><pre>
<span class="py-src-comment"># twisted imports
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">irc</span>
<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>
<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>
<span class="py-src-comment"># system imports
</span><span class="py-src-keyword">import</span> <span class="py-src-variable">time</span>, <span class="py-src-variable">sys</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MessageLogger</span>:
<span class="py-src-string">"""
An independant logger class (because separation of application
and protocol logic is a good thing).
"""</span>
<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>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">file</span> = <span class="py-src-variable">file</span>
<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>):
<span class="py-src-string">"""Write a message to the file."""</span>
<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>()))
<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>))
<span class="py-src-variable">self</span>.<span class="py-src-variable">file</span>.<span class="py-src-variable">flush</span>()
<span class="py-src-keyword">def</span> <span class="py-src-identifier">close</span>(<span class="py-src-parameter">self</span>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">file</span>.<span class="py-src-variable">close</span>()
<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>):
<span class="py-src-string">"""A logging IRC bot."""</span>
<span class="py-src-variable">nickname</span> = <span class="py-src-string">"twistedbot"</span>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
<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>)
<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>))
<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> %
<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>())))
<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>):
<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>)
<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> %
<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>())))
<span class="py-src-variable">self</span>.<span class="py-src-variable">logger</span>.<span class="py-src-variable">close</span>()
<span class="py-src-comment"># callbacks for events
</span>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">signedOn</span>(<span class="py-src-parameter">self</span>):
<span class="py-src-string">"""Called when bot has succesfully signed on to server."""</span>
<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>)
<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>):
<span class="py-src-string">"""This will get called when the bot joins the channel."""</span>
<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>)
<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>):
<span class="py-src-string">"""This will get called when the bot receives a message."""</span>
<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>]
<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>))
<span class="py-src-comment"># Check to see if they're sending me a private message
</span> <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>:
<span class="py-src-variable">msg</span> = <span class="py-src-string">"It isn't nice to whisper! Play nice with the group."</span>
<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>)
<span class="py-src-keyword">return</span>
<span class="py-src-comment"># Otherwise check to see if it is a message directed at me
</span> <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>):
<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>
<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>)
<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>))
<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>):
<span class="py-src-string">"""This will get called when the bot sees someone do an action."""</span>
<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>]
<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>))
<span class="py-src-comment"># irc callbacks
</span>
<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>):
<span class="py-src-string">"""Called when an IRC user changes their nickname."""</span>
<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>]
<span class="py-src-variable">new_nick</span> = <span class="py-src-variable">params</span>[<span class="py-src-number">0</span>]
<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>))
<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>):
<span class="py-src-string">"""A factory for LogBots.
A new protocol instance will be created each time we connect to the server.
"""</span>
<span class="py-src-comment"># the class of the protocol to build when new connection is made
</span> <span class="py-src-variable">protocol</span> = <span class="py-src-variable">LogBot</span>
<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>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">channel</span> = <span class="py-src-variable">channel</span>
<span class="py-src-variable">self</span>.<span class="py-src-variable">filename</span> = <span class="py-src-variable">filename</span>
<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>):
<span class="py-src-string">"""If we get disconnected, reconnect to server."""</span>
<span class="py-src-variable">connector</span>.<span class="py-src-variable">connect</span>()
<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>):
<span class="py-src-keyword">print</span> <span class="py-src-string">"connection failed:"</span>, <span class="py-src-variable">reason</span>
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
<span class="py-src-comment"># initialize logging
</span> <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>)
<span class="py-src-comment"># create factory protocol and application
</span> <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>])
<span class="py-src-comment"># connect factory to this host and port
</span> <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>)
<span class="py-src-comment"># run bot
</span> <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
</pre><div class="caption">Source listing - <a href="../../words/examples/ircLogBot.py"><span class="filename">../../words/examples/ircLogBot.py</span></a></div></div><p>ircLogBot.py connects to an IRC server, joins a channel, and logs all
traffic on it to a file. It demonstrates some of the connection-level
logic of reconnecting on a lost connection, as well as storing persistent
data in the Factory.</p><h3>Persistent Data in the Factory<a name="auto7"></a></h3><p>Since the Protocol instance is recreated each time the connection is
made, the client needs some way to keep track of data that should be
persisted. In the case of the logging bot, it needs to know which channel
it is logging, and where to log it to.</p><pre class="python">
<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>
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">protocols</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">irc</span>
<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>):
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connectionMade</span>(<span class="py-src-parameter">self</span>):
<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>)
<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>))
<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> %
<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>())))
<span class="py-src-keyword">def</span> <span class="py-src-identifier">signedOn</span>(<span class="py-src-parameter">self</span>):
<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>)
<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>):
<span class="py-src-variable">protocol</span> = <span class="py-src-variable">LogBot</span>
<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>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">channel</span> = <span class="py-src-variable">channel</span>
<span class="py-src-variable">self</span>.<span class="py-src-variable">filename</span> = <span class="py-src-variable">filename</span>
</pre><p>When the protocol is created, it gets a reference to the factory as
self.factory. It can then access attributes of the factory in its logic.
In the case of LogBot, it opens the file and connects to the channel
stored in the factory.</p></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 2.2.0</span></body></html>
|