Classy updates and renames to get horns working more flexibly in discord
authorjweigele <jweigele@local>
Tue, 8 Sep 2020 05:56:26 +0000 (22:56 -0700)
committerjweigele <jweigele@local>
Tue, 8 Sep 2020 05:56:26 +0000 (22:56 -0700)
Dockerfile [new file with mode: 0644]
grahbot.py [new file with mode: 0755]
wigglydiscord.py [deleted file]

diff --git a/Dockerfile b/Dockerfile
new file mode 100644 (file)
index 0000000..ebfe783
--- /dev/null
@@ -0,0 +1,17 @@
+# Use the official image as a parent image.
+FROM ubuntu
+
+# Set the working directory.
+WORKDIR /usr/src/app
+
+# Run the command inside your image filesystem.
+#RUN npm install
+RUN apt-get update
+RUN apt-get install python3 python3-pip ffmpeg --no-install-recommends -y
+RUN pip3 install discord.py asynqp PyNaCl
+
+# Run the specified command within the container.
+CMD [ "/usr/src/app/grahbot.py" ]
+
+# Copy the rest of your app's source code from your host to your image filesystem.
+COPY grahbot.py .
diff --git a/grahbot.py b/grahbot.py
new file mode 100755 (executable)
index 0000000..e02188c
--- /dev/null
@@ -0,0 +1,431 @@
+#!/usr/bin/python3
+
+import discord
+import socket
+import random
+import ssl
+import asynqp
+import discord.voice_client
+import datetime
+import asyncio
+import json
+import traceback
+import os
+from collections import defaultdict, OrderedDict
+
+PERSISTENT_DIR = '/var/lib/grahbot'
+
+
+class GrahState(object):
+    FILENAME = '{}/state.json'.format(PERSISTENT_DIR)
+    def __init__(self, grahbot, filename=FILENAME):
+        self.filename = filename
+        self.voice = defaultdict(lambda: None)
+        self.grahbot = grahbot
+        self.load()
+
+    def load(self):
+        if os.path.exists(self.filename):
+            self.data = json.load(open(self.filename, 'r'))
+        else:
+            self.data = OrderedDict()
+        if 'user' not in self.data:
+            self.data['user'] = {}
+        if 'guild' not in self.data:
+            self.data['guild'] = {}
+        self.save()
+
+    def save(self):
+        json.dump(self.data, open(self.filename, 'w'))
+
+
+    def set_voice(self, guild, voice):
+        self.set_voice_by_id(guild.id, voice)
+
+    def set_voice_by_id(self, guild_id, voice):
+        self.data['guild'][str(guild_id)] = voice.channel.id
+        if not self.voice[str(guild_id)]:
+            self.voice[str(guild_id)] = voice
+        self.save()
+
+
+    def get_voice_id(self, guild):
+        print('here is self.data for guild {}'.format(self.data['guild']))
+        if str(guild.id) in self.data['guild']:
+            return discord.utils.get(self.grahbot.voice_channels, id=self.data['guild'][str(guild.id)])
+        else:
+            return None
+
+    def get_voice(self, guild):
+        return self.get_voice_by_id(guild.id)
+
+    def get_voice_by_id(self, guild_id):
+        if str(guild_id) in self.voice:
+            return self.voice[str(guild_id)]
+        else:
+            return None
+
+    def get_user_guild(self, user):
+        print('Get user guild enter for {}'.format(user.id))
+        if str(user.id) in self.data['user']:
+            print('Found user, returning..')
+            retval = discord.utils.get(self.grahbot.guilds, id=self.data['user'][str(user.id)])
+            print(retval)
+            if type(retval) != list:
+                retval = [retval]
+            return retval
+        else:
+            return None
+
+    def set_user_guild(self, user, guild):
+        self.data['user'][str(user.id)] = guild.id
+        self.save()
+        
+class HornClient(object):
+    def __init__(self, config):
+        self.rabbit_config = config
+
+    def process_msg(self, msg):
+        print('>> {}'.format(msg.body))
+
+    async def rabbit_connect(self):
+        print('Creating rabbitmq socket')
+        # CREATE SOCKET
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+        # WRAP SOCKET
+        sock = ssl.wrap_socket(sock)
+        sock.connect((self.rabbit_config['host'], int(self.rabbit_config['port'])))
+
+        connection = await asynqp.connect(virtual_host=self.rabbit_config['vhost'], username=self.rabbit_config['user'], password=self.rabbit_config['password'], sock=sock)
+        print('Connected to rabbitmq')
+        channel = await connection.open_channel()
+        print('Declaring exchange')
+        exchange = await channel.declare_exchange(self.rabbit_config['exchange'], 'topic', passive=True)
+
+        # we have 10 users. Set up a queue for each of them
+        # use different channels to avoid any interference
+        # during message consumption, just in case.
+        self.channel = await connection.open_channel()
+        print('Declaring queue')
+        self.queue = await self.channel.declare_queue('horn_listener')
+        await self.queue.bind(exchange, routing_key=self.rabbit_config['exchange'])
+        print('Bound')
+        await self.queue.consume(self.process_msg, no_ack=True)
+        print('Consumed a thing')
+
+        # deliver 10 messages to each user
+        while (True):
+            await asyncio.sleep(1)
+            #msg = asynqp.Message("omg here is the time {}".format(datetime.datetime.now()))
+            #exchange.publish(msg, routing_key=self.rabbit_config['exchange'])
+
+
+class GrahDiscordException(Exception):
+    pass
+
+
+
+class GrahDiscordBot(discord.Client, HornClient):
+    AUDIO_FILES = ['mp3', '.ogg']
+    def process_msg(self, msg):
+        print('Received a message!!! {}'.format(msg))
+        if msg._properties['content_type'] == 'application/json':
+            # decode the object from json
+            obj = json.loads(msg.body.decode(msg._properties['content_encoding']))
+        else:
+            obj = msg.body
+
+        if 'output_type' in obj and obj['output_type'] == 'discord':
+            print('Received a horn for us! {}'.format(msg.body))
+            asyncio.ensure_future(self.horn(dict(sample_name=obj['sample_name']), properties=None))
+        else:
+            print('Received a horn not for us: {}'.format(msg.body))
+
+    def __init__(self, config):
+        self.config = config
+        self.player = None
+        self.used = []
+        self.state = GrahState(grahbot=self)
+
+        self.loop = asyncio.get_event_loop()
+        #self.horn_client = HornClient('config-piege.json')
+        HornClient.__init__(self, config)
+        self.rabbit = self.loop.create_task(self.rabbit_connect())
+        discord.Client.__init__(self, loop=self.loop)
+        self.loop_forever()
+
+    def terminate_all(self, guild):
+        if self.state.get_voice(guild):
+            if self.state.get_voice(guild).is_playing():
+                self.state.get_voice(guild).stop()
+
+    def get_sample_name(self):
+        selections = os.listdir(self.config['airhorn_directory'])
+        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:
+                self.used.pop(use)
+        while len(self.used) < len(selections) and sample_name in self.used:
+            print('already used, researching')
+            sample_name = random.choice(selections)
+        self.used.append(sample_name)
+        if len(self.used) == len(selections):
+            self.used = []
+        sample_name = "{}/{}".format(self.config['airhorn_directory'], sample_name)
+        print('{} selected'.format(sample_name))
+        return sample_name
+
+    async def horn(self, obj, properties):
+        try:
+            print('Horn! {}'.format(obj))
+            # exclusive access on each horn or not?
+            if 'exclusive' in obj:
+                exclusive = obj['exclusive']
+            else:
+                exclusive = False
+
+            if obj['sample_name'] == 'random':
+                sample_name = self.get_sample_name()
+            elif obj['sample_name'] == 'terminate':
+                self.terminate_all()
+                return None
+            else:
+                sample_name = '{}/{}'.format(self.config['airhorn_directory'], obj['sample_name'])
+        except:
+            traceback.print_exc()
+            print('Error object was: {}'.format(obj))
+        else:
+            await self.msg_play_filename(sample_name, guild_id=336396117791211522, exclusive=exclusive)
+
+
+        
+
+    # for further manipulation if we want to do funky stuff
+    def loop_forever(self):
+        try:
+            self.loop.run_until_complete(self.start(self.config['token']))
+        except KeyboardInterrupt:
+            self.loop.run_until_complete(self.logout())
+            pending = asyncio.all_tasks(loop=self.loop)
+            gathered = asyncio.gather(*pending, loop=self.loop)
+            try:
+                gathered.cancel()
+                self.loop.run_until_complete(gathered)
+
+                # we want to retrieve any exceptions to make sure that
+                # they don't nag us about it being un-retrieved.
+                gathered.exception()
+            except:
+                pass
+        except Exception:
+            pass
+        finally:
+            self.loop.close()
+
+    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()])
+        #await self.user.edit(nick='Varimathras')
+        print(dir(self))
+        print(list(self.guilds))
+        for guild in self.guilds:
+            print('Checking for rejoin of {}'.format(guild.__repr__()))
+            if self.state.get_voice_id(guild):
+                print('Rejoining prior voice channel')
+                await self.msg_join_voice_channel(self.state.get_voice_id(guild).name, guild)
+        self.member_guilds = {}
+        print(self.voice_clients)
+        print('------')
+
+    @property
+    def voice_channels(self):
+        return [x for x in self.get_all_channels() if x.type == discord.ChannelType.voice]
+
+
+    @property
+    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
+
+    async def msg_play_sound(self, name=None, guild=None):
+        if name:
+            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_id=guild.id)
+
+    async def msg_play_filename(self, filename, guild_id, exclusive=False):
+        guild = discord.utils.get(self.guilds, id=guild_id)
+        if self.state.get_voice(guild):
+            self.terminate_all(guild=guild)
+            self.state.get_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, guild):
+        print('Called join voice channel')
+        for vc in self.voice_channels:
+            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.state.get_voice(guild):
+                    if vc != self.state.get_voice(guild).channel:
+                        self.state.set_voice(guild, await self.state.get_voice(guild).move_to(vc))
+                else:
+                    self.state.set_voice(guild, await vc.connect())
+                print('Joined voice channel {} {} (id: {})'.format(guild, vc.name, vc.id))
+                print('Voice now {}'.format(self.state.get_voice(guild)))
+
+        print('Exit join voice')
+
+    def get_airhorn_filenames(self):
+        retval = []
+        for x in os.listdir(self.config['airhorn_directory']):
+            if self.is_audio_file(x):
+                retval.append(x.lower())
+        retval = sorted([x[:-4] for x in retval])
+        print('Found airhorn files:\n{}'.format('\n'.join(retval)))
+        return retval
+
+        
+    def chunk_it_up(self, filename_list):
+        yieldval = []
+        char_limit = 1950 
+        cur_chars = 0
+        for index, filename in enumerate(filename_list):
+            yieldval.append(filename)
+            cur_chars += len(yieldval)
+            if index + 1 < len(filename_list):
+                if len(filename_list[index+1]) + cur_chars > char_limit:
+                    print('Yielding {}'.format(yieldval))
+                    yield yieldval
+                    yieldval = []
+                    cur_chars = 0
+
+        if yieldval:
+            print('Final yield {}'.format(yieldval))
+            yield yieldval
+                    
+
+    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 channel.send(exc_string)
+
+    def print_call(self, message):
+        print('#{} <{}>: {}'.format(message.channel, message.author, message.content))
+
+
+    def member_guild_list(self, member):
+        return [x for x in self.guilds if member in x.members]
+
+    def contextual_guild(self, message):
+        channel = message.channel
+        if isinstance(channel, discord.DMChannel):
+            print('Is a DM with {}'.format(channel.recipient))
+            retval = self.member_guild_list(channel.recipient)
+            print('Member guilds: {}'.format(retval))
+            return retval
+        else:
+            print('Guild: {}'.format(channel.guild))
+            return [channel.guild]
+
+
+    def channel_guild(self, message):
+        if isinstance(message.channel, discord.DMChannel):
+            return self.state.get_user_guild(message.channel.recipient)
+        else:
+            return self.contextual_guild(message)
+
+    def select_guild(self, message):
+        print('Selectguild {}'.format(message.content))
+        guilds = self.contextual_guild(message)
+        if len(guilds) < 2:
+            raise Exception('No, this is not needed')
+        guild_name = message.content.replace('!selectguild ', '')
+        for guild in guilds:
+            if guild.name == guild_name:
+                print('Setting member {} to guild {}'.format(message.channel.recipient, guild.name))
+                self.member_guilds[message.channel.recipient] = [guild]
+                self.state.set_user_guild(message.channel.recipient, guild)
+                break
+        
+
+
+    def debug_info(self):
+        return socket.gethostname()
+
+    async def on_message(self, message):
+        try:
+            if message.author != self.user:
+                print('gettin guilds here')
+                guilds = self.channel_guild(message)
+                print(guilds)
+                if message.content.startswith('!selectguild'):
+                    self.select_guild(message)
+                    return
+
+                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('!join'):
+                    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()):
+                        print('Sending {}'.format(airhorn_files))
+                        await message.channel.send('```\n{}```'.format('\n'.join(airhorn_files)))
+                elif message.content == '!leave':
+                    await self.state.get_voice(guild).disconnect()
+                    self.state.set_voice(guild, None)
+                elif message.content.startswith('!selectguild'):
+                    self.print_call(message)
+                    await self.select_guild(message)
+                elif message.content == '!help':
+                    self.print_call(message)
+                    await message.channel.send('```\n!join [channel]\n!play [file]\n!list (for all of them)\n!leave```')
+                elif message.content == '!whoami':
+                    self.print_call(message)
+                    await message.channel.send('```{}```'.format(self.debug_info()))
+        except Exception as e:
+            await self.oh_no(message.channel, e)
+
+if __name__ == '__main__':
+    config = json.load(open('{}/config.json'.format(PERSISTENT_DIR), 'r'))
+    bot = GrahDiscordBot(config)
diff --git a/wigglydiscord.py b/wigglydiscord.py
deleted file mode 100755 (executable)
index ca8977b..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-#!/usr/bin/python3
-
-import sys
-import discord
-import socket
-import random
-import ssl
-import asynqp
-import discord.voice_client
-import datetime
-import asyncio
-import json
-import traceback
-import os
-
-class HornClient(object):
-    def __init__(self, config):
-        self.rabbit_config = config
-
-    def process_msg(self, msg):
-        print('>> {}'.format(msg.body))
-
-    @asyncio.coroutine
-    async def rabbit_connect(self):
-        # CREATE SOCKET
-        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-        # WRAP SOCKET
-        sock = ssl.wrap_socket(sock)
-        sock.connect((self.rabbit_config['host'], int(self.rabbit_config['port'])))
-
-        connection = await asynqp.connect(virtual_host=self.rabbit_config['vhost'], username=self.rabbit_config['user'], password=self.rabbit_config['password'], sock=sock)
-        channel = await connection.open_channel()
-        exchange = await channel.declare_exchange(self.rabbit_config['exchange'], 'topic', passive=True)
-
-        # we have 10 users. Set up a queue for each of them
-        # use different channels to avoid any interference
-        # during message consumption, just in case.
-        self.channel = await connection.open_channel()
-        self.queue = await self.channel.declare_queue('horn_listener')
-        await self.queue.bind(exchange, routing_key=self.rabbit_config['exchange'])
-        await self.queue.consume(self.process_msg, no_ack=True)
-
-        # deliver 10 messages to each user
-        while (True):
-            await asyncio.sleep(1)
-            #msg = asynqp.Message("omg here is the time {}".format(datetime.datetime.now()))
-            #exchange.publish(msg, routing_key=self.rabbit_config['exchange'])
-
-
-class GrahDiscordException(Exception):
-    pass
-
-
-
-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
-            obj = json.loads(msg.body.decode(msg._properties['content_encoding']))
-        else:
-            obj = msg.body
-
-        if 'output_type' in obj and obj['output_type'] == 'discord':
-            print('Received a horn for us! {}'.format(msg.body))
-            asyncio.async(self.horn(dict(sample_name=obj['sample_name']), properties=None))
-        else:
-            print('Received a horn not for us: {}'.format(msg.body))
-
-    def __init__(self, config):
-        self.config = config
-        self.voice = {}
-        self.player = None
-        self.used = []
-
-        self.loop = asyncio.get_event_loop()
-        #self.horn_client = HornClient('config-piege.json')
-        HornClient.__init__(self, config)
-        discord.Client.__init__(self, loop=self.loop)
-        asyncio.async(self.rabbit_connect())
-        self.loop_forever()
-
-    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 self.is_audio_file(x)]
-        sample_name = random.choice(selections)
-        for index, use in enumerate(self.used):
-            if use not in selections:
-                self.used.pop(use)
-        while len(self.used) < len(selections) and sample_name in self.used:
-            print('already used, researching')
-            sample_name = random.choice(selections)
-        self.used.append(sample_name)
-        if len(self.used) == len(selections):
-            self.used = []
-        sample_name = "{}/{}".format(self.config['airhorn_directory'], sample_name)
-        print('{} selected'.format(sample_name))
-        return sample_name
-
-    @asyncio.coroutine
-    async def horn(self, obj, properties):
-        try:
-            print('Horn! {}'.format(obj))
-            # exclusive access on each horn or not?
-            if 'exclusive' in obj:
-                exclusive = obj['exclusive']
-            else:
-                exclusive = False
-
-            if obj['sample_name'] == 'random':
-                sample_name = self.get_sample_name()
-            elif obj['sample_name'] == 'terminate':
-                self.terminate_all()
-                return None
-            else:
-                sample_name = '{}/{}'.format(self.config['airhorn_directory'], obj['sample_name'])
-        except:
-            traceback.print_exc()
-            print('Error object was: {}'.format(obj))
-        else:
-            await self.msg_play_filename(sample_name, exclusive=exclusive)
-
-
-        
-
-    # for further manipulation if we want to do funky stuff
-    def loop_forever(self):
-        try:
-            self.loop.run_until_complete(self.start(self.config['token']))
-        except KeyboardInterrupt:
-            self.loop.run_until_complete(self.logout())
-            pending = asyncio.Task.all_tasks(loop=self.loop)
-            gathered = asyncio.gather(*pending, loop=self.loop)
-            try:
-                gathered.cancel()
-                self.loop.run_until_complete(gathered)
-
-                # we want to retrieve any exceptions to make sure that
-                # they don't nag us about it being un-retrieved.
-                gathered.exception()
-            except:
-                pass
-        finally:
-            self.loop.close()
-
-    @asyncio.coroutine
-    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(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
-    def voice_channels(self):
-        return [x for x in self.get_all_channels() if x.type == discord.ChannelType.voice]
-
-
-    @property
-    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, guild=None):
-        if name:
-            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, 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, guild):
-        print('Called join voice channel')
-        for vc in self.voice_channels:
-            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[guild]:
-                    if vc != self.voice[guild].channel:
-                        await self.voice[guild].move_to(vc)
-                else:
-                    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):
-        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):
-        yieldval = []
-        char_limit = 1950 
-        cur_chars = 0
-        for index, filename in enumerate(filename_list):
-            yieldval.append(filename)
-            cur_chars += len(yieldval)
-            if index + 1 < len(filename_list):
-                if len(filename_list[index+1]) + cur_chars > char_limit:
-                    yield yieldval
-                    yieldval = []
-                    cur_chars = 0
-
-        if yieldval:
-            yield yieldval
-                    
-
-    @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 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.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 = GrahDiscordBot(config)