5
""" fleet is a list of bots. """
7
__copyright__ = 'this file is in the public domain'
14
from gozerbot.datadir import datadir
15
from utils.exception import handle_exception
16
from utils.generic import waitforqueue
17
from utils.log import rlog
18
from utils.locking import lockdec
19
from threads.thr import start_new_thread, threaded
20
from config import Config, fleetbotconfigtxt, config
21
from users import users
22
from plugins import plugins
23
from simplejson import load
27
import Queue, os, types, threading, time, pickle, glob, logging, shutil, thread
34
fleetlock = thread.allocate_lock()
35
fleetlocked = lockdec(fleetlock)
42
class FleetBotAlreadyExists(Exception):
49
a fleet contains multiple bots (list of bots). used the datadir
50
set in gozerbot/datadir.py
55
self.datadir = datadir + os.sep + 'fleet'
56
if hasattr(os, 'mkdir'):
57
if not os.path.exists(self.datadir):
58
os.mkdir(self.datadir)
59
self.startok = threading.Event()
62
def getfirstbot(self):
65
return the main bot of the fleet.
67
:rtype: gozerbot.botbase.BotBase
69
.. literalinclude:: ../../gozerbot/fleet.py
70
:pyobject: Fleet.getfirstbot
76
def getfirstjabber(self):
79
return the first jabber bot of the fleet.
81
:rtype: gozerbot.botbase.BotBase
83
.. literalinclude:: ../../gozerbot/fleet.py
84
:pyobject: Fleet.getfirstjabber
91
if bot.type == 'xmpp':
97
return number of bots in fleet.
101
.. literalinclude:: ../../gozerbot/fleet.py
102
:pyobject: Fleet.size
106
return len(self.bots)
108
def resume(self, sessionfile):
111
resume bot from session file.
113
:param sessionfile: filename of the session data file
114
:type sessionfile: string
116
.. literalinclude:: ../../gozerbot/fleet.py
117
:pyobject: Fleet.resume
120
# read JSON session file
121
session = load(open(sessionfile))
123
# resume bots in session file
124
for name in session['bots'].keys():
126
if name == session['name']:
127
reto = session['channel']
128
start_new_thread(self.resumebot, (name, session['bots'][name], reto))
130
# allow 5 seconds for bots to resurrect
136
def makebot(self, name, cfg=None):
139
create a bot .. use configuration if provided.
141
:param name: the name of the bot
143
:param cfg: configuration file for the bot
144
:type cfg: gozerbot.config.Config
146
.. literalinclude:: ../../gozerbot/fleet.py
147
:pyobject: Fleet.makebot
151
if self.byname(name):
152
raise FleetBotAlreadyExists("there is already a %s bot in the fleet" % name)
154
rlog(10, 'fleet', 'making bot')
157
# if not config create a default bot
159
cfg = Config(self.datadir + os.sep + name, 'config', inittxt=fleetbotconfigtxt)
162
# create bot based on type
163
if cfg['type'] == 'irc':
164
from gozerbot.irc.bot import Bot
166
elif cfg['type'] == 'xmpp' or cfg['type'] == 'jabber':
167
from gozerbot.xmpp.bot import Bot
169
elif cfg['type'] == 'gozernet':
170
from gozerbot.gozernet.bot import GozerNetBot
171
bot = GozerNetBot(name, cfg)
173
rlog(10, 'fleet', '%s .. unproper type: %s' % (cfg['name'], cfg['type']))
175
# set bot name and initialize bot
177
cfg['name'] = bot.name = name
181
# failed to created the bot
182
raise Exception("can't make %s bot" % name)
184
def resumebot(self, botname, data={}, printto=None):
187
resume individual bot.
189
:param botname: name of the bot to resume
190
:type botname: string
191
:param data: resume data
193
:param printto: whom to reply to that resuming is done
194
:type printto: nick or JID
196
.. literalinclude:: ../../gozerbot/fleet.py
197
:pyobject: Fleet.resumebot
201
# see if we need to exit the old bot
202
oldbot = self.byname(botname)
206
# recreate config file of the bot
207
cfg = Config(datadir + os.sep + 'fleet' + os.sep + botname, 'config')
209
# make the bot and resume (IRC) or reconnect (Jabber)
210
bot = self.makebot(botname, cfg)
211
rlog(100, 'fleet', 'bot made: %s' % str(bot))
215
self.replace(oldbot, bot)
217
self.bots.append(bot)
220
bot._resume(data, printto)
222
start_new_thread(bot.connectwithjoin, ())
224
def start(self, botlist=[], enable=False):
229
:param botlist: list of bots to start .. if not provided the bots in the gozerdata/fleet dir will be used
231
:param enable: whether the bot should be enabled
232
:type enable: boolean
234
.. literalinclude:: ../../gozerbot/fleet.py
235
:pyobject: Fleet.start
239
# scan the fleet datadir for bots
243
dirs.append(self.datadir + os.sep + bot)
246
dirs = glob.glob(self.datadir + os.sep + "*")
248
for fleetdir in dirs:
250
if fleetdir.endswith('fleet'):
253
rlog(10, 'fleet', 'found bot: ' + fleetdir)
254
cfg = Config(fleetdir, 'config')
257
rlog(10, 'fleet', "can't read %s config file" % fleetdir)
260
name = fleetdir.split(os.sep)[-1]
263
rlog(10, 'fleet', "can't read botname from %s config file" % \
267
if not enable and not cfg['enable']:
268
rlog(10, 'fleet', '%s bot is disabled' % name)
271
rlog(10, 'fleet', '%s bot is enabled' % name)
273
if not name in fleetdir:
274
rlog(10, 'fleet', 'bot name in config file doesnt match dir name')
278
bot = self.makebot(name, cfg)
279
except FleetBotAlreadyExists:
280
rlog(10, 'fleet', 'there is already a fleetbot with the name %s' % name)
285
start_new_thread(bot.connectwithjoin, ())
296
save fleet data and call save on all the bots.
298
.. literalinclude:: ../../gozerbot/fleet.py
299
:pyobject: Fleet.save
307
except Exception, ex:
313
show available fleet bots.
317
.. literalinclude:: ../../gozerbot/fleet.py
318
:pyobject: Fleet.avail
322
return os.listdir(self.datadir)
327
return list of bot names.
331
.. literalinclude:: ../../gozerbot/fleet.py
332
:pyobject: Fleet.list
339
result.append(i.name)
346
call stop() on all fleet bots.
348
.. literalinclude:: ../../gozerbot/fleet.py
349
:pyobject: Fleet.stopall
360
def byname(self, name):
365
:param name: name of the bot
368
.. literalinclude:: ../../gozerbot/fleet.py
369
:pyobject: Fleet.byname
377
def replace(self, name, bot):
380
replace bot with a new bot.
382
:param name: name of the bot to replace
384
:param bot: bot to replace old bot with
385
:type bot: gozerbot.botbase.BotBase
387
.. literalinclude:: ../../gozerbot/fleet.py
388
:pyobject: Fleet.replace
392
for i in range(len(self.bots)):
393
if name == self.bots[i].name:
397
def initbot(self, bot):
402
:param bot: bot to initialise
403
:type bot: gozerbot.botbase.BotBase
405
.. literalinclude:: ../../gozerbot/fleet.py
406
:pyobject: Fleet.initbot
410
if bot not in self.bots:
412
if not os.path.exists(self.datadir + os.sep + bot.name):
413
os.mkdir(self.datadir + os.sep + bot.name)
415
if type(bot.cfg['owner']) == types.StringType or type(bot.cfg['owner']) == types.UnicodeType:
416
bot.cfg['owner'] = [bot.cfg['owner'], ]
419
users.make_owner(config['owner'] + bot.cfg['owner'])
420
rlog(10, 'fleet', 'added bot: ' + bot.name)
423
def addbot(self, bot):
426
add a bot to the fleet .. remove all existing bots with the
429
:param bot: bot to add
430
:type bot: gozerbot.botbase.BotBase
433
.. literalinclude:: ../../gozerbot/fleet.py
434
:pyobject: Fleet.addbot
440
for i in range(len(self.bots)-1, -1, -1):
441
if self.bots[i].name == bot.name:
442
rlog(10, 'fleet', 'removing %s from fleet' % bot.name)
445
rlog(10, 'fleet', 'adding %s' % bot.name)
446
self.bots.append(bot)
451
def connect(self, name):
454
connect bot to the server.
456
:param name: name of the bot
460
.. literalinclude:: ../../gozerbot/fleet.py
461
:pyobject: Fleet.connect
471
start_new_thread(i.joinchannels, ())
477
def delete(self, name):
480
delete bot with name from fleet.
482
:param name: name of bot to delete
486
.. literalinclude:: ../../gozerbot/fleet.py
487
:pyobject: Fleet.delete
498
rlog(10, 'fleet', '%s disabled' % i.name)
504
def remove(self, bot):
507
delete bot by object.
509
:param bot: bot to delete
510
:type bot: gozerbot.botbase.BotBase
513
.. literalinclude:: ../../gozerbot/fleet.py
514
:pyobject: Fleet.remove
519
self.bots.remove(bot)
524
def exit(self, name=None, jabber=False):
527
call exit on all bots. if jabber=True only jabberbots will exit.
529
:param name: name of the bot to exit. if not provided all bots will exit.
531
:param jabber: flag to set when only jabberbots should exit
532
:type jabber: boolean
535
.. literalinclude:: ../../gozerbot/fleet.py
536
:pyobject: Fleet.exit
544
if jabber and not i.jabber:
547
threads.append(start_new_thread(i.exit, ()))
567
def cmnd(self, event, name, cmnd):
572
:param event: event to pass on to the dispatcher
573
:type event: gozerbot.event.EventBase
574
:param name: name of the bot to pass on to the dispatcher
576
:param cmnd: command to execute on the fleet bot
579
.. literalinclude:: ../../gozerbot/fleet.py
580
:pyobject: Fleet.cmnd
584
bot = self.byname(name)
589
from gozerbot.eventbase import EventBase
590
j = plugins.clonedevent(bot, event)
596
start_new_thread(plugins.trydispatch, (bot, j))
597
result = waitforqueue(q)
602
res = ["<%s>" % bot.name, ]
606
def cmndall(self, event, cmnd):
609
do a command on all bots.
611
:param event: event to pass on to dispatcher
612
:type event: gozerbot.eventbase.EventBase
613
:param cmnd: the command string to execute
616
.. literalinclude:: ../../gozerbot/fleet.py
617
:pyobject: Fleet.cmndall
624
thread = start_new_thread(self.cmnd, (event, i.name, cmnd))
625
threads.append(thread)
630
def broadcast(self, txt):
633
broadcast txt to all bots.
635
:param txt: text to broadcast on all bots
638
.. literalinclude:: ../../gozerbot/fleet.py
639
:pyobject: Fleet.broadcast