Handle lots of channels better

Get rid of threads and make all actions synchronous instead (to
make sure we are measuring our commands to the server so we don't
flood).

Don't explicitly ask for the topics to save.  Instead, notice the
topics when joining and keep updating the local copy (except when
we are updating the topic) to speed up the process of changing
topics.

Move the setting of the topic and the broadcast closer together so
that channels don't wait a long time between the announcement
and the topic change.

Give the user confirmation of what's going on, since their channel
might not be near the beginning or end of the list.

Change-Id: I317062268bc591c5ed9db0cb1bbc6fe0e79bec5e
This commit is contained in:
James E. Blair 2014-03-07 10:01:00 -08:00
parent 34429fa549
commit d98505cbb3
1 changed files with 38 additions and 62 deletions

View File

@ -40,7 +40,6 @@ import json
import logging.config
import os
import tempfile
import threading
import time
import simplemediawiki
import datetime
@ -195,9 +194,8 @@ class StatusBot(irc.bot.SingleServerIRCBot):
self.nickname = nickname
self.password = password
self.identify_msg_cap = False
self.ignore_topics = True
self.topic_lock = threading.Lock()
self.topics = {}
self.current_topic = None
self.publishers = publishers
def on_nicknameinuse(self, c, e):
@ -245,96 +243,74 @@ class StatusBot(irc.bot.SingleServerIRCBot):
self.log.debug("Ignoring message from untrusted user %s" % nick)
return
try:
self.handle_command(nick, msg)
self.handle_command(e.target, nick, msg)
except:
self.log.exception("Exception handling command %s" % msg)
def handle_command(self, nick, msg):
def handle_command(self, channel, nick, msg):
parts = msg.split()
command = parts[1].lower()
text = ' '.join(parts[2:])
if command == 'alert':
self.log.info("Processing alert from %s: %s" % (nick, text))
self.set_all_topics(text)
self.broadcast('NOTICE: ' + text)
self.send(channel, "%s: sending alert" % (nick,))
self.broadcast('NOTICE: ', text, set_topic=True)
for p in self.publishers:
p.alert(text)
self.send(channel, "%s: finished sending alert" % (nick,))
elif command == 'notice':
self.log.info("Processing notice from %s: %s" % (nick, text))
self.broadcast('NOTICE: ' + text)
self.send(channel, "%s: sending notice" % (nick,))
self.broadcast('NOTICE: ', text)
for p in self.publishers:
p.notice(text)
self.send(channel, "%s: finished sending notice" % (nick,))
elif command == 'log':
self.log.info("Processing log from %s: %s" % (nick, text))
for p in self.publishers:
p.log(text)
self.send(channel, "%s: finished logging" % (nick,))
elif command == 'ok':
self.log.info("Processing ok from %s: %s" % (nick, text))
self.restore_all_topics()
if text:
self.broadcast('NOTICE: ' + text)
self.send(channel, "%s: sending ok" % (nick,))
self.broadcast('NOTICE: ', text, restore_topic=True)
for p in self.publishers:
p.ok(text)
self.send(channel, "%s: finished sending ok" % (nick,))
else:
self.send(channel, "%s: unknown command" % (nick,))
self.log.info("Unknown command %s from %s: %s" % (
command, nick, msg))
def broadcast(self, msg):
def broadcast(self, prefix, msg, set_topic=False, restore_topic=False):
if set_topic:
self.current_topic = msg
for channel in self.channel_list:
self.send(channel, msg)
def restore_all_topics(self):
t = threading.Thread(target=self._restore_all_topics, args=())
t.start()
def _restore_all_topics(self):
self.topic_lock.acquire()
try:
if self.topics:
for channel in self.channel_list:
self.set_topic(channel, self.topics[channel])
self.topics = {}
finally:
self.topic_lock.release()
def set_all_topics(self, topic):
t = threading.Thread(target=self._set_all_topics, args=(topic,))
t.start()
def _set_all_topics(self, topic):
self.topic_lock.acquire()
try:
if not self.topics:
self.save_topics()
for channel in self.channel_list:
self.set_topic(channel, topic)
finally:
self.topic_lock.release()
def save_topics(self):
# Save all the current topics
self.ignore_topics = False
for channel in self.channel_list:
self.connection.topic(channel)
time.sleep(0.5)
start = time.time()
done = False
while time.time() < start + 300:
if len(self.topics) == len(self.channel_list):
done = True
break
time.sleep(0.5)
self.ignore_topics = True
if not done:
raise Exception("Unable to save topics")
if restore_topic:
# Set to the saved topic or just the channel name if
# we don't have a saved topic (to avoid leaving it as
# the alert).
t = self.topics.get(channel, channel)
self.set_topic(channel, t)
if msg:
self.send(channel, prefix + msg)
if set_topic:
self.set_topic(channel, msg)
def on_currenttopic(self, c, e):
if self.ignore_topics:
channel, topic = (e.arguments[0], e.arguments[1])
self.update_saved_topic(channel, topic)
def on_topic(self, c, e):
channel, topic = (e.target, e.arguments[0])
self.update_saved_topic(channel, topic)
def update_saved_topic(self, channel, topic):
if topic == self.current_topic:
return
self.log.info("Current topic on %s is %s" % (e.arguments[0],
e.arguments[1]))
self.topics[e.arguments[0]] = e.arguments[1]
self.log.info("Current topic on %s is %s" % (channel, topic))
self.topics[channel] = topic
def send(self, channel, msg):
self.connection.privmsg(channel, msg)