Announcement() class.
authorCMDR furrycat <elite@furrycat.net>
Fri, 11 Nov 2016 16:44:38 +0000 (16:44 +0000)
committerCMDR furrycat <elite@furrycat.net>
Fri, 11 Nov 2016 16:46:48 +0000 (16:46 +0000)
plugin/announcements/announcements.py

index 77b8aae..beccaad 100644 (file)
@@ -12,6 +12,11 @@ from plugins import PluginCommand
 import bot
 import cat
 
+class Announcement(object):
+  def __init__(self, row):
+    for k, v in dict(row).items():
+      setattr(self, k, v)
+
 class Announcements(DBConnection):
   def __init__(self, filename = None):
     super(Announcements, self).__init__(filename)
@@ -40,32 +45,32 @@ class Announcements(DBConnection):
       # Delete old announcements.
       now = int(time.time())
       for announcement in self.get_all_announcements(client):
-        if announcement['asap'] == 'true':
+        if announcement.asap == 'true':
           # Don't delete announcements that were requested ASAP.
           continue
-        if not announcement['last_spoke']:
+        if not announcement.last_spoke:
           # Don't delete announcements that haven't played yet.
           continue
-        interval = announcement['interval']
+        interval = announcement.interval
         if not interval:
           interval = 0
-        if now - announcement['last_spoke'] < max(interval, 86400):
+        if now - announcement.last_spoke < max(interval, 86400):
           # Don't delete announcements that aren't old.
           continue
-        if announcement['interval']:
-          if not announcement['end_date']:
+        if announcement.interval:
+          if not announcement.end_date:
             # Don't delete ongoing announcements.
             continue
-          if announcement['end_date'] > now:
+          if announcement.end_date > now:
             # Don't delete announcements that still have time to run.
             continue
         if bot.get('dryrun'):
-          log.info("Would delete old announcement {}".format(announcement['id']))
+          log.info("Would delete old announcement {}".format(announcement.id))
         else:
-          if self.delete_announcement(client, announcement['id']):
-            log.info('Deleted old announcement {}'.format(announcement['id']))
+          if self.delete_announcement(client, announcement.id):
+            log.info('Deleted old announcement {}'.format(announcement.id))
           else:
-            log.info('Failed to delete old announcement {}'.format(announcement['id']))
+            log.info('Failed to delete old announcement {}'.format(announcement.id))
 
       yield from asyncio.sleep(waittime)
 
@@ -84,16 +89,18 @@ class Announcements(DBConnection):
     yield from self.help_announcements(message, *args)
 
   def get_all_announcements(self, client, servers = []):
-    yield from self.get_all_from_table(client, 'announcements', servers)
+    for row in self.get_all_from_table(client, 'announcements', servers):
+      yield Announcement(row)
 
   def get_announcement(self, client, id):
-    return self.get_from_table(client, 'announcements', id)
+    row = self.get_from_table(client, 'announcements', id)
+    return Announcement(row) if row else None
 
   def get_announcements(self, client):
     now = self.now()
     cursor = self.query("select id, server_id, member_id, channel_id, voice_id, sound, attachment, mention, start_date, end_date, interval, probability, last_spoke, asap, message, digest from announcements where (start_date is null or start_date <= ?) and (end_date is null or end_date > ? or end_date=start_date) and (asap='true' or last_spoke is null or last_spoke < ? - interval) and (last_spoke is null or interval > 0) and bot_id=?", [now, now, now, client.user.id])
     for row in cursor.fetchall():
-      yield dict(row)
+      yield Announcement(row)
     self.close_db()
 
   def create_announcement(self, client, **args):
@@ -122,25 +129,25 @@ class Announcements(DBConnection):
     # Anyone on the server can show details of an announcement.
     if command == 'show':
       if announcement is not None:
-        if announcement['member_id'] == author.id:
+        if announcement.member_id == author.id:
           log.debug('Anyone can show own announcements.')
           return announcement
-        if announcement['channel_id'] != 'private':
-          server = client.get_server(announcement['server_id'])
+        if announcement.channel_id != 'private':
+          server = client.get_server(announcement.server_id)
           if server is not None:
             member = server.get_member(author.id)
             if member in server.members:
-              log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement['id']))
+              log.debug('Member {} on server {} can show announcement {}.'.format(member.name, server.name, announcement.id))
               return announcement
 
     if command in ['edit', 'delete', 'schedule']:
       if announcement is not None:
-        if announcement['member_id'] == author.id:
+        if announcement.member_id == author.id:
           log.debug('Anyone can manage own announcements.')
           return announcement
-        if announcement['channel_id'] != 'private':
-          server = client.get_server(announcement['server_id'])
-          announcer = server.get_member(announcement['member_id'])
+        if announcement.channel_id != 'private':
+          server = client.get_server(announcement.server_id)
+          announcer = server.get_member(announcement.member_id)
           announcer_role = bot.highest_role(announcer.roles)
           member = server.get_member(author.id)
           member_role = bot.highest_role(member.roles)
@@ -153,11 +160,11 @@ class Announcements(DBConnection):
               return announcement
 
     if command == 'create':
-      if announcement['channel_id'] == 'private':
+      if announcement.channel_id == 'private':
         log.debug('Anyone can schedule a private announcement.')
         return announcement
-      server = client.get_server(announcement['server_id'])
-      member = server.get_member(announcement['member_id'])
+      server = client.get_server(announcement.server_id)
+      member = server.get_member(announcement.member_id)
       bot_member = server.get_member(client.user.id)
       member_role = bot.highest_role(member.roles)
       bot_role = bot.highest_role(bot_member.roles)
@@ -283,28 +290,28 @@ class Announcements(DBConnection):
 
     results = []
     for announcement in self.get_all_announcements(client, self.get_message_servers(client, message, True)):
-      if announcement['channel_id'] == 'private' and announcement['member_id'] != message.author.id:
+      if announcement.channel_id == 'private' and announcement.member_id != message.author.id:
         continue
-      text = 'announcement **{}**'.format(announcement['id'])
-      if announcement['channel_id'] == 'private':
+      text = 'announcement **{}**'.format(announcement.id)
+      if announcement.channel_id == 'private':
         text += ' in private'
       else:
-        server = client.get_server(announcement['server_id'])
-        member = server.get_member(announcement['member_id'])
+        server = client.get_server(announcement.server_id)
+        member = server.get_member(announcement.member_id)
         text += ' by {}'.format(member.name)
-      if announcement['channel_id']:
-        if announcement['channel_id'] != 'private':
-          text += ' in <#{}>'.format(announcement['channel_id'])
-      if announcement['voice_id']:
-        if announcement['sound']:
-          text += ' sound {}'.format(announcement['sound'])
-        if announcement['voice_id'] != announcement['channel_id']:
-          text += ' in <#{}>'.format(announcement['voice_id'])
-      if announcement['attachment']:
-        text += ' attachment {}'.format(announcement['attachment'])
-      if announcement['message']:
-        short = announcement['message'][:100]
-        if short != announcement['message']:
+      if announcement.channel_id:
+        if announcement.channel_id != 'private':
+          text += ' in <#{}>'.format(announcement.channel_id)
+      if announcement.voice_id:
+        if announcement.sound:
+          text += ' sound {}'.format(announcement.sound)
+        if announcement.voice_id != announcement.channel_id:
+          text += ' in <#{}>'.format(announcement.voice_id)
+      if announcement.attachment:
+        text += ' attachment {}'.format(announcement.attachment)
+      if announcement.message:
+        short = announcement.message[:100]
+        if short != announcement.message:
           short += '...'
         text += ': {}'.format(short.replace('\n', ' '))
       results.append(text)
@@ -321,48 +328,48 @@ class Announcements(DBConnection):
       return
     lines = []
 
-    if announcement['channel_id'] == 'private':
+    if announcement.channel_id == 'private':
       for server in client.servers:
         try:
-          member = server.get_member(announcement['member_id'])
+          member = server.get_member(announcement.member_id)
         except:
           log.exception("announce")
     else:
-      server = client.get_server(announcement['server_id'])
-      member = server.get_member(announcement['member_id'])
-    text = '**{}announcement {} by {}'.format('private ' if announcement['channel_id'] == 'private' else '', id, member.name if member is not None else announcement['member_id'])
+      server = client.get_server(announcement.server_id)
+      member = server.get_member(announcement.member_id)
+    text = '**{}announcement {} by {}'.format('private ' if announcement.channel_id == 'private' else '', id, member.name if member is not None else announcement.member_id)
     now = int(time.time())
-    if announcement['last_spoke']:
-      offset = announcement['last_spoke']
-      text += ' last given at {}'.format(bot.iso8601(announcement['last_spoke']))
+    if announcement.last_spoke:
+      offset = announcement.last_spoke
+      text += ' last given at {}'.format(bot.iso8601(announcement.last_spoke))
     else:
       offset = now
-    if announcement['start_date']:
-      if announcement['start_date'] > now:
-        offset = announcement['start_date']
-        text += ' scheduled for {}'.format(bot.iso8601(announcement['start_date']))
-    if announcement['end_date']:
-      if announcement['end_date'] < now:
+    if announcement.start_date:
+      if announcement.start_date > now:
+        offset = announcement.start_date
+        text += ' scheduled for {}'.format(bot.iso8601(announcement.start_date))
+    if announcement.end_date:
+      if announcement.end_date < now:
         offset = None
         text += ' has expired'
     if offset is not None and offset <= now:
-      if announcement['interval']:
-        text += ' scheduled for {}'.format(bot.iso8601(offset + announcement['interval']))
-    if announcement['asap'] == 'true':
+      if announcement.interval:
+        text += ' scheduled for {}'.format(bot.iso8601(offset + announcement.interval))
+    if announcement.asap == 'true':
       text += ' will be given ASAP'
-    if announcement['probability'] < 0:
+    if announcement.probability < 0:
       text += ' paused'
     text += '**'
     lines.append(text)
 
     text = 'announce create'
-    if announcement['start_date']:
-      text += ' from "{}"'.format(bot.iso8601(announcement['start_date']))
-    if announcement['end_date']:
-      text += ' to "{}"'.format(bot.iso8601(announcement['end_date']))
-    if announcement['interval']:
-      text += ' every {}'.format(bot.unparse_seconds(int(announcement['interval'])))
-    mention = announcement['mention']
+    if announcement.start_date:
+      text += ' from "{}"'.format(bot.iso8601(announcement.start_date))
+    if announcement.end_date:
+      text += ' to "{}"'.format(bot.iso8601(announcement.end_date))
+    if announcement.interval:
+      text += ' every {}'.format(bot.unparse_seconds(int(announcement.interval)))
+    mention = announcement.mention
     if mention:
       if mention == 'everyone' or mention == 'here':
         text += ' tell @{}'.format(mention)
@@ -372,25 +379,25 @@ class Announcements(DBConnection):
           text += ' tell @{}'.format(mention)
         else:
           text += ' tell <@{}>'.format(mention)
-    if announcement['channel_id'] != announcement['voice_id']:
-      if announcement['channel_id'] == 'private':
+    if announcement.channel_id != announcement.voice_id:
+      if announcement.channel_id == 'private':
         text += ' in private'
       else:
-        text += ' in <#{}>'.format(announcement['channel_id'])
-    if announcement['voice_id']:
-      text += ' voice <#{}>'.format(announcement['voice_id'])
-      if announcement['sound']:
-        text += ' sound {}'.format(announcement['sound'])
-    if announcement['attachment']:
-      text += ' attachment {}'.format(announcement['attachment'])
-    if abs(announcement['probability']) < 1.0:
-      text += ' probability {}'.format(abs(announcement['probability']))
-
-    if announcement['message']:
+        text += ' in <#{}>'.format(announcement.channel_id)
+    if announcement.voice_id:
+      text += ' voice <#{}>'.format(announcement.voice_id)
+      if announcement.sound:
+        text += ' sound {}'.format(announcement.sound)
+    if announcement.attachment:
+      text += ' attachment {}'.format(announcement.attachment)
+    if abs(announcement.probability) < 1.0:
+      text += ' probability {}'.format(abs(announcement.probability))
+
+    if announcement.message:
       text += ':'
     lines.append(text)
-    if announcement['message']:
-      lines.append(announcement['message'])
+    if announcement.message:
+      lines.append(announcement.message)
 
     yield from bot.say_many(message.channel, lines)
 
@@ -624,7 +631,7 @@ class Announcements(DBConnection):
     if create is None:
       return
 
-    announce = yield from self.can_manage_announcements(message.author, message.channel, 'create', create = create)
+    announce = yield from self.can_manage_announcements(message.author, message.channel, 'create', create = Announcement(create))
     if not announce:
       return
 
@@ -687,9 +694,9 @@ class Announcements(DBConnection):
     # Pause announcement.
     if 'pause' in args:
       if args['pause']:
-        update['probability'] = -abs(announcement['probability'])
+        update['probability'] = -abs(announcement.probability)
       else:
-        update['probability'] = abs(announcement['probability'])
+        update['probability'] = abs(announcement.probability)
 
     # ASAP.
     if 'asap' in args:
@@ -746,47 +753,47 @@ class Announcements(DBConnection):
   @asyncio.coroutine
   def announce(self, announcement):
     update = {}
-    interval = announcement['interval']
+    interval = announcement.interval
     if interval is None:
       interval = 0
     # Adhere to schedule.
-    if bot.parse_boolean(announcement['asap']):
-      log.info('Announcement {} was requested ASAP'.format(announcement['id']))
-      announcement['probability'] = 1.0
+    if bot.parse_boolean(announcement.asap):
+      log.info('Announcement {} was requested ASAP'.format(announcement.id))
+      announcement.probability = 1.0
       update['asap'] = 'false'
     elif interval:
-      if announcement['start_date'] or announcement['last_spoke']:
+      if announcement.start_date or announcement.last_spoke:
         now = int(time.time())
-        if announcement['start_date']:
-          start = announcement['start_date']
+        if announcement.start_date:
+          start = announcement.start_date
         else:
-          start = announcement['last_spoke']
+          start = announcement.last_spoke
         intervals = math.floor((now - start) / interval)
         if intervals:
           # We missed a schedule.
           scheduled = start + intervals * interval
           if now - scheduled > max(60, interval / 2):
-            log.info('Announcement {} should have been given at {}'.format(announcement['id'], bot.iso8601(scheduled)))
-            if 'digest' in announcement:
-              update['digest'] = announcement['digest']
+            log.info('Announcement {} should have been given at {}'.format(announcement.id, bot.iso8601(scheduled)))
+            if announcement.digest:
+              update['digest'] = announcement.digest
               update['last_spoke'] = scheduled
-            self.update_announcement(client, announcement['id'], **update)
+            self.update_announcement(client, announcement.id, **update)
             return
         # Give the announcement now but set the original schedule.
         last_spoke = start + (intervals + 1) * interval
         if last_spoke < now:
           update['last_spoke'] = last_spoke
-          log.info('Forcing last_spoke for announcement {} to {} in line with schedule'.format(announcement['id'], bot.iso8601(update['last_spoke'])))
+          log.info('Forcing last_spoke for announcement {} to {} in line with schedule'.format(announcement.id, bot.iso8601(update['last_spoke'])))
     if 'asap' not in update and 'last_spoke' not in update:
       update['last_spoke'] = int(time.time())
 
     channel = None
-    channel_id = str(announcement['channel_id'])
+    channel_id = str(announcement.channel_id)
     private = channel_id == 'private'
     if private:
       for server in client.servers:
         try:
-          member = server.get_member(announcement['member_id'])
+          member = server.get_member(announcement.member_id)
           channel = yield from client.start_private_message(member)
           break
         except:
@@ -794,34 +801,34 @@ class Announcements(DBConnection):
     else:
       channel = client.get_channel(channel_id)
     if channel is None:
-      log.warning("Can't get channel for announcement {}".format(announcement['id']))
+      log.warning("Can't get channel for announcement {}".format(announcement.id))
       # Set last_spoke so we don't spam.
-      self.update_announcement(client, announcement['id'], **update)
+      self.update_announcement(client, announcement.id, **update)
       return
 
     attachment = None
-    if announcement['attachment']:
-      attachment = bot.parse_attachment(announcement['attachment'])
+    if announcement.attachment:
+      attachment = bot.parse_attachment(announcement.attachment)
 
-    voice_only = attachment is None and announcement['voice_id'] == announcement['channel_id']
-    log.debug('Announcement {} {}.'.format(announcement['id'], 'is voice only' if voice_only else 'has text'))
+    voice_only = attachment is None and announcement.voice_id == announcement.channel_id
+    log.debug('Announcement {} {}.'.format(announcement.id, 'is voice only' if voice_only else 'has text'))
     already_posted = False
     if voice_only:
       text = None
     else:
       # @everyone mention mangles the digest
-      text = announcement['message']
-      mention = announcement['mention']
+      text = announcement.message
+      mention = announcement.mention
       if mention:
         if mention in ['everyone', 'here']:
           text = '@{}\n{}'.format(mention, text)
         else:
           text = '<@{}>\n{}'.format(mention, text)
 
-      digest = announcement['digest']
+      digest = announcement.digest
 
       # Always post private messages.  Try not to spam public announcements.
-      if not private and not bot.parse_boolean(announcement['asap']):
+      if not private and not bot.parse_boolean(announcement.asap):
         cutoff = datetime.datetime.utcnow() - datetime.timedelta(0, interval)
         log.debug('Getting logs from {}.'.format(cutoff))
         result = yield from client.logs_from(channel, after = cutoff)
@@ -834,15 +841,15 @@ class Announcements(DBConnection):
         for message in logs:
           raw = re.sub(r'^(<?@\S+\s)+', '', message.content).strip()
           if digest in [bot.digest(message.content), bot.digest(raw)]:
-            log.debug('Saw previously posted announcement {} with digest {}'.format(announcement['id'], digest))
+            log.debug('Saw previously posted announcement {} with digest {}'.format(announcement.id, digest))
             already_posted = True
             break
 
-    if announcement['voice_id']:
-      filename = announcement['sound']
+    if announcement.voice_id:
+      filename = announcement.sound
       if not filename:
         filename = 'meow.wav'
-        log.debug('Will play {} for announcement {}.'.format(filename, announcement['id']))
+        log.debug('Will play {} for announcement {}.'.format(filename, announcement.id))
 
     try:
       logtext = text
@@ -854,32 +861,32 @@ class Announcements(DBConnection):
       if bot.get('dryrun'):
         if not already_posted:
           if voice_only:
-            log.info('Dryrun: Not playing announcement {} {} in {}'.format(announcement['id'], filename, channel))
+            log.info('Dryrun: Not playing announcement {} {} in {}'.format(announcement.id, filename, channel))
           else:
-            log.info('Dryrun: Not posting announcement {} to {}: {}'.format(announcement['id'], channel, logtext))
+            log.info('Dryrun: Not posting announcement {} to {}: {}'.format(announcement.id, channel, logtext))
       else:
         announce = False
         if not already_posted:
-          if random.random() <= announcement['probability']:
+          if random.random() <= announcement.probability:
             announce = True
         if announce:
           if voice_only:
             yield from bot.wake_up()
           else:
-            log.info('Posting announcement {} to {}: {}'.format(announcement['id'], channel, logtext))
+            log.info('Posting announcement {} to {}: {}'.format(announcement.id, channel, logtext))
             message = yield from bot.say(channel, text, attachment = attachment)
             update['digest'] = bot.digest(message.content)
         else:
-          if announcement['probability'] < 0:
-            log.info("Announcement {} is paused".format(announcement['id']))
+          if announcement.probability < 0:
+            log.info("Announcement {} is paused".format(announcement.id))
           else:
-            log.info("Didn't bother with announcement {}".format(announcement['id']))
+            log.info("Didn't bother with announcement {}".format(announcement.id))
         # Set last_spoke even if we chose not to announce.
-        self.update_announcement(client, announcement['id'], **update)
+        self.update_announcement(client, announcement.id, **update)
         if not announce:
           return
-        if announcement['voice_id']:
-          log.info('Playing announcement {} {} in {}'.format(announcement['id'], filename, channel))
-          yield from bot.play_sound(client.get_channel(str(announcement['voice_id'])), filename)
+        if announcement.voice_id:
+          log.info('Playing announcement {} {} in {}'.format(announcement.id, filename, channel))
+          yield from bot.play_sound(client.get_channel(str(announcement.voice_id)), filename)
     except:
       log.exception("announce")