mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-02-19 23:20:57 +01:00
Added some lockingEXCLAIM w00rEXCLAIM LocksEXCLAIM
This commit is contained in:
parent
c198f79352
commit
f77f48d0f3
101
plugins/RSS.py
101
plugins/RSS.py
@ -95,9 +95,9 @@ class RSS(callbacks.Privmsg):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
callbacks.Privmsg.__init__(self)
|
callbacks.Privmsg.__init__(self)
|
||||||
self.feedNames = sets.Set()
|
self.feedNames = sets.Set()
|
||||||
|
self.locks = {}
|
||||||
self.lastRequest = {}
|
self.lastRequest = {}
|
||||||
self.cachedFeeds = {}
|
self.cachedFeeds = {}
|
||||||
self.currentlyDownloading = sets.Set()
|
|
||||||
for (name, url) in registry._cache.iteritems():
|
for (name, url) in registry._cache.iteritems():
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
if name.startswith('supybot.plugins.rss.feeds.'):
|
if name.startswith('supybot.plugins.rss.feeds.'):
|
||||||
@ -121,53 +121,71 @@ class RSS(callbacks.Privmsg):
|
|||||||
url = name
|
url = name
|
||||||
if self.willGetNewFeed(url):
|
if self.willGetNewFeed(url):
|
||||||
newFeeds.setdefault(url, []).append(channel)
|
newFeeds.setdefault(url, []).append(channel)
|
||||||
for (feed, channels) in newFeeds.iteritems():
|
for (url, channels) in newFeeds.iteritems():
|
||||||
t = threading.Thread(target=self._newHeadlines,
|
# We check if we can acquire the lock right here because if we
|
||||||
name='Fetching <%s>' % url,
|
# don't, we'll possibly end up spawning a lot of threads to get
|
||||||
args=(irc, channels, name, url))
|
# the feed, because this thread may run for a number of bytecodes
|
||||||
self.log.info('Spawning thread to fetch <%s>', url)
|
# before it switches to a thread that'll get the lock in
|
||||||
world.threadsSpawned += 1
|
# _newHeadlines.
|
||||||
t.setDaemon(True)
|
if self.locks[url].acquire(blocking=False):
|
||||||
t.start()
|
try:
|
||||||
|
t = threading.Thread(target=self._newHeadlines,
|
||||||
|
name='Fetching <%s>' % url,
|
||||||
|
args=(irc, channels, name, url))
|
||||||
|
self.log.info('Spawning thread to fetch <%s>', url)
|
||||||
|
world.threadsSpawned += 1
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
|
finally:
|
||||||
|
self.locks[url].release()
|
||||||
|
|
||||||
def _newHeadlines(self, irc, channels, name, url):
|
def _newHeadlines(self, irc, channels, name, url):
|
||||||
try:
|
try:
|
||||||
oldresults = self.cachedFeeds[url]
|
# We acquire the lock here so there's only one announcement thread
|
||||||
oldheadlines = self.getHeadlines(oldresults)
|
# in this code at any given time. Otherwise, several announcement
|
||||||
except KeyError:
|
# threads will getFeed (all blocking, in turn); then they'll all
|
||||||
oldheadlines = []
|
# want to sent their news messages to the appropriate channels.
|
||||||
newresults = self.getFeed(url)
|
self.locks[url].acquire()
|
||||||
newheadlines = self.getHeadlines(newresults)
|
|
||||||
for headline in oldheadlines:
|
|
||||||
try:
|
try:
|
||||||
newheadlines.remove(headline)
|
oldresults = self.cachedFeeds[url]
|
||||||
except ValueError:
|
oldheadlines = self.getHeadlines(oldresults)
|
||||||
pass
|
except KeyError:
|
||||||
if newheadlines:
|
oldheadlines = []
|
||||||
for channel in channels:
|
newresults = self.getFeed(url)
|
||||||
bold = self.registryValue('bold', channel)
|
newheadlines = self.getHeadlines(newresults)
|
||||||
sep = self.registryValue('headlineSeparator', channel)
|
for headline in oldheadlines:
|
||||||
prefix = self.registryValue('announcementPrefix', channel)
|
try:
|
||||||
pre = '%s%s: ' % (prefix, name)
|
newheadlines.remove(headline)
|
||||||
if bold:
|
except ValueError:
|
||||||
pre = ircutils.bold(pre)
|
pass
|
||||||
irc.replies(newheadlines, prefixer=pre, joiner=sep,
|
if newheadlines:
|
||||||
to=channel, prefixName=False, private=True)
|
for channel in channels:
|
||||||
|
bold = self.registryValue('bold', channel)
|
||||||
|
sep = self.registryValue('headlineSeparator', channel)
|
||||||
|
prefix = self.registryValue('announcementPrefix', channel)
|
||||||
|
pre = '%s%s: ' % (prefix, name)
|
||||||
|
if bold:
|
||||||
|
pre = ircutils.bold(pre)
|
||||||
|
irc.replies(newheadlines, prefixer=pre, joiner=sep,
|
||||||
|
to=channel, prefixName=False, private=True)
|
||||||
|
finally:
|
||||||
|
self.locks[url].release()
|
||||||
|
|
||||||
def willGetNewFeed(self, url):
|
def willGetNewFeed(self, url):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
wait = self.registryValue('waitPeriod')
|
wait = self.registryValue('waitPeriod')
|
||||||
if url in self.currentlyDownloading:
|
|
||||||
return False
|
|
||||||
if url not in self.lastRequest or now - self.lastRequest[url] > wait:
|
if url not in self.lastRequest or now - self.lastRequest[url] > wait:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getFeed(self, url):
|
def getFeed(self, url):
|
||||||
if self.willGetNewFeed(url):
|
try:
|
||||||
try:
|
# This is the most obvious place to acquire the lock, because a
|
||||||
self.currentlyDownloading.add(url)
|
# malicious user could conceivably flood the bot with rss commands
|
||||||
|
# and DoS the website in question.
|
||||||
|
self.locks[url].acquire()
|
||||||
|
if self.willGetNewFeed(url):
|
||||||
try:
|
try:
|
||||||
self.log.info('Downloading new feed from <%s>', url)
|
self.log.info('Downloading new feed from <%s>', url)
|
||||||
results = rssparser.parse(url)
|
results = rssparser.parse(url)
|
||||||
@ -176,13 +194,13 @@ class RSS(callbacks.Privmsg):
|
|||||||
raise callbacks.Error, 'Invalid (unparseable) RSS feed.'
|
raise callbacks.Error, 'Invalid (unparseable) RSS feed.'
|
||||||
self.cachedFeeds[url] = results
|
self.cachedFeeds[url] = results
|
||||||
self.lastRequest[url] = time.time()
|
self.lastRequest[url] = time.time()
|
||||||
finally:
|
try:
|
||||||
self.currentlyDownloading.discard(url)
|
return self.cachedFeeds[url]
|
||||||
try:
|
except KeyError:
|
||||||
return self.cachedFeeds[url]
|
self.lastRequest[url] = 0
|
||||||
except KeyError:
|
return {'items': [{'title': 'Unable to download feed.'}]}
|
||||||
self.lastRequest[url] = 0
|
finally:
|
||||||
return {'items': {'title': 'Unable to download feed.'}}
|
self.locks[url].release()
|
||||||
|
|
||||||
def getHeadlines(self, feed):
|
def getHeadlines(self, feed):
|
||||||
return [utils.htmlToText(d['title'].strip()) for d in feed['items']]
|
return [utils.htmlToText(d['title'].strip()) for d in feed['items']]
|
||||||
@ -195,6 +213,7 @@ class RSS(callbacks.Privmsg):
|
|||||||
to 1800 (30 minutes) since that's what most websites prefer.
|
to 1800 (30 minutes) since that's what most websites prefer.
|
||||||
""" % (name, url)
|
""" % (name, url)
|
||||||
name = callbacks.canonicalName(name)
|
name = callbacks.canonicalName(name)
|
||||||
|
self.locks[url] = threading.RLock()
|
||||||
if hasattr(self, name):
|
if hasattr(self, name):
|
||||||
s = 'I already have a command in this plugin named %s' % name
|
s = 'I already have a command in this plugin named %s' % name
|
||||||
raise callbacks.Error, s
|
raise callbacks.Error, s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user