Minimal changes to support Python 3.5.
authorCMDR furrycat <elite@furrycat.net>
Sat, 18 Feb 2017 12:02:31 +0000 (12:02 +0000)
committerCMDR furrycat <elite@furrycat.net>
Tue, 21 Feb 2017 10:05:24 +0000 (10:05 +0000)
bot.py
plugin/announcements/announcements.py
plugin/faction/faction.py
plugin/feeds/feeds.py
plugin/greetings/greetings.py
requirements.txt

diff --git a/bot.py b/bot.py
index 3bbc26c..e6bd5f0 100644 (file)
--- a/bot.py
+++ b/bot.py
@@ -297,7 +297,7 @@ def set_avatar(url):
 
 @asyncio.coroutine
 def set_idle(idle):
-  yield from client.change_status(idle = idle)
+  yield from client.change_presence(status = discord.Status.idle if idle else discord.Status.online)
   for voice in list(client.voice_clients):
     yield from voice.disconnect()
 
@@ -484,74 +484,33 @@ def highest_role(roles):
       highest = role
   return highest
 
-# Discord API 0.13 has is_superset() to do this.
-def equal_permission(x, y, *, strict = True):
+def lower_role_than(member, *, strict = False):
+  if server.me is None:
+    log.error("Can't call lower_role_than() a member on a server we aren't on!")
+    return False
+  if strict:
+    return member.top_role > server.me.top_role
+  else:
+    return member.top_role >= server.me.top_role
+
+def lower_role_on_server_than(server, id, *, strict = False):
+  member = server.get_member(id)
+  if member is None:
+    log.error("Can't call lower_role_on_server_than() a member not on the server!")
+    return False
+  return lower_role_than(member, strict = strict)
+
+def equal_permissions(x, y, *, strict = True):
   if x == y:
     return True
-  if x and not y and not strict:
+  if x.is_subset(y) and not strict:
     return True
   return False
 
-# Discord API 0.13 has == to do this.
-def equal_permissions(x, y, *, strict = True):
-  if not equal_permission(x.create_instant_invite, y.create_instant_invite, strict = strict):
-    return False
-  if not equal_permission(x.kick_members, y.kick_members, strict = strict):
-    return False
-  if not equal_permission(x.ban_members, y.ban_members, strict = strict):
-    return False
-  if not equal_permission(x.administrator, y.administrator, strict = strict):
-    return False
-  if not equal_permission(x.manage_channels, y.manage_channels, strict = strict):
-    return False
-  if not equal_permission(x.manage_server, y.manage_server, strict = strict):
-    return False
-  if not equal_permission(x.read_messages, y.read_messages, strict = strict):
-    return False
-  if not equal_permission(x.send_messages, y.send_messages, strict = strict):
-    return False
-  if not equal_permission(x.send_tts_messages, y.send_tts_messages, strict = strict):
-    return False
-  if not equal_permission(x.manage_messages, y.manage_messages, strict = strict):
-    return False
-  if not equal_permission(x.embed_links, y.embed_links, strict = strict):
-    return False
-  if not equal_permission(x.attach_files, y.attach_files, strict = strict):
-    return False
-  if not equal_permission(x.read_message_history, y.read_message_history, strict = strict):
-    return False
-  if not equal_permission(x.mention_everyone, y.mention_everyone, strict = strict):
-    return False
-  if not equal_permission(x.external_emojis, y.external_emojis, strict = strict):
-    return False
-  if not equal_permission(x.connect, y.connect, strict = strict):
-    return False
-  if not equal_permission(x.speak, y.speak, strict = strict):
-    return False
-  if not equal_permission(x.mute_members, y.mute_members, strict = strict):
-    return False
-  if not equal_permission(x.deafen_members, y.deafen_members, strict = strict):
-    return False
-  if not equal_permission(x.move_members, y.move_members, strict = strict):
-    return False
-  if not equal_permission(x.use_voice_activation, y.use_voice_activation, strict = strict):
-    return False
-  if not equal_permission(x.change_nickname, y.change_nickname, strict = strict):
-    return False
-  if not equal_permission(x.manage_nicknames, y.manage_nicknames, strict = strict):
-    return False
-  if not equal_permission(x.manage_roles, y.manage_roles, strict = strict):
-    return False
-  #if not equal_permission(x.manage_webhooks, y.manage_webhooks, strict = strict):
-    #return False
-  #if not equal_permission(x.manage_emojis, y.manage_emojis, strict = strict):
-    #return False
-  return True
-
 @asyncio.coroutine
 def overwrite_permissions(channel, whom, overwrite, *, strict = True):
   permissions = channel.overwrites_for(whom)
-  if equal_permissions(permissions, overwrite, strict = strict):
+  if equal_permissions(permissions.pair(), overwrite.pair(), strict = strict):
     log.debug('Permissions for {} in {} are already correct.'.format(whom.name, channel.name))
     return
   log.info('Setting permissions for {} in {} to {}'.format(whom.name, channel.name, permissions.__dict__))
index 0a5ea77..eb44f89 100644 (file)
@@ -161,15 +161,13 @@ class Announcements(DBConnection):
         if not announcement.is_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)
-          if member_role.position > announcer_role.position:
-            log.debug('Member {} on server {} can edit announcements from lower role.'.format(member.name, server.name))
+          if member.top_role > announcer.top_role:
+            log.debug('Member {} with role {} on server {} can edit announcements from lower role.'.format(member.name, member.top_role.name, server.name))
             return announcement
-          elif member_role.position == announcer_role.position:
-            if member_role.position == bot.highest_role(server.roles).position:
-              log.debug('Member {} with role {} on server {} can edit announcements.'.format(member.name, member_role.name, server.name))
+          elif member.top_role == announcer.top_role:
+            if member.top_role == member.server.me.top_role:
+              log.debug('Member {} with role {} on server {} can edit announcements.'.format(member.name, member.top_role.name, server.name))
               return announcement
 
     if command == 'create':
@@ -178,11 +176,8 @@ class Announcements(DBConnection):
         return announcement
       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)
-      if member_role.position > bot_role.position:
-        log.debug('Member {} with role {} on server {} can create announcements.'.format(member.name, member_role.name, server.name))
+      if bot.lower_role_than(member, strict = True):
+        log.debug('Member {} with role {} on server {} can create announcements.'.format(member.name, member.top_role.name, server.name))
         return announcement
 
     yield from cat.hiss(channel)
index b408f98..8b12a53 100644 (file)
@@ -369,13 +369,12 @@ class Faction(DBConnection):
           announcer = server.get_member(faction.member_id)
           announcer_role = bot.highest_role(announcer.roles)
           member = server.get_member(author.id)
-          member_role = bot.highest_role(member.roles)
-          if member_role.position > announcer_role.position:
+          if member.top_role > announcer.top_role:
             log.debug('Member {} on server {} can edit factions from lower role.'.format(member.name, server.name))
             return faction
-          elif member_role.position == announcer_role.position:
-            if member_role.position == bot.highest_role(server.roles).position:
-              log.debug('Member {} with role {} on server {} can edit factions.'.format(member.name, member_role.name, server.name))
+          elif member.top_role == announcer.top_role:
+            if member.top_role == member.server.me.top_role:
+              log.debug('Member {} with role {} on server {} can edit factions.'.format(member.name, member.top_role.name, server.name))
               return faction
 
     if command == 'create':
@@ -385,10 +384,8 @@ class Faction(DBConnection):
       server = client.get_server(faction.server_id)
       member = server.get_member(faction.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)
-      if member_role.position > bot_role.position:
-        log.debug('Member {} with role {} on server {} can create factions.'.format(member.name, member_role.name, server.name))
+      if bot.lower_role_than(member, strict = True):
+        log.debug('Member {} with role {} on server {} can create factions.'.format(member.name, member.top_role.name, server.name))
         return faction
 
     yield from cat.hiss(channel)
index 19d13c2..5aa717e 100644 (file)
@@ -32,7 +32,8 @@ class Feeds(DBConnection):
     self.dbh.commit()
 
   def get_all_feeds(self, client, servers = []):
-    yield from self.get_all_from_table(client, 'feeds', servers)
+    for row in self.get_all_from_table(client, 'feeds', servers):
+      yield row
 
   def get_feeds(self, client):
     now = self.now()
@@ -68,15 +69,14 @@ class Feeds(DBConnection):
     else:
       return k
 
-  @asyncio.coroutine
-  def set_rss_permissions(self, channel_id):
+  async def set_rss_permissions(self, channel_id):
     channel = client.get_channel(channel_id)
     if channel is None:
       log.warning("Can't get channel {} for RSS feeds.".format(channel_id))
       return
     role = None
     # My highest role.
-    role = bot.highest_role(channel.server.me.roles)
+    role = channel.server.me.top_role
     if role is not None:
       # Permissions for bot.
       overwrite = discord.PermissionOverwrite()
@@ -86,17 +86,16 @@ class Feeds(DBConnection):
       overwrite.send_tts_messages = True
       overwrite.manage_messages = True
       overwrite.attach_files = True
-      yield from bot.overwrite_permissions(channel, role, overwrite, strict = False)
+      await bot.overwrite_permissions(channel, role, overwrite, strict = False)
     # Permissions for @everyone.
     overwrite = discord.PermissionOverwrite()
     overwrite.read_messages = True
     overwrite.read_message_history = True
     overwrite.send_messages = False
     overwrite.send_tts_messages = False
-    yield from bot.overwrite_permissions(channel, channel.server.default_role, overwrite)
+    await bot.overwrite_permissions(channel, channel.server.default_role, overwrite)
 
-  @asyncio.coroutine
-  def do_rss(self, feeds):
+  async def do_rss(self, feeds):
     channel_id = str(feeds[0]['channel_id'])
     channel = client.get_channel(channel_id)
     if not channel:
@@ -157,8 +156,7 @@ class Feeds(DBConnection):
       limit = max(len(all_entries) * 2, int(bot.get('message_limit') * 1.5))
 
       digests = []
-      logs = yield from client.logs_from(channel, limit = limit)
-      for message in logs:
+      async for message in client.logs_from(channel, limit = limit):
         digest = bot.digest(message.content)
         log.debug('Saw previously posted RSS with digest {}'.format(digest))
         if digest not in digests:
@@ -209,7 +207,7 @@ class Feeds(DBConnection):
             log.info('Dryrun: Not posting to {}: {}'.format(channel, formatted))
           else:
             log.info('Posting to {}: {}'.format(channel, formatted))
-            message = yield from bot.say(channel, formatted)
+            message = await bot.say(channel, formatted)
             digest = bot.digest(message.content)
             digests.append(digest)
             log.debug('Posted RSS with digest {}'.format(digest))
@@ -220,8 +218,7 @@ class Feeds(DBConnection):
       for id, update in updates.items():
         self.update_feed(client, id, **update)
 
-  @asyncio.coroutine
-  def on_ready(self):
+  async def on_ready(self):
     while True:
       feeds = {}
       for feed in list(self.get_feeds(client)):
@@ -231,62 +228,58 @@ class Feeds(DBConnection):
         feeds[feed['channel_id']].append(feed)
       for channel_id in feeds.keys():
         if any([bot.parse_boolean(feed['permissions']) for feed in feeds[channel_id]]):
-          yield from self.set_rss_permissions(channel_id)
-        yield from self.do_rss(feeds[channel_id])
-      yield from asyncio.sleep(rsstime.value)
+          await self.set_rss_permissions(channel_id)
+        await self.do_rss(feeds[channel_id])
+      await asyncio.sleep(rsstime.value)
 
   def valid_commands(self):
     return ['news', 'feed']
 
-  @asyncio.coroutine
-  def handle_command(self, message, command, raw):
+  async def handle_command(self, message, command, raw):
     if command not in self.valid_commands():
       return PluginCommand.ignored
-    yield from self.manage_feeds(message, shlex.split(raw)[1:])
+    await self.manage_feeds(message, shlex.split(raw)[1:])
     return PluginCommand.exclusive
 
-  @asyncio.coroutine
-  def handle_help(self, message, command, *args):
-    yield from self.help_feeds(message, *args)
+  async def handle_help(self, message, command, *args):
+    await self.help_feeds(message, *args)
 
-  @asyncio.coroutine
-  def manage_feeds(self, message, args):
+  async def manage_feeds(self, message, args):
     if not len(args):
-      yield from self.list_feeds(message)
+      await self.list_feeds(message)
       return
     command = args[0].lower()
     if len(args) == 1:
       if command == 'list':
-        yield from self.list_feeds(message)
+        await self.list_feeds(message)
         return
       elif command == 'help':
-        yield from self.help_feeds(message)
+        await self.help_feeds(message)
         return
       else:
-        yield from cat.yelp(message.channel)
+        await cat.yelp(message.channel)
         return
 
     if command == 'show':
-      yield from self.show_feed(message, args[1])
+      await self.show_feed(message, args[1])
     elif command == 'help':
-      yield from self.help_feeds(message, args[1])
+      await self.help_feeds(message, args[1])
     elif command == 'delete':
-      yield from self.delete_existing_feed(message, args[1])
+      await self.delete_existing_feed(message, args[1])
     elif command == 'pause':
-      yield from self.schedule_feed(message, args[1], pause = True)
+      await self.schedule_feed(message, args[1], pause = True)
     elif command == 'resume':
-      yield from self.schedule_feed(message, args[1], pause = False)
+      await self.schedule_feed(message, args[1], pause = False)
     elif command == 'create':
-      yield from self.create_new_feed(message, args)
+      await self.create_new_feed(message, args)
     elif command == 'edit':
-      yield from self.edit_existing_feed(message, args)
+      await self.edit_existing_feed(message, args)
     elif command == 'time':
-      yield from self.set_poll_time(message, args[1])
+      await self.set_poll_time(message, args[1])
     else:
-      yield from cat.yelp(message.channel)
+      await cat.yelp(message.channel)
 
-  @asyncio.coroutine
-  def can_manage_feeds(self, author, channel, command, **args):
+  async def can_manage_feeds(self, author, channel, command, **args):
     # Anyone can list feeds.
     if command == 'list':
       log.debug('Anyone can list feeds.')
@@ -313,23 +306,19 @@ class Feeds(DBConnection):
       if feed is not None:
         server = client.get_server(feed['server_id'])
         member = server.get_member(author.id)
-        bot_member = server.get_member(client.user.id)
-        member_role = bot.highest_role(member.roles)
-        bot_role = bot.highest_role(bot_member.roles)
-        if member_role.position >= bot_role.position:
-          log.debug('Member {} with role {} on server {} can manage feeds.'.format(member.name, member_role.name, server.name))
+        if bot.lower_role_than(member):
+          log.debug('Member {} with role {} on server {} can manage feeds.'.format(member.name, member.top_role, server.name))
           return feed
 
     if command in ['time']:
       if bot.is_admin(member, member.server):
         return True
 
-    yield from cat.hiss(channel)
+    await cat.hiss(channel)
     return False
 
-  @asyncio.coroutine
-  def list_feeds(self, message):
-    result = yield from self.can_manage_feeds(message.author, message.channel, 'list')
+  async def list_feeds(self, message):
+    result = await self.can_manage_feeds(message.author, message.channel, 'list')
     if not result:
       return
 
@@ -339,13 +328,12 @@ class Feeds(DBConnection):
       results.append(text)
 
     if len(results):
-      yield from bot.say_many(message.channel, results)
+      await bot.say_many(message.channel, results)
     else:
-      yield from cat.shrug(message.channel)
+      await cat.shrug(message.channel)
 
-  @asyncio.coroutine
-  def show_feed(self, message, id):
-    feed = yield from self.can_manage_feeds(message.author, message.channel, 'show', id = id)
+  async def show_feed(self, message, id):
+    feed = await self.can_manage_feeds(message.author, message.channel, 'show', id = id)
     if not feed:
       return
     lines = []
@@ -381,10 +369,9 @@ class Feeds(DBConnection):
       text += "--link_json '{}'".format(feed['link_json'])
     lines.append(text)
 
-    yield from bot.say_many(message.channel, lines)
+    await bot.say_many(message.channel, lines)
 
-  @asyncio.coroutine
-  def parse_feed(self, message, args, editing = False):
+  async def parse_feed(self, message, args, editing = False):
     parsed = {}
     if editing:
       command = 'edit'
@@ -478,7 +465,7 @@ class Feeds(DBConnection):
         else:
           break
       else:
-        yield from bot.say(message.channel, 'What is {}?'.format(arg))
+        await bot.say(message.channel, 'What is {}?'.format(arg))
         return
 
       if ok:
@@ -488,30 +475,30 @@ class Feeds(DBConnection):
 
     if not ok:
       log.info('Failed to parse feed.  Got: {}'.format(parsed))
-      yield from bot.say(message.channel, '{}?'.format(arg))
+      await bot.say(message.channel, '{}?'.format(arg))
       return None
 
     if 'channel_id' in parsed:
       channel = client.get_channel(parsed['channel_id'])
       if not channel:
-        yield from bot.say(message.channel, 'Invalid channel!')
+        await bot.say(message.channel, 'Invalid channel!')
         return None
       server = channel.server
       parsed['server_id'] = server.id
 
     if editing:
       if 'id' not in parsed:
-        yield from bot.say(message.channel, 'Missing ID!')
+        await bot.say(message.channel, 'Missing ID!')
         return None
     else:
       if 'channel_id' not in parsed:
-        yield from bot.say(message.channel, 'Missing channel!')
+        await bot.say(message.channel, 'Missing channel!')
         return None
       if 'url' not in parsed:
-        yield from bot.say(message.channel, 'Missing URL!')
+        await bot.say(message.channel, 'Missing URL!')
         return None
       if 'description' not in parsed:
-        yield from bot.say(message.channel, 'Missing title!')
+        await bot.say(message.channel, 'Missing title!')
         return None
       if 'show_date' not in parsed:
         parsed['show_date'] = True
@@ -522,14 +509,13 @@ class Feeds(DBConnection):
 
     return parsed
 
-  @asyncio.coroutine
-  def create_new_feed(self, message, *args):
+  async def create_new_feed(self, message, *args):
     # feed create [params]: <text>
-    create = yield from self.parse_feed(message, *args)
+    create = await self.parse_feed(message, *args)
     if create is None:
       return
 
-    feed = yield from self.can_manage_feeds(message.author, message.channel, 'create', create = create)
+    feed = await self.can_manage_feeds(message.author, message.channel, 'create', create = create)
     if not feed:
       return
 
@@ -537,39 +523,37 @@ class Feeds(DBConnection):
     if id:
       create['id'] = id
       log.info('Created feed: {}'.format(create))
-      yield from bot.say(message.channel, id)
-      yield from cat.purr(None, bot.voice_channel_for_user(message.author), join = False)
+      await bot.say(message.channel, id)
+      await cat.purr(None, bot.voice_channel_for_user(message.author), join = False)
     else:
       log.info('Failed to create feed: {}'.format(create))
-      yield from cat.yelp(message.channel)
+      await cat.yelp(message.channel)
 
-  @asyncio.coroutine
-  def edit_existing_feed(self, message, *args):
-    update = yield from self.parse_feed(message, *args, editing = True)
+  async def edit_existing_feed(self, message, *args):
+    update = await self.parse_feed(message, *args, editing = True)
     if update is None:
       return
 
     id = update['id']
     del(update['id'])
-    feed = yield from self.can_manage_feeds(message.author, message.channel, 'edit', id = id)
+    feed = await self.can_manage_feeds(message.author, message.channel, 'edit', id = id)
     if not feed:
       return
 
     if not len(update.keys()):
-      yield from bot.say(message.channel, '?')
+      await bot.say(message.channel, '?')
       return
 
     if self.update_feed(client, id, **update):
       log.info('Edited feed {}: {}'.format(id, update))
-      yield from bot.say(message.channel, ', '.join([self.feed_key(k) for k in update]))
-      yield from cat.purr(None, bot.voice_channel_for_user(message.author), join = False)
+      await bot.say(message.channel, ', '.join([self.feed_key(k) for k in update]))
+      await cat.purr(None, bot.voice_channel_for_user(message.author), join = False)
     else:
       log.info('Failed to edit feed {}: {}'.format(id, update))
-      yield from cat.yelp(message.channel)
+      await cat.yelp(message.channel)
 
-  @asyncio.coroutine
-  def delete_existing_feed(self, message, id):
-    feed = yield from self.can_manage_feeds(message.author, message.channel, 'delete', id = id)
+  async def delete_existing_feed(self, message, id):
+    feed = await self.can_manage_feeds(message.author, message.channel, 'delete', id = id)
     if not feed:
       return
     if bot.get('dryrun'):
@@ -577,13 +561,12 @@ class Feeds(DBConnection):
     else:
       log.info('Deleting feed {}'.format(id))
       if self.delete_feed(client, id):
-        yield from cat.purr(message.channel)
+        await cat.purr(message.channel)
       else:
-        yield from cat.yelp(message.channel)
+        await cat.yelp(message.channel)
 
-  @asyncio.coroutine
-  def schedule_feed(self, message, id, **args):
-    feed = yield from self.can_manage_feeds(message.author, message.channel, 'schedule', id = id)
+  async def schedule_feed(self, message, id, **args):
+    feed = await self.can_manage_feeds(message.author, message.channel, 'schedule', id = id)
     if not feed:
       return
 
@@ -601,20 +584,18 @@ class Feeds(DBConnection):
     else:
       log.info('Updating feed {}: {}'.format(id, update))
       if self.update_feed(client, id, **update):
-        yield from cat.purr(message.channel)
+        await cat.purr(message.channel)
       else:
-        yield from cat.yelp(message.channel)
+        await cat.yelp(message.channel)
 
-  @asyncio.coroutine
-  def set_poll_time(self, message, *args):
+  async def set_poll_time(self, message, *args):
     seconds = bot.parse_seconds(args[0])
     if seconds < 60:
-      yield from bot.say(message.channel, 'At least 60!')
+      await bot.say(message.channel, 'At least 60!')
     else:
       rsstime.value = seconds
 
-  @asyncio.coroutine
-  def help_feeds(self, message, command = None):
+  async def help_feeds(self, message, command = None):
     if command is None:
       lines = [
         'Commands to manage news feeds are:',
@@ -707,4 +688,4 @@ class Feeds(DBConnection):
       ]
     else:
       lines = ['*shrugs*']
-    yield from bot.say_many(message.channel, lines)
+    await bot.say_many(message.channel, lines)
index 46479f1..4833900 100644 (file)
@@ -104,11 +104,8 @@ class Greetings(DBConnection):
       if greeting is not None:
         server = client.get_server(greeting['server_id'])
         member = server.get_member(author.id)
-        bot_member = server.get_member(client.user.id)
-        member_role = bot.highest_role(member.roles)
-        bot_role = bot.highest_role(bot_member.roles)
-        if member_role.position >= bot_role.position:
-          log.debug('Member {} with role {} on server {} can manage greetings.'.format(member.name, member_role.name, server.name))
+        if bot.lower_role_than(member):
+          log.debug('Member {} with role {} on server {} can manage greetings.'.format(member.name, member.top_role.name, server.name))
           return greeting
 
     yield from cat.hiss(channel)
index d191763..8a4f084 100644 (file)
@@ -1,5 +1,5 @@
 bs4
-discord.py[voice]==0.11.0
+discord.py[voice]==0.16.6
 feedparser
 pexpect
 pillow