Some iteration on the API changes, and an attempt to shard by "guild" which is the...
authorjweigele <jweigele@local>
Mon, 7 Sep 2020 06:03:16 +0000 (23:03 -0700)
committerjweigele <jweigele@local>
Mon, 7 Sep 2020 06:03:16 +0000 (23:03 -0700)
wigglydiscord.py

index 40e285084301589d6770e09fa268fa6791283ed5..ca8977b69b43d132beea9895c81668046de5a35d 100755 (executable)
@@ -13,8 +13,6 @@ import json
 import traceback
 import os
 
-SOUND_DIR="soundboard/"
-
 class HornClient(object):
     def __init__(self, config):
         self.rabbit_config = config
@@ -50,12 +48,13 @@ class HornClient(object):
             #exchange.publish(msg, routing_key=self.rabbit_config['exchange'])
 
 
-class WigglyDiscordException(Exception):
+class GrahDiscordException(Exception):
     pass
 
 
 
-class WigglyDiscordBot(discord.Client, HornClient):
+class GrahDiscordBot(discord.Client, HornClient):
+    AUDIO_FILES = ['mp3', '.ogg']
     def process_msg(self, msg):
         if msg._properties['content_type'] == 'application/json':
             # decode the object from json
@@ -71,7 +70,7 @@ class WigglyDiscordBot(discord.Client, HornClient):
 
     def __init__(self, config):
         self.config = config
-        self.voice = None
+        self.voice = {}
         self.player = None
         self.used = []
 
@@ -82,15 +81,14 @@ class WigglyDiscordBot(discord.Client, HornClient):
         asyncio.async(self.rabbit_connect())
         self.loop_forever()
 
-    def terminate_all(self):
-        if self.voice:
-            if self.player:
-                if self.player.is_playing():
-                    self.player.stop()
+    def terminate_all(self, guild):
+        if self.voice[guild]:
+            if self.voice[guild].is_playing():
+                self.voice[guild].stop()
 
     def get_sample_name(self):
         selections = os.listdir(self.config['airhorn_directory'])
-        selections = [x for x in selections if '.mp3' in x]
+        selections = [x for x in selections if self.is_audio_file(x)]
         sample_name = random.choice(selections)
         for index, use in enumerate(self.used):
             if use not in selections:
@@ -107,7 +105,6 @@ class WigglyDiscordBot(discord.Client, HornClient):
 
     @asyncio.coroutine
     async def horn(self, obj, properties):
-
         try:
             print('Horn! {}'.format(obj))
             # exclusive access on each horn or not?
@@ -156,8 +153,15 @@ class WigglyDiscordBot(discord.Client, HornClient):
     async def on_ready(self):
         print('Logged in as {} ({})'.format(self.user.name, self.user.id))
         print([x for x in self.get_all_channels()])
-        print(list(self.servers))
-        print(self.is_voice_connected(list(self.servers)[0]))
+        print(dir(self))
+        print(list(self.guilds))
+        for guild in self.guilds:
+            self.voice[guild] = None
+        print(self.voice_clients)
+#        if len(self.voice_clients) > 0:
+#            print('Found a preexisting voice client: {}'.format(self.voice_clients))
+#            self.voice[guild] = self.voice_clients[0]
+        #print(self.is_voice_connected(list(self.guilds)[0]))
         print('------')
 
     @property
@@ -169,41 +173,64 @@ class WigglyDiscordBot(discord.Client, HornClient):
     def text_channels(self):
         return [x for x in self.get_all_channels() if x.type == discord.ChannelType.text]
 
+    def is_audio_file(self, filename):
+        fn = filename.lower()
+        if any([fn.endswith(y) for y in self.AUDIO_FILES]):
+            return True
+        else:
+            return False
+
+    def name_matches_filename(self, name, filename):
+        name = name.lower()
+        filename = filename.lower()
+        if '.'.join(filename.split('.')[:-1]) == name and self.is_audio_file(filename):
+            return True
+        else:
+            return False
+
     @asyncio.coroutine
-    async def msg_play_sound(self, name=None):
+    async def msg_play_sound(self, name=None, guild=None):
         if name:
-            filename = '{}/{}.mp3'.format(self.config['airhorn_directory'], name)
-            if not os.path.exists(filename):
-                raise WigglyDiscordException("File '{}' not found in airhorn directory".format(name))
-            await self.msg_play_filename(filename)
+            filename=None
+            for fn in os.listdir(self.config['airhorn_directory']):
+                if self.name_matches_filename(name, fn):
+                    filename = '{}/{}'.format(self.config['airhorn_directory'], fn)
+                    break
+            if not filename or not os.path.exists(filename):
+                raise GrahDiscordException("File '{}' not found in airhorn directory".format(name))
+            await self.msg_play_filename(filename, guild=guild)
 
     @asyncio.coroutine
-    async def msg_play_filename(self, filename, exclusive=False):
-        if self.voice:
-            self.terminate_all()
-            self.player = self.voice.create_ffmpeg_player(filename)
-            self.player.start()
+    async def msg_play_filename(self, filename, exclusive=False, guild=None):
+        if self.voice[guild]:
+            self.terminate_all(guild=guild)
+            self.voice[guild].play(discord.FFmpegPCMAudio(filename))
+            #self.player.start()
         else:
             print('Was asked to play {} but no voice channel'.format(filename))
 
 
-    async def msg_join_voice_channel(self, channel_name):
+    async def msg_join_voice_channel(self, channel_name, guild):
         print('Called join voice channel')
         for vc in self.voice_channels:
-            if vc.name == channel_name:
+            if vc.name == channel_name and (not guild or vc.guild == guild):
                 print('Found a voice channel to join {} {}'.format(vc, type(vc)))
-                if self.voice:
-                    if vc != self.voice.channel:
-                        self.voice = await self.voice.move_to(vc)
+                if self.voice[guild]:
+                    if vc != self.voice[guild].channel:
+                        await self.voice[guild].move_to(vc)
                 else:
-                    self.voice = await self.join_voice_channel(vc)
-                print('Joined voice channel {} (id: {})'.format(vc.name, vc.id))
-                print('Voice now {}'.format(self.voice))
+                    self.voice[guild] = await vc.connect()
+                print('Joined voice channel {} {} (id: {})'.format(guild, vc.name, vc.id))
+                print('Voice now {}'.format(self.voice[guild]))
 
         print('Exit join voice')
 
     def get_airhorn_filenames(self):
-        return sorted([x[:-4] for x in os.listdir(self.config['airhorn_directory']) if 'mp3' in x.lower()])
+        retval = []
+        for x in os.listdir(self.config['airhorn_directory']):
+            if self.is_audio_file(x):
+                retval.append(x.lower())
+        return sorted([x[:-4] for x in retval])
 
         
     def chunk_it_up(self, filename_list):
@@ -225,37 +252,66 @@ class WigglyDiscordBot(discord.Client, HornClient):
 
     @asyncio.coroutine
     async def oh_no(self, channel, exc):
+        print("Exception: {}\t{}".format(channel, exc))
         exc_string = 'Ruh Roh! {}: {}'.format(type(exc), str(exc))
         traceback.print_exc()
-        await self.send_message(channel, exc_string)
+        await channel.send(exc_string)
 
     def print_call(self, message):
         print('#{} <{}>: {}'.format(message.channel, message.author, message.content))
 
+
+    def member_guilds(self, member):
+        return [x for x in self.guilds if member in x.members]
+
+    def contextual_guild(self, message):
+        channel = message.channel
+        print(channel)
+        if isinstance(channel, discord.DMChannel):
+            print('Is a DM with {}'.format(channel.recipient))
+            retval = self.member_guilds(channel.recipient)
+            print('Member guilds: {}'.format(retval))
+            return retval
+        else:
+            print('Guild: {}'.format(channel.guild))
+            return [channel.guild]
+
     @asyncio.coroutine
     async def on_message(self, message):
         try:
-            if message.content.startswith('!sleep'):
-                await asyncio.sleep(5)
-                await self.send_message(message.channel, 'Done sleeping')
-            elif message.content.startswith('!joinvoice'):
-                self.print_call(message)
-                print('Was instructed to join {}'.format(message.content.split(' ')[1]))
-                await self.msg_join_voice_channel(message.content.split(' ')[1])
-            elif message.content.startswith('!play'):
-                self.print_call(message)
-                print('Was instructed to play {}'.format(message.content.split(' ')[1]))
-                await self.msg_play_sound(name=' '.join(message.content.split(' ')[1:]))
-            elif message.content.startswith('!listairhorns'):
-                self.print_call(message)
-                for airhorn_files in self.chunk_it_up(self.get_airhorn_filenames()):
-                    await self.send_message(message.channel, '```{}```'.format('\n'.join(airhorn_files)))
-            elif message.content.startswith('!leavevoice'):
-                await self.voice.disconnect()
-                self.voice = None
+            if message.author != self.user:
+                guilds = self.contextual_guild(message)
+                if len(guilds) == 0:
+                    return
+                elif len(guilds) >1:
+                    await message.channel.send('Too many guilds!!!\n{}'.format('\n'.join([str(x) for x in guilds])))
+                    return
+                elif len(guilds) == 1:
+                    guild = guilds[0]
+
+                if message.content.startswith('!sleep'):
+                    await asyncio.sleep(5)
+                    await message.channel.send('Done sleeping')
+                elif message.content.startswith('!joinvoice'):
+                    self.print_call(message)
+                    print('Was instructed to join {} guild {}'.format(message.content.split(' ')[1], str(guild)))
+                    await self.msg_join_voice_channel(message.content.split(' ')[1], guild=guild)
+                elif message.content.startswith('!play'):
+                    self.print_call(message)
+                    print('Was instructed to play {}'.format(message.content.split(' ')[1]))
+                    await self.msg_play_sound(name=' '.join(message.content.split(' ')[1:]), guild=guild)
+                elif message.content == '!list':
+                    self.print_call(message)
+                    for airhorn_files in self.chunk_it_up(self.get_airhorn_filenames()):
+                        await message.channel.send('```{}```'.format('\n'.join(airhorn_files)))
+                elif message.content == '!leavevoice':
+                    await self.voice[guild].disconnect()
+                    self.voice[guild] = None
+                elif message.content == '!help':
+                    await message.channel.send('```!joinvoice [channel]\n!play [file]\n!list (for all of them)\n!leavevoice```')
         except Exception as e:
             await self.oh_no(message.channel, e)
 
 if __name__ == '__main__':
     config = json.load(open(sys.argv[1], 'r'))
-    bot = WigglyDiscordBot(config)
+    bot = GrahDiscordBot(config)