root/hodgestar/PythonCode/SmallScripts/seymour.py

Revision 375, 7.6 kB (checked in by simon, 4 years ago)

Miscellaneous small scripts.

  • Property svn:mime-type set to text/python-source
  • Property svn:eol-style set to native
Line 
1#!/usr/bin/env python
2
3# By Simon Cross
4
5"""
6Monitors RSS feeds for changes, updating a given channel when they occur.
7"""
8
9import plugins
10import time
11import rssparser
12import utils
13import privmsgs
14import callbacks
15import ircdb
16import threading
17import ircmsgs
18import schedule
19import conf
20
21def configure(onStart, afterConnect, advanced):
22# Do configuration magic here
23        onStart.append('load seymourRSS')
24
25example = utils.wrapLines("""
26<Hodgestar> Put example usage here.
27""")
28
29class seymourRSS(callbacks.Privmsg):
30        threaded = True
31        def __init__(self):
32                callbacks.Privmsg.__init__(self)
33                self.EventKey = "seymourRSSupdateCheck"
34                self.time = 30
35                self.flock = threading.RLock() # feeds lock
36                self.llock = threading.RLock() # listeners lock
37                self.slock = threading.RLock() # scheduler lock
38                # Dictionary of feed tuples: url, results (key is shortname)
39                self.feeds = {}
40                # Dictionary of feed listeners: shortname, irc, channel
41                self.listeners = []
42                self.upd = updater(self)
43                self.upd.start()
44
45        def startseymour(self, irc, msg, args):
46                """
47                Adds the SeymourRSS update checker to the list of periodic events.
48                """
49                if not ircdb.checkCapability(msg.prefix, 'admin'):
50                        irc.error(msg, conf.replyNoCapability % 'admin')
51                        return
52               
53                self.slock.acquire()
54                if schedule.schedule.events.has_key(self.EventKey):
55                        rep = "SeymourRSS feed monitor already running."
56                else:
57                        schedule.addPeriodicEvent(self.checkForUpdates, self.time, self.EventKey)
58                        rep = "SeymourRSS feed monitor started."
59                self.slock.release()
60                irc.reply(msg, rep)
61
62        def stopseymour(self, irc, msg, args):
63                """
64                Removes the SeymourRSS update checker from the list of events.
65                """
66                if not ircdb.checkCapability(msg.prefix, 'admin'):
67                        irc.error(msg, conf.replyNoCapability % 'admin')
68                        return
69                       
70                self.slock.acquire()
71                if schedule.schedule.events.has_key(self.EventKey):     
72                        schedule.removeEvent(self.EventKey)
73                        rep = "SeymourRSS feed monitor stopped."
74                else:
75                        rep = "SeymourRSS feed monitor was not running."
76                self.slock.release()
77                irc.reply(msg, rep)
78
79        def setseymourtime(self, irc, msg, args):
80                """<time in seconds>
81                Sets the frequency with which the Seymour updater checks RSS feeds.
82                You need to restart the updated using startseymour and stopseymour for
83                this to take effect.
84                """
85                if not ircdb.checkCapability(msg.prefix, 'admin'):
86                        irc.error(msg, conf.replyNoCapability % 'admin')
87                        return
88
89                if len(args) != 1:
90                        irc.error(msg,"Usage: setseymourtime <time in seconds>")
91                        return
92
93                self.time = int(args[0])
94                if self.time <= 0:
95                        self.time = 600
96                irc.reply(msg,"Frequency changed to " + str(self.time) + " seconds.")
97
98        def addfeed(self, irc, msg, args):
99                """<shortname> <url>
100                Adds the RSS feed given by <url> to the list of feeds checked
101                checked periodically the update checker.  You'll also need to
102                add a listener to a feed in order to receive updates.
103                <shortname> is a unique identifier for the feed.
104                """
105                if not ircdb.checkCapability(msg.prefix, 'admin'):
106                        irc.error(msg, conf.replyNoCapability % 'admin')
107                        return
108               
109                if len(args) != 2:
110                        irc.error(msg,"Usage: addfeed <shortname> <url>")
111                        return
112               
113                key = args[0]
114                url = args[1]
115                       
116                self.flock.acquire()
117                key_exists = self.feeds.has_key(key)
118                self.flock.release()
119               
120                if key_exists:
121                        irc.error(msg,key + " must be a unique name for the feed.")
122                        return
123               
124                results = rssparser.parse(url)
125               
126                self.flock.acquire()
127                self.feeds[key] = url, {}
128                self.flock.release()
129               
130                irc.reply(msg,url + " added to feed list as " + key + ".")
131       
132        def removefeed(self, irc, msg, args):
133                """<shortname>
134                Remove the given RSS feed (<shortname> is its unique id) from the list of feeds
135                checked for updates.
136                """
137                if not ircdb.checkCapability(msg.prefix, 'admin'):
138                        irc.error(msg, conf.replyNoCapability % 'admin')
139                        return
140                       
141                if len(args) != 1:
142                        irc.error(msg,"Usage: removefeed <shortname>")
143                        return
144               
145                key = shortname
146               
147                self.flock.acquire()
148                if (key not in self.feeds):
149                        rep = key + " not present in list of feeds."
150                elif (key in self.listeners.values()):
151                        rep = key + " there are still some listeners using this feed."
152                else:
153                        del self.feeds[key]     
154                        rep = url + " successfully deleted from list for " + channel + "."
155                self.flock.release()
156                irc.reply(msg, rep)
157
158        def showfeeds(self, irc, msg, args):
159                """
160                Prints out a list of feeds.
161                """
162                self.flock.acquire()
163                feedcopy = self.feeds
164                self.flock.release()
165       
166                feedkeys = feedcopy.keys()
167                if len(feedkeys) == 0:
168                        irc.reply(msg,"The feed list is empty.")
169                for key in feedkeys:
170                        url = feedcopy[key][0]
171                        irc.reply(msg,"[" + key + "] " + url + ".")
172
173        def flushfeeds(self, irc, msg, args):
174                """
175                Flushes all cached information, forcing feeds to appear to be updated.
176                """
177                if not ircdb.checkCapability(msg.prefix, 'admin'):
178                        irc.error(msg, conf.replyNoCapability % 'admin')
179                        return
180               
181                self.flock.acquire()
182                for key in self.feeds.keys():
183                        self.feeds[key] = self.feeds[key][0], {}
184                self.flock.release()
185
186                irc.reply(msg,"Flush complete.")
187
188        def addlistener(self, irc, msg, args):
189                """<shortname> <channel/nick>
190                Add a feed listener for the feed <shortname> which sends updates
191                to <channel/nick>.
192                """     
193                if len(args) != 2:
194                        irc.reply(msg, "Usage: addlistener <shortname> <channel/nick>")
195                        return
196                listener = args[0], irc, args[1]
197
198                self.llock.acquire()
199                self.listeners.append(listener)
200                self.llock.release()
201
202                irc.reply(msg, "Listener added.")
203               
204        def removelistener(self, irc, msg, args):
205                """<shortname> <channel/nick>
206                Remove a feed listener.
207                """
208                if len(args) != 2:
209                        irc.reply(msg, "Usage: removelistener <shortname> <channel/nick>")
210                        return
211                listener = args[0], irc, args[1]
212
213                self.llock.acquire()
214                if self.listeners.count(listener) == 0:
215                        rep = "Listener not present."
216                else:
217                        self.listeners.remove(listener)
218                        rep = "Listener successfully removed."
219                self.llock.release()
220               
221                irc.reply (msg, rep)
222
223        def showlisteners(self, irc, msg, args):
224                """
225                Display the feed listners
226                """
227
228                self.llock.acquire()
229                lcopy = self.listeners
230                self.llock.release()
231
232                if len(lcopy) == 0:
233                        irc.reply(msg, "No listeners present.")
234                else:
235                        for l in lcopy:
236                                irc.reply(msg, "[" + l[0] + "] " + l[2]) # display irc (l[1]) somehow?
237
238        # methods which are not commands
239        # have capital letters
240
241        def extractListFromItems(self,key,results):
242                R = []
243                if results.has_key('items'):
244                        for d in results['items']:
245                                if d.has_key(key):
246                                        R.append(d[key].strip().replace('\n',' '))
247                return R
248
249        def checkForUpdates(self):
250                self.flock.acquire()
251                fcopy = self.feeds
252                self.flock.release()
253               
254                self.llock.acquire()
255                lcopy = self.listeners
256                self.llock.release()
257               
258                updatedFeeds = {}
259
260                for key in fcopy.keys():
261                        url = fcopy[key][0]
262                        results = rssparser.parse(url)
263                        new_titles = self.extractListFromItems('title',results)
264                        new_dates = self.extractListFromItems('dc:date',results)
265                        old_titles = self.extractListFromItems('title',fcopy[key][1])
266                        old_dates = self.extractListFromItems('dc:date',fcopy[key][1])
267                        if (new_titles != old_titles) or (new_dates != old_dates):
268                                updatedFeeds[key] = results
269                       
270                for key in updatedFeeds.keys():
271                        def eqkey(x): return x[0] == key
272                       
273                        ears = filter(eqkey,lcopy)
274                        for e in ears:
275                                irc = e[1]
276                                channel = e[2]
277                                irc.queueMsg(ircmsgs.privmsg(channel,key + " has been updated."))
278                       
279                self.flock.acquire()
280                for key in updatedFeeds.keys():
281                        if self.feeds.has_key(key):
282                                self.feeds[key] = self.feeds[key][0], updatedFeeds[key]
283                self.flock.release()
284
285Class = seymourRSS
286
287class updater(threading.Thread):
288        def __init__(self,parent):
289                threading.Thread.__init__(self)
290                self.parent = parent
291
292        def run(self):
293                while self.isAlive():
294                        self.parent.checkForUpdates()
295                        time.sleep(self.parent.time)
296
Note: See TracBrowser for help on using the browser.