~ubuntu-branches/ubuntu/utopic/gozerbot/utopic

« back to all changes in this revision

Viewing changes to build/lib/gozerbot/compat/rss.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Malcolm
  • Date: 2012-04-03 21:58:28 UTC
  • mfrom: (3.1.11 sid)
  • Revision ID: package-import@ubuntu.com-20120403215828-6mik0tzug5na93la
Tags: 0.99.1-2
* Removes logfiles on purge (Closes: #668767)
* Reverted location of installed files back to /usr/lib/gozerbot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# plugs/rss.py
 
2
#
 
3
#
 
4
 
 
5
"""the rss mantra is of the following:
 
6
 
 
7
as OPER:
 
8
 
 
9
. add a url with rss-add
 
10
. start the watcher with rss-watch
 
11
 
 
12
now the user can start the bot sending messages of the feed to him with
 
13
rss-start. if an OPER gives a rss-start command in a channel data will be
 
14
send to the channel.
 
15
 """
 
16
 
 
17
__copyright__ = 'this file is in the public domain'
 
18
__gendocfirst__ = ['rss-add', 'rss-watch', 'rss-start']
 
19
__gendocskip__ = ['rss-dump', ]
 
20
 
 
21
from gozerbot.compat.persist import Persist
 
22
from gozerbot.utils.url import geturl2
 
23
from gozerbot.utils.exception import handle_exception
 
24
from gozerbot.utils.log import rlog
 
25
from gozerbot.utils.locking import lockdec
 
26
from gozerbot.utils.generic import strippedtxt, fromenc
 
27
from gozerbot.utils.url import striphtml, useragent
 
28
from gozerbot.utils.rsslist import rsslist
 
29
from gozerbot.utils.statdict import Statdict
 
30
from gozerbot.fleet import fleet
 
31
from gozerbot.commands import cmnds
 
32
from gozerbot.examples import examples
 
33
from gozerbot.datadir import datadir
 
34
from gozerbot.utils.dol import Dol
 
35
from gozerbot.compat.pdod import Pdod
 
36
from gozerbot.compat.pdol import Pdol
 
37
from gozerbot.plughelp import plughelp
 
38
from gozerbot.periodical import periodical
 
39
from gozerbot.aliases import aliasset
 
40
from gozerbot.users import users
 
41
import feedparser
 
42
import gozerbot.threads.thr as thr
 
43
import time, os, types, thread, socket, xml
 
44
 
 
45
plughelp.add('rss', 'manage rss feeds')
 
46
 
 
47
savelist = []
 
48
 
 
49
def txtindicts(result, d):
 
50
    """ return lowlevel values in (nested) dicts """
 
51
    for j in d.values():
 
52
        if type(j) == types.DictType:
 
53
            txtindicts(result, j) 
 
54
        else:
 
55
            result.append(j)
 
56
 
 
57
def checkfordate(data, date):
 
58
    for item in data:
 
59
        try:
 
60
            d = item['updated']
 
61
        except KeyError:
 
62
            continue
 
63
        if date == d:
 
64
            return True
 
65
    return False
 
66
 
 
67
rsslock = thread.allocate_lock()
 
68
locked = lockdec(rsslock)
 
69
 
 
70
class RssException(Exception):
 
71
    pass
 
72
 
 
73
class Rss301(RssException):
 
74
    pass
 
75
 
 
76
class RssStatus(RssException):
 
77
    pass
 
78
 
 
79
class RssBozoException(RssException):
 
80
    pass
 
81
 
 
82
class RssNoSuchItem(RssException):
 
83
    pass
 
84
 
 
85
class Rssitem(object):
 
86
 
 
87
    """ item that contains rss data """
 
88
 
 
89
    def __init__(self, name, url, itemslist, watchchannels=[], \
 
90
sleeptime=30*60):
 
91
        self.name = name
 
92
        self.url = url
 
93
        self.itemslist = list(itemslist)
 
94
        self.watchchannels = list(watchchannels)
 
95
        self.sleeptime = int(sleeptime)
 
96
        self.running = 0
 
97
        self.stoprunning = 0
 
98
        self.botname = None
 
99
 
 
100
    def __str__(self):
 
101
        return "name=%s url=%s itemslist=%s watchchannels=%s sleeptime=%s \
 
102
running=%s" % (self.name, self.url, str(self.itemslist), \
 
103
str(self.watchchannels), str(self.sleeptime), self.running)
 
104
 
 
105
class Rssdict(Persist):
 
106
 
 
107
    """ dict of rss entries """
 
108
 
 
109
    def __init__(self, filename):
 
110
        Persist.__init__(self, filename)
 
111
        if not self.data:
 
112
            self.data = {}
 
113
        if self.data.has_key('itemslists'):
 
114
            del self.data['itemslists']
 
115
        self.itemslists = Pdol(filename + '.itemslists')
 
116
        self.handlers = {}
 
117
        self.results = {}
 
118
        self.jobids = {}
 
119
        self.rawresults = {}
 
120
        self.results = Dol()
 
121
        self.modified = {}
 
122
        self.etag = {}
 
123
        self.markup = Pdod(filename + '.markup')
 
124
 
 
125
    def size(self):
 
126
        """ return number of rss entries """
 
127
        return len(self.data)
 
128
 
 
129
    @locked
 
130
    def add(self, name, url):
 
131
        """ add rss item """
 
132
        rlog(10, 'rss', 'adding %s %s' % (name, url))
 
133
        self.data[name] = Rssitem(name, url, ['title', ])
 
134
        self.save()
 
135
 
 
136
    @locked
 
137
    def delete(self, name):
 
138
        """ delete rss item by name """
 
139
        target = None
 
140
        for j, i in self.data.iteritems():
 
141
           if i.name == name:
 
142
               target = i
 
143
        if target:
 
144
            try:
 
145
                target.running = 0
 
146
                del self.data[name]
 
147
                self.save()
 
148
            except:
 
149
                pass
 
150
 
 
151
    def byname(self, name):
 
152
        """ return rss item by name """
 
153
        try:
 
154
            return self.data[name]
 
155
        except:
 
156
            return
 
157
 
 
158
    def getdata(self, name):
 
159
        """ get data of rss feed """
 
160
        rssitem = self.byname(name)
 
161
        if rssitem == None:
 
162
            raise RssNoSuchItem("no %s rss item found" % name)
 
163
        try:
 
164
            modified = self.modified[name]
 
165
        except KeyError:
 
166
            modified = None
 
167
        try:
 
168
            etag = self.etag[name]
 
169
        except KeyError:
 
170
            etag = None
 
171
        result = feedparser.parse(rssitem.url, modified=modified, etag=etag, \
 
172
agent=useragent())
 
173
        if result and result.has_key('bozo_exception'):
 
174
            rlog(1, 'rss', '%s bozo_exception: %s' % (name, \
 
175
result['bozo_exception']))
 
176
            #raise RssStatus(result['bozo_exception'])
 
177
        try:
 
178
            status = result.status
 
179
        except AttributeError:
 
180
            status = 200
 
181
        if status != 200 and status != 301 and status != 302 and status != 304:
 
182
            raise RssStatus(status)
 
183
        try:
 
184
            self.modified[name] = result.modified
 
185
        except AttributeError:
 
186
            pass
 
187
        try:
 
188
            self.etag[name] = result.etag
 
189
        except AttributeError:
 
190
            pass
 
191
        if status == 304:
 
192
            return self.rawresults[name]
 
193
        else:
 
194
            self.rawresults[name] = result.entries
 
195
        return result.entries
 
196
 
 
197
class Rsswatcher(Rssdict):
 
198
 
 
199
    """ rss watchers """ 
 
200
 
 
201
    def __init__(self, filename):
 
202
        Rssdict.__init__(self, filename)
 
203
        
 
204
    def sync(self, name):
 
205
        result = self.getdata(name)
 
206
        if result:
 
207
            self.results[name] = result
 
208
 
 
209
    def changeinterval(self, name, interval):
 
210
        periodical.changeinterval(self.jobids[name], interval)
 
211
 
 
212
    @locked
 
213
    def startwatchers(self):
 
214
        """ start watcher threads """
 
215
        for j, i in self.data.iteritems():
 
216
            if i.running:
 
217
                thr.start_new_thread(self.watch, (i.name, ))
 
218
 
 
219
    @locked
 
220
    def stopwatchers(self):
 
221
        """ stop all watcher threads """
 
222
        for j, i in self.data.iteritems():
 
223
            if i.running:
 
224
                i.stoprunning = 1
 
225
        periodical.killgroup('rss')
 
226
 
 
227
    def dowatch(self, name, sleeptime=1800):
 
228
        rssitem = self.byname(name)
 
229
        if not rssitem:
 
230
            rlog(10, 'rss', "no %s rss item available" % name)
 
231
            return
 
232
        while 1:
 
233
            try:
 
234
                self.watch(name)
 
235
            except Exception, ex:
 
236
                rlog(100, 'rss', '%s feed error: %s' % (name, str(ex)))
 
237
                rlog(100, 'rss', '%s sleeping %s seconds for retry' % \
 
238
(name, sleeptime))
 
239
                time.sleep(sleeptime)
 
240
                if not rssitem.running:
 
241
                    break
 
242
            else:
 
243
                break
 
244
 
 
245
    def makeresult(self, name, target, data):
 
246
        res = []
 
247
        for j in data:
 
248
            tmp = {}
 
249
            if not self.itemslists[(name, target)]:
 
250
                return []
 
251
            for i in self.itemslists[(name, target)]:
 
252
                try:
 
253
                    tmp[i] = unicode(j[i])
 
254
                except KeyError:
 
255
                    continue
 
256
            res.append(tmp)
 
257
        return res
 
258
 
 
259
    def watch(self, name):
 
260
        """ start a watcher thread """
 
261
        # get basic data
 
262
        rlog(10, 'rss', 'trying %s rss feed watcher' % name)
 
263
        try:
 
264
            result = self.getdata(name)
 
265
        except RssException, ex:
 
266
            rlog(10, 'rss', "%s error: %s" % (name, str(ex)))
 
267
            result = []
 
268
        rssitem = self.byname(name)
 
269
        if not rssitem:
 
270
            raise RssNoItem()
 
271
        # poll every sleeptime seconds
 
272
        self.results[name] = result
 
273
        pid = periodical.addjob(rssitem.sleeptime, 0, self.peek, name, name)
 
274
        self.jobids[name] = pid
 
275
        rlog(10, 'rss', 'started %s rss watch' % name)
 
276
 
 
277
    def makeresponse(self, name, res, channel, sep="\002||\002"):
 
278
        # loop over result to make a response
 
279
        result = ""
 
280
        itemslist = self.itemslists[(name, channel)]
 
281
        if not itemslist:
 
282
            rssitem = self.byname(name)
 
283
            if not rssitem:
 
284
                return "no %s rss item" % name
 
285
            else:
 
286
                self.itemslists.extend((name, channel), rssitem.itemslist)
 
287
                self.itemslists.save()
 
288
        for j in res:
 
289
            resultstr = ""
 
290
            for i in self.itemslists[(name, channel)]:
 
291
                try:
 
292
                    item = unicode(j[i])
 
293
                    if not item:
 
294
                        continue
 
295
                    if item.startswith('http://'):
 
296
                        resultstr += "<%s> - " % item
 
297
                    else:
 
298
                        resultstr += "%s - " % striphtml(item)
 
299
                except KeyError:
 
300
                    continue
 
301
            resultstr = resultstr[:-3]
 
302
            if resultstr:
 
303
                result += "%s %s " % (resultstr, sep)
 
304
        return result[:-6]
 
305
 
 
306
    def peek(self, name, *args):
 
307
        rssitem = self.byname(name)
 
308
        if not rssitem or not rssitem.running or rssitem.stoprunning:
 
309
            return
 
310
        try:
 
311
            try:
 
312
                res = self.getdata(name)
 
313
            except socket.timeout:
 
314
                rlog(10, 'rss', 'socket timeout of %s' % name)
 
315
                return
 
316
            except RssException, ex:
 
317
                rlog(10, 'rss', '%s error: %s' % (name, str(ex)))
 
318
                return
 
319
            if not res:
 
320
                return
 
321
            res2 = []
 
322
            for j in res:
 
323
                try:
 
324
                    d = j['date']
 
325
                except KeyError:
 
326
                    if j not in self.results[name]:
 
327
                        self.results[name].append(j)
 
328
                        res2.append(j)
 
329
                else:
 
330
                    if not checkfordate(self.results[name], d):
 
331
                        self.results[name].append(j)
 
332
                        res2.append(j)
 
333
            if not res2:
 
334
                return
 
335
            for item in rssitem.watchchannels:
 
336
                try:
 
337
                    (botname, channel) = item
 
338
                except:
 
339
                    rlog(10, 'rss', '%s is not in the format \
 
340
(botname,channel)' % str(item))
 
341
                bot = fleet.byname(botname)
 
342
                if not bot:
 
343
                    continue
 
344
                if self.markup.get((name, channel), 'all-lines'):
 
345
                    for i in res2:
 
346
                        response = self.makeresponse(name, [i, ], channel)
 
347
                        bot.say(channel, "\002%s\002: %s" % \
 
348
(rssitem.name, response), fromm=rssitem.name)
 
349
                else:
 
350
                    sep =  self.markup.get((name, channel), 'seperator')
 
351
                    if sep:
 
352
                        response = self.makeresponse(name, res2, channel, \
 
353
sep=sep)
 
354
                    else:
 
355
                        response = self.makeresponse(name, res2, channel)
 
356
                    bot.say(channel, "\002%s\002: %s" % (rssitem.name, \
 
357
response), fromm=rssitem.name)
 
358
        except Exception, ex:
 
359
            handle_exception(txt=name)
 
360
 
 
361
    @locked
 
362
    def stopwatch(self, name):
 
363
        """ stop watcher thread """
 
364
        for i, j in self.data.iteritems():
 
365
            if i == name:
 
366
                j.running = 0
 
367
                try:
 
368
                    del self.results[name]
 
369
                except KeyError:
 
370
                    pass
 
371
                self.save()
 
372
                try:
 
373
                    periodical.killjob(self.jobids[i])
 
374
                except KeyError:
 
375
                    pass
 
376
                return 1
 
377
 
 
378
    @locked
 
379
    def list(self):
 
380
        """ return of rss names """
 
381
        feeds = self.data.keys()
 
382
        return feeds
 
383
 
 
384
    @locked
 
385
    def runners(self):  
 
386
        """ show names/channels of running watchers """
 
387
        result = []
 
388
        for j, i in self.data.iteritems():
 
389
            if i.running == 1 and not i.stoprunning: 
 
390
                result.append((i.name, i.watchchannels))
 
391
        return result
 
392
 
 
393
    @locked
 
394
    def feeds(self, botname, channel):
 
395
        """ show names/channels of running watcher """
 
396
        result = []
 
397
        for j, i in self.data.iteritems():
 
398
            if (botname, channel) in i.watchchannels:
 
399
                result.append(i.name)
 
400
        return result
 
401
 
 
402
    @locked
 
403
    def url(self, name):
 
404
        """ return url of rssitem """
 
405
        for j, i in self.data.iteritems():
 
406
            if i.name == name:
 
407
                return i.url
 
408
 
 
409
    def scan(self, name):
 
410
        """ scan a rss url for used xml items """
 
411
        try:
 
412
            result = self.getdata(name)
 
413
        except RssException, ex:
 
414
            rlog(10, 'rss', '%s error: %s' % (name, str(ex)))
 
415
            return
 
416
        if not result:
 
417
            return
 
418
        keys = []
 
419
        for item in self.rawresults[name]:
 
420
             for key in item.keys():
 
421
                 keys.append(key)            
 
422
        statdict = Statdict()
 
423
        for key in keys:
 
424
            statdict.upitem(key)
 
425
        return statdict.top()  
 
426
 
 
427
    def search(self, name, item, search):
 
428
        res = []
 
429
        for result in self.rawresults[name]:
 
430
            try:
 
431
                title = result['title']
 
432
                txt = result[item]
 
433
            except KeyError:
 
434
                continue
 
435
            if search in title.lower():
 
436
                if txt:
 
437
                   res.append(txt)
 
438
        return res
 
439
 
 
440
    def searchall(self, item, search):
 
441
        res = []
 
442
        for name, results in self.rawresults.iteritems():
 
443
            for result in results:
 
444
                try:
 
445
                    title = result['title']
 
446
                    txt = result[item]
 
447
                except KeyError:
 
448
                    continue
 
449
                if search in title.lower():
 
450
                    if txt:
 
451
                        res.append("%s: %s" % (name, txt))
 
452
        return res
 
453
 
 
454
    def all(self, name, item):
 
455
        res = []
 
456
        for result in self.rawresults[name]:
 
457
            try:
 
458
                txt = result[item]
 
459
            except KeyError:
 
460
                continue
 
461
            if txt:
 
462
                res.append(txt)
 
463
        return res
 
464
 
 
465
watcher = Rsswatcher(datadir + os.sep + 'old' + os.sep + 'rss')
 
466
 
 
467
def init(): 
 
468
    """ called after plugin import """
 
469
    thr.start_new_thread(watcher.startwatchers, ())
 
470
    return 1
 
471
 
 
472
def size():
 
473
    """ return number of watched rss entries """
 
474
    return watcher.size()
 
475
 
 
476
def shutdown():
 
477
    """ called before plugin import """
 
478
    watcher.stopwatchers()
 
479
    return 1
 
480
    
 
481
def handle_rssadd(bot, ievent):
 
482
    """ rss-add <name> <url> .. add a rss item """
 
483
    try:
 
484
        (name, url) = ievent.args
 
485
    except ValueError:
 
486
        ievent.missing('<name> <url>')
 
487
        return
 
488
    watcher.add(name, url)
 
489
    ievent.reply('rss item added')
 
490
 
 
491
cmnds.add('rss-add', handle_rssadd, 'OPER')
 
492
examples.add('rss-add', 'rss-add <name> <url> to the rsswatcher', 'rss-add \
 
493
gozerbot http://gozerbot.org/hg/gozerbot/?cmd=changelog;style=rss')
 
494
 
 
495
def handle_rssdel(bot, ievent):
 
496
    """ rss-del <name> .. delete a rss item """
 
497
    try:
 
498
        name = ievent.args[0]
 
499
    except IndexError:
 
500
        ievent.missing('<name>')
 
501
        return
 
502
    if watcher.byname(name):
 
503
        watcher.stopwatch(name)
 
504
        watcher.delete(name)
 
505
        ievent.reply('rss item deleted')
 
506
    else:
 
507
        ievent.reply('there is no %s rss item' % name)
 
508
 
 
509
cmnds.add('rss-del', handle_rssdel, 'OPER')
 
510
examples.add('rss-del', 'rss-del <name> .. remove <name> from the \
 
511
rsswatcher', 'rss-del mekker')
 
512
 
 
513
def handle_rsswatch(bot, ievent):
 
514
    """ rss-watch <name> .. start watcher thread """
 
515
    if not ievent.channel:
 
516
        ievent.reply('no channel provided')
 
517
    try:
 
518
        name, sleepsec = ievent.args
 
519
    except ValueError:
 
520
        try:
 
521
            name = ievent.args[0]
 
522
            sleepsec = 1800
 
523
        except IndexError:
 
524
            ievent.missing('<name> [secondstosleep]')
 
525
            return
 
526
    try:
 
527
        sleepsec = int(sleepsec)
 
528
    except ValueError:
 
529
        ievent.reply("time to sleep needs to be in seconds")
 
530
        return
 
531
    rssitem = watcher.byname(name)
 
532
    if rssitem == None:
 
533
        ievent.reply("we don't have a %s rss object" % name)
 
534
        return
 
535
    got = None
 
536
    if not rssitem.running:
 
537
        rssitem.sleeptime = sleepsec
 
538
        rssitem.running = 1
 
539
        rssitem.stoprunning = 0
 
540
        got = True
 
541
        watcher.save()
 
542
        try:
 
543
            watcher.watch(name)
 
544
        except Exception, ex:
 
545
            ievent.reply(str(ex))
 
546
            return
 
547
    if got:
 
548
        watcher.save()
 
549
        ievent.reply('watcher started')
 
550
    else:
 
551
        ievent.reply('already watching %s' % name)
 
552
 
 
553
cmnds.add('rss-watch', handle_rsswatch, 'OPER')
 
554
examples.add('rss-watch', 'rss-watch <name> [seconds to sleep] .. go \
 
555
watching <name>', '1) rss-watch gozerbot 2) rss-watch gozerbot 600')
 
556
 
 
557
def handle_rssstart(bot, ievent):
 
558
    """ rss-start <name> .. start a rss feed to a user """
 
559
    if not ievent.rest:
 
560
       ievent.missing('<feed name>')
 
561
       return
 
562
    name = ievent.rest
 
563
    rssitem = watcher.byname(name)
 
564
    if bot.jabber:
 
565
        target = ievent.userhost
 
566
    else:
 
567
        if users.allowed(ievent.userhost, ['OPER', ]) and not ievent.msg:
 
568
            target = ievent.channel
 
569
        else:
 
570
            target = ievent.nick
 
571
    if rssitem == None:
 
572
        ievent.reply("we don't have a %s rss object" % name)
 
573
        return
 
574
    if not rssitem.running:
 
575
        ievent.reply('%s watcher is not running' % name)
 
576
        return
 
577
    if (bot.name, target) in rssitem.watchchannels:
 
578
        ievent.reply('we are already monitoring %s on (%s,%s)' % \
 
579
(name, bot.name, target))
 
580
        return
 
581
    rssitem.watchchannels.append((bot.name, target))
 
582
    for item in rssitem.itemslist:
 
583
        watcher.itemslists.adduniq((name, target), item)
 
584
    watcher.save()
 
585
    ievent.reply('%s started' % name)
 
586
 
 
587
cmnds.add('rss-start', handle_rssstart, ['RSS', 'USER'])
 
588
examples.add('rss-start', 'rss-start <name> .. start a rss feed \
 
589
(per user/channel) ', 'rss-start gozerbot')
 
590
 
 
591
def handle_rssstop(bot, ievent):
 
592
    """ rss-start <name> .. start a rss feed to a user """
 
593
    if not ievent.rest:
 
594
       ievent.missing('<feed name>')
 
595
       return
 
596
    name = ievent.rest
 
597
    rssitem = watcher.byname(name)
 
598
    if bot.jabber:
 
599
        target = ievent.userhost
 
600
    else:
 
601
        if users.allowed(ievent.userhost, ['OPER', ]) and not ievent.msg:
 
602
            target = ievent.channel
 
603
        else:
 
604
            target = ievent.nick
 
605
    if rssitem == None:
 
606
        ievent.reply("we don't have a %s rss feed" % name)
 
607
        return
 
608
    if not rssitem.running:
 
609
        ievent.reply('%s watcher is not running' % name)
 
610
        return
 
611
    if not (bot.name, target) in rssitem.watchchannels:
 
612
        ievent.reply('we are not monitoring %s on (%s,%s)' % \
 
613
(name, bot.name, target))
 
614
        return
 
615
    rssitem.watchchannels.remove((bot.name, target))
 
616
    watcher.save()
 
617
    ievent.reply('%s stopped' % name)
 
618
 
 
619
cmnds.add('rss-stop', handle_rssstop, ['RSS', 'USER'])
 
620
examples.add('rss-stop', 'rss-stop <name> .. stop a rss feed \
 
621
(per user/channel) ', 'rss-stop gozerbot')
 
622
 
 
623
def handle_rsschannels(bot, ievent):
 
624
    """ rss-channels <name> .. show channels of rss feed """
 
625
    try:
 
626
        name = ievent.args[0]
 
627
    except IndexError:
 
628
        ievent.missing("<name>") 
 
629
        return
 
630
    rssitem = watcher.byname(name)
 
631
    if rssitem == None:
 
632
        ievent.reply("we don't have a %s rss object" % name)
 
633
        return
 
634
    if not rssitem.watchchannels:
 
635
        ievent.reply('%s is not in watch mode' % name)
 
636
        return
 
637
    result = []
 
638
    for i in rssitem.watchchannels:
 
639
        result.append(str(i))
 
640
    ievent.reply("channels of %s: " % name, result, dot=True)
 
641
 
 
642
cmnds.add('rss-channels', handle_rsschannels, 'OPER')
 
643
examples.add('rss-channels', 'rss-channels <name> .. show channels', \
 
644
'rss-channels gozerbot')
 
645
 
 
646
def handle_rssaddchannel(bot, ievent):
 
647
    """ rss-addchannel <name> [<botname>] <channel> .. add a channel to \
 
648
        rss item """
 
649
    try:
 
650
        (name, botname, channel) = ievent.args
 
651
    except ValueError:
 
652
        try:
 
653
            (name, channel) = ievent.args
 
654
            botname = bot.name
 
655
        except ValueError:
 
656
            try:
 
657
                name = ievent.args[0]
 
658
                botname = bot.name
 
659
                channel = ievent.channel
 
660
            except IndexError:
 
661
                ievent.missing('<name> [<botname>] <channel>')
 
662
                return
 
663
    rssitem = watcher.byname(name)
 
664
    if rssitem == None:
 
665
        ievent.reply("we don't have a %s rss object" % name)
 
666
        return
 
667
    if not rssitem.running:
 
668
        ievent.reply('%s watcher is not running' % name)
 
669
        return
 
670
    if (botname, channel) in rssitem.watchchannels:
 
671
        ievent.reply('we are already monitoring %s on (%s,%s)' % \
 
672
(name, botname, channel))
 
673
        return
 
674
    rssitem.watchchannels.append((botname, channel))
 
675
    watcher.save()
 
676
    ievent.reply('%s added to %s rss item' % (channel, name))
 
677
 
 
678
cmnds.add('rss-addchannel', handle_rssaddchannel, 'OPER')
 
679
examples.add('rss-addchannel', 'rss-addchannel <name> [<botname>] <channel> \
 
680
..add <channel> or <botname> <channel> to watchchannels of <name>', \
 
681
'1) rss-addchannel gozerbot #dunkbots 2) rss-addchannel gozerbot main \
 
682
#dunkbots')
 
683
 
 
684
def handle_rssadditem(bot, ievent):
 
685
    try:
 
686
        (name, item) = ievent.args
 
687
    except ValueError:
 
688
        ievent.missing('<name> <item>')
 
689
        return
 
690
    if bot.jabber or users.allowed(ievent.userhost, ['OPER', ]):
 
691
        target = ievent.channel.lower()
 
692
    else:
 
693
        target = ievent.nick.lower()
 
694
    if not watcher.byname(name):
 
695
        ievent.reply("we don't have a %s feed" % name)
 
696
        return
 
697
    watcher.itemslists.adduniq((name, target), item)
 
698
    watcher.itemslists.save()
 
699
    #watcher.sync(name)
 
700
    ievent.reply('%s added to (%s,%s) itemslist' % (item, name, target))
 
701
 
 
702
cmnds.add('rss-additem', handle_rssadditem, ['RSS', 'USER'])
 
703
examples.add('rss-additem', 'add a token to the itemslist (per user/channel)',\
 
704
 'rss-additem gozerbot link')
 
705
 
 
706
def handle_rssdelitem(bot, ievent):
 
707
    try:
 
708
        (name, item) = ievent.args
 
709
    except ValueError:
 
710
        ievent.missing('<name> <item>')
 
711
        return
 
712
    if users.allowed(ievent.userhost, ['OPER', 'RSS']):
 
713
        target = ievent.channel.lower()
 
714
    else:
 
715
        target = ievent.nick.lower()
 
716
    if not watcher.byname(name):
 
717
        ievent.reply("we don't have a %s feed" % name)
 
718
        return
 
719
    try:
 
720
        watcher.itemslists.remove((name, target), item)
 
721
        watcher.itemslists.save()
 
722
    except RssNoSuchItem:
 
723
        ievent.reply("we don't have a %s rss feed" % name)
 
724
        return
 
725
    ievent.reply('%s removed from (%s,%s) itemslist' % (item, name, target))
 
726
 
 
727
cmnds.add('rss-delitem', handle_rssdelitem, ['RSS', 'USER'])
 
728
examples.add('rss-delitem', 'remove a token from the itemslist \
 
729
(per user/channel)', 'rss-delitem gozerbot link')
 
730
 
 
731
def handle_rssmarkup(bot, ievent):
 
732
    try:
 
733
        name = ievent.args[0]
 
734
    except IndexError:
 
735
        ievent.missing('<name>')
 
736
        return
 
737
    if users.allowed(ievent.userhost, ['OPER', ]):
 
738
        target = ievent.channel.lower()
 
739
    else:
 
740
        target = ievent.nick.lower()
 
741
    try:
 
742
        ievent.reply(str(watcher.markup[(name, target)]))
 
743
    except KeyError:
 
744
        pass
 
745
 
 
746
cmnds.add('rss-markup', handle_rssmarkup, ['RSS', 'USER'])
 
747
examples.add('rss-markup', 'show markup list for a feed (per user/channel)', \
 
748
'rss-markup gozerbot')
 
749
 
 
750
def handle_rssaddmarkup(bot, ievent):
 
751
    try:
 
752
        (name, item, value) = ievent.args
 
753
    except ValueError:
 
754
        ievent.missing('<name> <item> <value>')
 
755
        return
 
756
    if users.allowed(ievent.userhost, ['OPER', ]):
 
757
        target = ievent.channel.lower()
 
758
    else:
 
759
        target = ievent.nick.lower()
 
760
    try:
 
761
        watcher.markup.set((name, target), item, value)
 
762
        watcher.markup.save()
 
763
        ievent.reply('%s added to (%s,%s) markuplist' % (item, name, target))
 
764
    except KeyError:
 
765
        ievent.reply("no (%s,%s) feed available" % (name, target))
 
766
 
 
767
cmnds.add('rss-addmarkup', handle_rssaddmarkup, ['RSS', 'USER'])
 
768
examples.add('rss-addmarkup', 'add a markup option to the markuplist \
 
769
(per user/channel)', 'rss-addmarkup gozerbot noseperator 1')
 
770
 
 
771
def handle_rssdelmarkup(bot, ievent):
 
772
    try:
 
773
        (name, item) = ievent.args
 
774
    except ValueError:
 
775
        ievent.missing('<name> <item>')
 
776
        return
 
777
    if users.allowed(ievent.userhost, ['OPER', 'RSS']):
 
778
        target = ievent.channel.lower()
 
779
    else:
 
780
        target = ievent.nick.lower()
 
781
    try:
 
782
        del watcher.markup[(name, target)][item]
 
783
    except (KeyError, TypeError):
 
784
        ievent.reply("can't remove %s from %s feed's markup" %  (item, name))
 
785
        return
 
786
    watcher.markup.save()
 
787
    ievent.reply('%s removed from (%s,%s) markuplist' % (item, name, target))
 
788
 
 
789
cmnds.add('rss-delmarkup', handle_rssdelmarkup, ['RSS', 'USER'])
 
790
examples.add('rss-delmarkup', 'remove a markup option from the markuplist \
 
791
(per user/channel)', 'rss-delmarkup gozerbot noseperator')
 
792
 
 
793
def handle_rssdelchannel(bot, ievent):
 
794
    """ rss-delchannel <name> [<botname>] <channel> .. delete channel \
 
795
        from rss item """
 
796
    botname = None
 
797
    try:
 
798
        (name, botname, channel) = ievent.args
 
799
    except ValueError:
 
800
        try:
 
801
            (name, channel) = ievent.args
 
802
            botname = bot.name
 
803
        except ValueError:
 
804
            try:
 
805
                name = ievent.args[0]
 
806
                botname = bot.name
 
807
                channel = ievent.channel
 
808
            except IndexError:
 
809
                ievent.missing('<name> [<botname>] [<channel>]')
 
810
                return
 
811
    rssitem = watcher.byname(name)
 
812
    if rssitem == None:
 
813
        ievent.reply("we don't have a %s rss object" % name)
 
814
        return
 
815
    if (botname, channel) not in rssitem.watchchannels:
 
816
        ievent.reply('we are not monitoring %s on (%s,%s)' % \
 
817
(name, botname, channel))
 
818
        return
 
819
    rssitem.watchchannels.remove((botname, channel))
 
820
    ievent.reply('%s removed from %s rss item' % (channel, name))
 
821
    watcher.save()
 
822
 
 
823
cmnds.add('rss-delchannel', handle_rssdelchannel, 'OPER')
 
824
examples.add('rss-delchannel', 'rss-delchannel <name> [<botname>] \
 
825
[<channel>] .. delete <channel> or <botname> <channel> from watchchannels of \
 
826
<name>', '1) rss-delchannel gozerbot #dunkbots 2) rss-delchannel gozerbot \
 
827
main #dunkbots')
 
828
 
 
829
def handle_rssstopwatch(bot, ievent):
 
830
    """ rss-stopwatch <name> .. stop a watcher thread """
 
831
    try:
 
832
        name = ievent.args[0]
 
833
    except IndexError:
 
834
        ievent.missing('<name>')
 
835
        return
 
836
    rssitem = watcher.byname(name)
 
837
    if not rssitem:
 
838
        ievent.reply("there is no %s rssitem" % name)
 
839
        return
 
840
    if not watcher.stopwatch(name):
 
841
        ievent.reply("can't stop %s watcher" % name)
 
842
        return
 
843
    ievent.reply('stopped %s rss watch' % name)
 
844
 
 
845
cmnds.add('rss-stopwatch', handle_rssstopwatch, 'OPER')
 
846
examples.add('rss-stopwatch', 'rss-stopwatch <name> .. stop polling <name>', \
 
847
'rss-stopwatch gozerbot')
 
848
 
 
849
def handle_rsssleeptime(bot, ievent):
 
850
    """ rss-sleeptime <name> .. get sleeptime of rss item """
 
851
    try:
 
852
        name = ievent.args[0]
 
853
    except IndexError:
 
854
        ievent.missing('<name>')
 
855
        return
 
856
    rssitem = watcher.byname(name)
 
857
    if rssitem == None:
 
858
        ievent.reply("we don't have a %s rss item" % name)
 
859
        return
 
860
    try:
 
861
        ievent.reply('sleeptime for %s is %s seconds' % (name, \
 
862
str(rssitem.sleeptime)))
 
863
    except AttributeError:
 
864
        ievent.reply("can't get sleeptime for %s" % name)
 
865
 
 
866
cmnds.add('rss-sleeptime', handle_rsssleeptime, 'OPER')
 
867
examples.add('rss-sleeptime', 'rss-sleeptime <name> .. get sleeping time \
 
868
for <name>', 'rss-sleeptime gozerbot')
 
869
 
 
870
def handle_rsssetsleeptime(bot, ievent):
 
871
    """ rss-setsleeptime <name> <seconds> .. set sleeptime of rss item """
 
872
    try:
 
873
        (name, sec) = ievent.args
 
874
        sec = int(sec)
 
875
    except ValueError:
 
876
        ievent.missing('<name> <seconds>')
 
877
        return
 
878
    if sec < 60:
 
879
        ievent.reply('min is 60 seconds')
 
880
        return
 
881
    rssitem = watcher.byname(name)
 
882
    if rssitem == None:
 
883
        ievent.reply("we don't have a %s rss item" % name)
 
884
        return
 
885
    rssitem.sleeptime = sec
 
886
    if rssitem.running:
 
887
        watcher.changeinterval(name, sec)
 
888
    watcher.save()
 
889
    ievent.reply('sleeptime set')
 
890
 
 
891
cmnds.add('rss-setsleeptime', handle_rsssetsleeptime, 'OPER')
 
892
examples.add('rss-setsleeptime', 'rss-setsleeptime <name> <seconds> .. set \
 
893
sleeping time for <name> .. min 60 sec', 'rss-setsleeptime gozerbot 600')
 
894
 
 
895
def handle_rssget(bot, ievent):
 
896
    """ rss-get <name> .. fetch rss data """
 
897
    try:
 
898
        name = ievent.args[0]
 
899
    except IndexError:
 
900
        ievent.missing('<name>')
 
901
        return
 
902
    rssitem = watcher.byname(name)
 
903
    if rssitem == None:
 
904
        ievent.reply("we don't have a %s rss item" % name)
 
905
        return
 
906
    try:
 
907
        result = watcher.getdata(name)
 
908
    except Exception, ex:
 
909
        ievent.reply('%s error: %s' % (name, str(ex)))
 
910
        return
 
911
    response = watcher.makeresponse(name, result, ievent.channel)
 
912
    if response:
 
913
        ievent.reply("results of %s: %s" % (name, response))
 
914
    else:
 
915
        ievent.reply("can't match watcher data")
 
916
 
 
917
cmnds.add('rss-get', handle_rssget, ['RSS', 'USER', 'ANON'])
 
918
examples.add('rss-get', 'rss-get <name> .. get data from <name>', \
 
919
'rss-get gozerbot')
 
920
 
 
921
def handle_rssrunning(bot, ievent):
 
922
    """ rss-running .. show which watchers are running """
 
923
    result = watcher.runners()
 
924
    resultlist = []
 
925
    teller = 1
 
926
    for i in result:
 
927
        resultlist.append("%s %s" % (i[0], i[1]))
 
928
    if resultlist:
 
929
        ievent.reply("running rss watchers: ", resultlist, nr=1)
 
930
    else:
 
931
        ievent.reply('nothing running yet')
 
932
 
 
933
cmnds.add('rss-running', handle_rssrunning, ['RSS', 'USER'])
 
934
examples.add('rss-running', 'rss-running .. get running rsswatchers', \
 
935
'rss-running')
 
936
 
 
937
def handle_rsslist(bot, ievent):
 
938
    """ rss-list .. return list of rss items """
 
939
    result = watcher.list()
 
940
    result.sort()
 
941
    if result:
 
942
        ievent.reply("rss items: ", result, dot=True)
 
943
    else:
 
944
        ievent.reply('no rss items yet')
 
945
 
 
946
cmnds.add('rss-list', handle_rsslist, ['RSS', 'USER', 'ANON'])
 
947
examples.add('rss-list', 'get list of rss items', 'rss-list')
 
948
 
 
949
def handle_rssurl(bot, ievent):
 
950
    """ rss-url <name> .. return url of rss item """
 
951
    try:
 
952
        name = ievent.args[0]
 
953
    except IndexError:
 
954
        ievent.missing('<name>')
 
955
        return
 
956
    result = watcher.url(name)
 
957
    ievent.reply('url of %s: %s' % (name, result))
 
958
 
 
959
cmnds.add('rss-url', handle_rssurl, ['RSS', 'USER', 'ANON'])
 
960
examples.add('rss-url', 'rss-url <name> .. get url from rssitem with \
 
961
<name>', 'rss-url gozerbot')
 
962
 
 
963
def handle_rssitemslist(bot, ievent):
 
964
    """ rss-itemslist <name> .. show itemslist of rss item """
 
965
    try:
 
966
        name = ievent.args[0]
 
967
    except IndexError:
 
968
        ievent.missing('<name>')
 
969
        return
 
970
    try:
 
971
        itemslist = watcher.itemslists[(name, ievent.channel.lower())]
 
972
    except KeyError:
 
973
        ievent.reply("no itemslist set for (%s, %s)" % (name, \
 
974
ievent.channel.lower()))
 
975
        return
 
976
    ievent.reply("itemslist of (%s, %s): " % (name, ievent.channel.lower()), \
 
977
itemslist, dot=True)
 
978
 
 
979
cmnds.add('rss-itemslist', handle_rssitemslist, ['RSS', 'USER'])
 
980
examples.add('rss-itemslist', 'rss-itemslist <name> .. get itemslist of \
 
981
<name> ', 'rss-itemslist gozerbot')
 
982
 
 
983
def handle_rssscan(bot, ievent):
 
984
    """ rss-scan <name> .. scan rss item for used xml items """
 
985
    try:
 
986
        name = ievent.args[0]
 
987
    except IndexError:
 
988
        ievent.missing('<name>')
 
989
        return
 
990
    if not watcher.byname(name):
 
991
        ievent.reply('no %s feeds available' % name)
 
992
        return
 
993
    try:
 
994
        result = watcher.scan(name)
 
995
    except Exception, ex:
 
996
        ievent.reply(str(ex))
 
997
        return
 
998
    if result == None:
 
999
        ievent.reply("can't get data for %s" % name)
 
1000
        return
 
1001
    res = []
 
1002
    for i in result:
 
1003
        res.append("%s=%s" % i)
 
1004
    ievent.reply("tokens of %s: " % name, res)
 
1005
 
 
1006
cmnds.add('rss-scan', handle_rssscan, 'OPER')
 
1007
examples.add('rss-scan', 'rss-scan <name> .. get possible items of <name> ', \
 
1008
'rss-scan gozerbot')
 
1009
 
 
1010
def handle_rsssync(bot, ievent):
 
1011
    """ rss-sync <name> .. sync rss item data """
 
1012
    try:
 
1013
        name = ievent.args[0]
 
1014
    except IndexError:
 
1015
        ievent.missing('<name>')
 
1016
        return
 
1017
    try:
 
1018
        result = watcher.sync(name)
 
1019
        ievent.reply('%s synced' % name)
 
1020
    except Exception, ex:
 
1021
        ievent.reply("%s error: %s" % (name, str(ex)))
 
1022
 
 
1023
cmnds.add('rss-sync', handle_rsssync, 'OPER')
 
1024
examples.add('rss-sync', 'rss-sync <name> .. sync data of <name>', \
 
1025
'rss-sync gozerbot')
 
1026
 
 
1027
def handle_rssfeeds(bot, ievent):
 
1028
    """ rss-feeds <channel> .. show what feeds are running in a channel """
 
1029
    try:
 
1030
        channel = ievent.args[0]
 
1031
    except IndexError:
 
1032
        channel = ievent.printto
 
1033
    try:
 
1034
        result = watcher.feeds(bot.name, channel)
 
1035
        if result:
 
1036
            ievent.reply("feeds running in %s: " % channel, result, dot=True)
 
1037
        else:
 
1038
            ievent.reply('%s has no feeds running' % channel)
 
1039
    except Exception, ex:
 
1040
        ievent.reply("ERROR: %s" % str(ex))
 
1041
 
 
1042
cmnds.add('rss-feeds', handle_rssfeeds, ['USER', 'RSS'])
 
1043
examples.add('rss-feeds', 'rss-feeds <name> .. show what feeds are running \
 
1044
in a channel', '1) rss-feeds 2) rss-feeds #dunkbots')
 
1045
 
 
1046
def handle_rsslink(bot, ievent):
 
1047
    try:
 
1048
        feed, rest = ievent.rest.split(' ', 1)
 
1049
    except ValueError:
 
1050
        ievent.missing('<feed> <words to search>')
 
1051
        return
 
1052
    rest = rest.strip().lower()
 
1053
    try:
 
1054
        res = watcher.search(feed, 'link', rest)
 
1055
        if not res:
 
1056
            res = watcher.search(feed, 'feedburner:origLink', rest)
 
1057
        if res:
 
1058
            ievent.reply(res, dot=" \002||\002 ")
 
1059
    except KeyError:
 
1060
        ievent.reply('no %s feed data available' % feed)
 
1061
        return
 
1062
 
 
1063
cmnds.add('rss-link', handle_rsslink, ['RSS', 'USER'])
 
1064
examples.add('rss-link', 'give link of item which title matches search key', \
 
1065
'rss-link gozerbot gozer')
 
1066
 
 
1067
def handle_rssdescription(bot, ievent):
 
1068
    try:
 
1069
        feed, rest = ievent.rest.split(' ', 1)
 
1070
    except ValueError:
 
1071
        ievent.missing('<feed> <words to search>')
 
1072
        return
 
1073
    rest = rest.strip().lower()
 
1074
    res = ""
 
1075
    try:
 
1076
        ievent.reply(watcher.search(feed, 'description', rest), \
 
1077
dot=" \002||\002 ")
 
1078
    except KeyError:
 
1079
        ievent.reply('no %s feed data available' % feed)
 
1080
        return
 
1081
 
 
1082
cmnds.add('rss-description', handle_rssdescription, ['RSS', 'USER'])
 
1083
examples.add('rss-description', 'give description of item which title \
 
1084
matches search key', 'rss-description gozerbot gozer')
 
1085
 
 
1086
def handle_rssall(bot, ievent):
 
1087
    try:
 
1088
        feed = ievent.args[0]
 
1089
    except IndexError:
 
1090
        ievent.missing('<feed>')
 
1091
        return
 
1092
    try:
 
1093
        ievent.reply(watcher.all(feed, 'title'), dot=" \002||\002 ")
 
1094
    except KeyError:
 
1095
        ievent.reply('no %s feed data available' % feed)
 
1096
        return
 
1097
 
 
1098
cmnds.add('rss-all', handle_rssall, ['RSS', 'USER'])
 
1099
examples.add('rss-all', "give titles of a feed", 'rss-all gozerbot')
 
1100
 
 
1101
def handle_rsssearch(bot, ievent):
 
1102
    try:
 
1103
        txt = ievent.args[0]
 
1104
    except IndexError:
 
1105
        ievent.missing('<txt>')
 
1106
        return
 
1107
    try:        
 
1108
        ievent.reply(watcher.searchall('title', txt), dot=" \002||\002 ")
 
1109
    except KeyError:
 
1110
        ievent.reply('no %s feed data available' % feed)
 
1111
        return
 
1112
 
 
1113
cmnds.add('rss-search', handle_rsssearch, ['RSS', 'USER'])
 
1114
examples.add('rss-search', "search titles of all current feeds", \
 
1115
'rss-search goz')
 
1116
 
 
1117
def handle_rssdump(bot, ievent):
 
1118
    try:
 
1119
        ievent.reply(str(watcher.rawresults[ievent.rest]))
 
1120
    except Exception, ex:
 
1121
        ievent.reply(str(ex))
 
1122
 
 
1123
cmnds.add('rss-dump', handle_rssdump, 'OPER')
 
1124
examples.add('rss-dump', 'dump cached rss data', 'rss-dump')