1
# gozerbot/jabberbot.py
5
""" jabber bot definition """
7
__copyright__ = 'this file is in the public domain'
8
__revision__ = '$Id: bot.py 75 2005-09-12 16:33:00Z bart $'
10
from gozerbot.eventhandler import commandhandler
11
from gozerbot.users import users
12
from gozerbot.monitor import jabbermonitor
13
from gozerbot.wait import Jabberwait, Jabbererrorwait
14
from gozerbot.generic import rlog, handle_exception, lockdec, waitforqueue, \
15
toenc, fromenc, jabberstrip
16
from gozerbot.config import config
17
from gozerbot.plugins import plugins
18
from gozerbot.jabbermsg import Jabbermsg
19
from gozerbot.jabberpresence import Jabberpresence
20
from gozerbot.pdod import Pdod
21
from gozerbot.dol import Dol
22
from gozerbot.datadir import datadir
23
from gozerbot.channels import Channels
24
from gozerbot.less import Less
25
from gozerbot.ignore import shouldignore
26
from gozerbot.callbacks import jcallbacks
27
from gozerbot.thr import start_new_thread
28
from gozerbot.fleet import fleet
29
from gozerbot.runner import runner
30
from gozerbot.botbase import BotBase
31
from xmpp.simplexml import Node
32
import xmpp, time, Queue, os, threading, thread, types, xml
34
jabberoutlock = thread.allocate_lock()
35
jabberinlock = thread.allocate_lock()
36
outlocked = lockdec(jabberoutlock)
37
inlocked = lockdec(jabberinlock)
39
class Jabberbot(BotBase):
41
""" jabber bot class """
43
def __init__(self, name='jabbermain', owner=[]):
44
BotBase.__init__(self, name, owner)
46
self.outqueue = Queue.Queue()
57
self.connection = None
58
self.privwait = Jabberwait()
59
self.errorwait = Jabbererrorwait()
61
self.connectok = threading.Event()
65
if not self.state.has_key('ratelimit'):
66
self.state['ratelimit'] = 0
68
def _resumedata(self): # stateful reboot request, just shutdown
70
return {self.name: [self.host, self.user, self.password, self.port]}
74
while not self.stopped:
77
res = self.connection.Process()
79
self.lastin = time.time()
80
except xmpp.StreamError, ex:
81
if u'Disconnected' in str(ex):
82
rlog(10, self.name, str(ex))
84
except xml.parsers.expat.ExpatError, ex:
85
if u'not well-formed' in str(ex):
86
rlog(10, self.name, str(ex))
95
def _outputloop(self):
96
rlog(10, self.name, 'starting outputloop')
97
while not self.stopped:
98
what = self.outqueue.get()
99
if self.stopped or what == None:
102
sleeptime = config['jabberoutsleep']
104
time.sleep(sleeptime)
107
rlog(10, self.name, 'stopping outputloop')
109
def _keepalive(self):
110
""" keepalive method .. send empty string to self every 3 minutes """
112
while not self.stopped:
119
self.say(self.me, "")
121
def _keepchannelsalive(self):
122
""" channels keep alive method """
124
while not self.stopped:
129
for i in self.state['joinedchannels']:
132
def _connect(self, host, user, password, port=5222):
133
""" connect to server .. start read loop """
137
self.password = password
139
rlog(100, self.name, 'user needs to be in username@host format')
141
self.username = user.split('@')[0]
143
self.jid = xmpp.JID(user)
144
self.server = self.jid.getDomain()
145
self.nick = self.username
146
self.password = password
147
rlog(10, self.name, 'connecting to %s' % self.host)
148
# self.connection = xmpp.Client(self.server, debug=['always', 'nodebuilder'])
149
self.connection = xmpp.Client(self.server, debug=[])
150
self.connection.connect((self.host, self.port))
151
rlog(10, self.name, 'doing auth')
152
auth = self.connection.auth(self.username, self.password, \
155
rlog(10, self.name, 'auth for %s failed .. trying register' \
157
info = {'username': self.username, 'password': self.password}
158
xmpp.features.getRegInfo(self.connection, self.host, info)
159
if not xmpp.features.register(self.connection, self.host, info):
160
rlog(100, self.name, "can't register")
163
self.connection = xmpp.Client(self.server, debug=[])
164
self.connection.connect((self.host, self.port))
165
auth = self.connection.auth(self.username, self.password, \
167
rlog(100, self.name, "register succeded")
168
self.connecttime = time.time()
169
rlog(100, self.name, 'connected! type: %s' % \
170
self.connection.connected)
171
self.connection.RegisterHandler('message', self.messageHandler)
172
self.connection.RegisterHandler('presence', self.presenceHandler)
173
self.connection.RegisterHandler('iq', self.iqHandler)
174
self.connection.UnregisterDisconnectHandler(\
175
self.connection.DisconnectHandler)
176
self.connection.RegisterDisconnectHandler(self.disconnectHandler)
177
self.connection.UnregisterHandlerOnce = self.UnregisterHandlerOnce
179
jabbermonitor.start()
180
start_new_thread(self._doprocess, ())
181
start_new_thread(self._keepalive, ())
182
start_new_thread(self._outputloop, ())
183
#start_new_thread(self._keepchannelsalive, ())
184
self.connection.sendInitPresence()
185
self.connection.getRoster()
189
def connect(self, host, user, password, port=5222, reconnect=True):
192
res = self._connect(host, user, password, port)
193
#except AttributeError:
194
# rlog(10, self.name, "%s denied the connection" % self.host)
196
except Exception, ex:
199
rlog(10, self.name, str(ex))
202
return self.reconnect()
203
if res and not fleet.byname(self.name):
207
def joinchannels(self):
208
""" join channels """
210
for i in self.state['joinedchannels']:
211
key = self.channels.getkey(i)
212
nick = self.channels.getnick(i)
213
result = self.join(i, key, nick)
215
rlog(10, self.name, 'joined %s' % i)
217
rlog(10, self.name, 'failed to join %s: %s' % (i, result))
220
def broadcast(self, txt):
221
for i in self.state['joinedchannels']:
224
def sendpresence(self, to):
225
""" send presence """
226
presence = xmpp.Presence(to=to)
227
presence.setFrom(self.me)
231
def iqHandler(self, conn, node):
232
""" handle iq stanza's """
233
rlog(2, self.name + '-Iq', str(node))
236
jcallbacks.check(self, node)
238
def messageHandler(self, conn, msg):
239
""" message handler """
242
if 'jabber:x:delay' in str(msg):
246
if m.groupchat and m.getSubject():
249
if self.privwait.check(m):
253
if self.me in m.userhost:
255
if m.groupchat and self.nick == m.resource:
259
cc = self.channels[m.channel]['cc']
260
except (TypeError, KeyError):
261
cc = config['defaultcc'] or '!'
263
channick = self.channels[m.channel]['nick']
264
except (TypeError, KeyError):
266
if m.groupchat and not m.txt[0] in cc:
268
if m.txt.startswith("%s: " % channick):
269
m.txt = m.txt.replace("%s: " % channick, "")
271
elif m.txt.startswith("%s, " % channick):
272
m.txt = m.txt.replace("%s, " % channick, "")
276
if go and not 'dojcoll' in str(m.id):
278
if plugins.woulddispatch(self, m):
280
plugins.trydispatch(self, m)
285
jcallbacks.check(self, nm)
286
if nm.getType() == 'error':
287
err = nm.getErrorCode()
289
rlog(10, self.name + '.error', "%s => %s: %s" % (nm.getFrom(),\
291
rlog(10, self.name + '.error', str(nm))
292
self.errorwait.check(nm)
294
def presenceHandler(self, conn, pres):
295
""" overloaded presence handler """
296
p = Jabberpresence(pres)
300
nick = frm.getResource()
302
self.userhosts[nick] = str(frm)
305
for i in p.getPayload():
307
if i.getName() == 'x':
308
for j in i.getPayload():
309
if j.getName() == 'item':
311
if attrs.has_key('jid'):
312
jid = xmpp.JID(attrs['jid'])
313
except AttributeError:
316
channel = frm.getStripped()
317
if not self.jids.has_key(channel):
318
self.jids[channel] = {}
319
self.jids[channel][nickk] = jid
320
self.userhosts[nickk.lower()] = str(jid)
321
rlog(0, 'jabberbot', 'setting jid of %s (%s) to %s' % (nickk, \
323
if p.type == 'subscribe':
325
self.send(xmpp.Presence(to=fromm, typ='subscribed'))
326
self.send(xmpp.Presence(to=fromm, typ='subscribe'))
328
if p.type != 'unavailable':
329
self.userchannels.adduniq(nick, p.channel)
331
elif self.me in p.userhost:
333
del self.jids[p.channel]
334
rlog(10, 'jabberbot', 'removed %s channel jids' % p.channel)
339
del self.jids[p.channel][p.nick]
340
rlog(10, 'jabberbot', 'removed %s jid' % p.nick)
344
jcallbacks.check(self, p)
345
if p.getType() == 'error':
346
err = p.getErrorCode()
348
rlog(10, self.name + '.error', "%s => %s: %s" % (p.getFrom(),\
350
rlog(10, self.name + '.error', str(p))
351
self.errorwait.check(p)
354
rlog(100, self.name, 'reconnecting .. sleeping 15 seconds')
357
newbot = Jabberbot(self.name)
358
if newbot.connect(self.host, self.user, self.password, self.port):
359
newbot.joinchannels()
360
fleet.replace(self.name, newbot)
363
def disconnectHandler(self):
364
""" overloaded disconnect handler """
365
rlog(100, self.name, "disconnected")
369
def send(self, what):
370
self.outqueue.put(toenc(what))
371
jabbermonitor.put(self, what)
374
def rawsend(self, what):
375
""" send via jabber.Client and check for output monitor """
377
rlog(2, '%s-send' % self.name, str(what))
378
if self.connection.isConnected():
379
self.connection.send(what)
380
jabbermonitor.put(self, what)
384
def sendnocb(self, what):
385
""" send via jabber.Client and without checking for output monitor """
389
time.sleep(self.state['ratelimit'])
390
if self.connection.isConnected():
391
self.connection.send(what)
392
rlog(2, '%s-send' % self.name, str(what))
396
def action(self, printto, txt, fromm=None, groupchat=True):
397
""" action txt to printto """
401
if printto in self.state['joinedchannels'] and groupchat:
402
message = xmpp.Message(to=printto, body=txt, typ='groupchat')
404
message = xmpp.Message(to=printto, body=txt)
406
message.setFrom(fromm)
409
def say(self, printto, txt, fromm=None, groupchat=True, speed=5):
410
""" say txt to printto """
411
txt = jabberstrip(txt)
414
if printto in self.state['joinedchannels'] and groupchat:
415
message = xmpp.Message(to=printto, body=txt, typ='groupchat')
417
message = xmpp.Message(to=printto, body=txt, typ='chat')
419
message.setFrom(fromm)
422
def saynocb(self, printto, txt, fromm=None, groupchat=True, speed=5):
423
""" say txt to printto """
424
txt = jabberstrip(txt)
427
if printto in self.state['joinedchannels'] and groupchat:
428
message = xmpp.Message(to=printto, body=txt, typ='groupchat')
430
message = xmpp.Message(to=printto, body=txt, typ='chat')
431
self.sendnocb(message)
433
def wait(self, msg, txt):
434
""" wait for user response """
436
queue = Queue.Queue()
437
self.privwait.register(msg, queue)
440
return result.getBody()
443
""" save bot's state """
447
""" send unavailable presence """
449
presence = xmpp.Presence()
452
presence.setType('unavailable')
453
for i in self.state['joinedchannels']:
457
presence = xmpp.Presence()
458
presence.setType('unavailable')
466
self.outqueue.put_nowait(None)
468
rlog(10, self.name, 'exit')
470
def join(self, channel, password=None, nick=None):
471
""" join conference """
474
nick = channel.split('/')[1]
477
channel = channel.split('/')[0]
478
if not self.channels.has_key(channel):
480
self.channels.setdefault(channel, {})
483
self.errorwait.register("409", q, 3)
484
self.errorwait.register("401", q, 3)
485
self.errorwait.register("400", q, 3)
487
presence = xmpp.Presence(to=channel + '/' + nick)
488
#presence.setFrom(self.me)
490
passnode = Node('password')
491
passnode.addData(password)
492
presence.addChild(name='x', namespace='http://jabber.org/protocol/muc', \
493
payload=[passnode, ])
495
errorobj = waitforqueue(q, 3)
497
err = errorobj[0].error
498
rlog(10, self.name, 'error joining %s: %s' % (channel, err))
500
self.timejoined[channel] = time.time()
501
chan = self.channels[channel]
502
# if password is provided set it
505
chan['key'] = password
506
# check for control char .. if its not there init to !
507
if not chan.has_key('cc'):
508
chan['cc'] = config['defaultcc'] or '!'
509
if not chan.has_key('perms'):
512
if channel not in self.state['joinedchannels']:
513
self.state['joinedchannels'].append(channel)
517
def part(self, channel):
518
""" leace conference """
519
presence = xmpp.Presence(to=channel)
520
presence.setFrom(self.me)
521
presence.setType('unavailable')
523
if channel in self.state['joinedchannels']:
524
self.state['joinedchannels'].remove(channel)
528
def outputnolog(self, printto, what, how, who=None, fromm=None):
529
""" doe output but don't log it """
530
if fromm and shouldignore(fromm):
532
self.saynocb(printto, what)
534
def topiccheck(self, msg):
535
""" chek if topic is set """
538
topic = msg.getSubject()
541
self.topics[msg.channel] = (topic, msg.userhost, time.time())
542
rlog(10, self.name, 'topic of %s set to %s' % \
543
(msg.channel, topic))
544
except AttributeError:
547
def settopic(self, channel, txt):
549
pres = xmpp.Message(to=channel, subject=txt)
550
pres.setType('groupchat')
553
def gettopic(self, channel):
556
topic = self.topics[channel]
561
def UnregisterHandlerOnce(self, a, b, xmlns=None):
562
""" hack to work around missing method """
565
def sendraw(self, msg):
566
rlog(2, '%s-sendraw' % self.name, str(msg))
569
if self.connection.__dict__.has_key('TCPsocket'):
570
self.connection.TCPsocket.send(msg)
572
self.connection.Connection.send(msg)