Logging support (debug is default for now) and some modularity on the command execution
authorjweigele <jweigele@local>
Sun, 20 Sep 2020 19:46:55 +0000 (12:46 -0700)
committerjweigele <jweigele@local>
Sun, 20 Sep 2020 19:46:55 +0000 (12:46 -0700)
Dockerfile
grahbot.py

index ebfe783ad80d3d28e4f09ba85eeebb55f7e6504f..ed72f14cfa29242a02c63e7fb7fea1771d7e49f4 100644 (file)
@@ -5,9 +5,8 @@ FROM ubuntu
 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 apt-get install -y --no-install-recommends python3-pip ffmpeg
 RUN pip3 install discord.py asynqp PyNaCl
 
 # Run the specified command within the container.
index 340d1de4c0277eedcb53e8aa9f420eff8c4cd77a..173adb37ab45657c6538ef12c758802eb2b49bb9 100755 (executable)
@@ -11,6 +11,13 @@ import asyncio
 import json
 import traceback
 import os
+import logging
+log = logging.getLogger(__name__)
+log.setLevel(logging.DEBUG)
+ch = logging.StreamHandler()
+ch.setFormatter(logging.Formatter('|%(levelname)s|%(asctime)s|%(module)s| %(message)s'))
+log.addHandler(ch)
+
 from collections import defaultdict, OrderedDict
 
 PERSISTENT_DIR = '/var/lib/grahbot'
@@ -50,7 +57,7 @@ class GrahState(object):
 
 
     def get_voice_id(self, guild):
-        print('here is self.data for guild {}'.format(self.data['guild']))
+        log.debug('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:
@@ -66,11 +73,11 @@ class GrahState(object):
             return None
 
     def get_user_guild(self, user):
-        print('Get user guild enter for {}'.format(user.id))
+        log.debug('Get user guild enter for {}'.format(user.id))
         if str(user.id) in self.data['user']:
-            print('Found user, returning..')
+            log.debug('Found user, returning..')
             retval = discord.utils.get(self.grahbot.guilds, id=self.data['user'][str(user.id)])
-            print(retval)
+            log.debug(retval)
             if type(retval) != list:
                 retval = [retval]
             return retval
@@ -86,10 +93,10 @@ class HornClient(object):
         self.rabbit_config = config
 
     def process_msg(self, msg):
-        print('>> {}'.format(msg.body))
+        log.debug('>> {}'.format(msg.body))
 
     async def rabbit_connect(self):
-        print('Creating rabbitmq socket')
+        log.debug('Creating rabbitmq socket')
         # CREATE SOCKET
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
@@ -98,21 +105,21 @@ class HornClient(object):
         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')
+        log.info('Connected to rabbitmq')
         channel = await connection.open_channel()
-        print('Declaring exchange')
+        log.debug('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')
+        log.debug('Declaring queue')
         self.queue = await self.channel.declare_queue('horn_listener')
         await self.queue.bind(exchange, routing_key=self.rabbit_config['exchange'])
-        print('Bound')
+        log.debug('Bound')
         await self.queue.consume(self.process_msg, no_ack=True)
-        print('Consumed a thing')
+        log.debug('Consumed a thing')
 
         # deliver 10 messages to each user
         while (True):
@@ -128,8 +135,10 @@ class GrahDiscordException(Exception):
 
 class GrahDiscordBot(discord.Client, HornClient):
     AUDIO_FILES = ['mp3', '.ogg']
+    CMD_MARKER = '!'
+    CMD_PREFIX = 'command_'
     def process_msg(self, msg):
-        print('Received a message!!! {}'.format(msg))
+        log.debug('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']))
@@ -137,10 +146,10 @@ class GrahDiscordBot(discord.Client, HornClient):
             obj = msg.body
 
         if 'output_type' in obj and obj['output_type'] == 'discord':
-            print('Received a horn for us! {}'.format(msg.body))
+            log.debug('Received a horn for us! {}'.format(msg.body))
             asyncio.ensure_future(self.horn(obj, properties=None))
         else:
-            print('Received a horn not for us: {}'.format(msg.body))
+            log.debug('Received a horn not for us: {}'.format(msg.body))
 
     def __init__(self, config):
         self.config = config
@@ -168,18 +177,18 @@ class GrahDiscordBot(discord.Client, HornClient):
             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')
+            log.debug('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))
+        log.debug('{} selected'.format(sample_name))
         return sample_name
 
     async def horn(self, obj, properties):
         try:
-            print('Horn! {}'.format(obj))
+            log.info('Horn! {}'.format(obj))
             # exclusive access on each horn or not?
             if 'exclusive' in obj:
                 exclusive = obj['exclusive']
@@ -195,7 +204,7 @@ class GrahDiscordBot(discord.Client, HornClient):
                 sample_name = '{}/{}'.format(self.config['airhorn_directory'], obj['sample_name'])
         except:
             traceback.print_exc()
-            print('Error object was: {}'.format(obj))
+            log.debug('Error object was: {}'.format(obj))
         else:
             await self.msg_play_filename(sample_name, guild_id=obj['guild'], exclusive=exclusive)
 
@@ -225,19 +234,19 @@ class GrahDiscordBot(discord.Client, HornClient):
             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()])
+        log.debug('Logged in as {} ({})'.format(self.user.name, self.user.id))
+        log.debug([x for x in self.get_all_channels()])
         #await self.user.edit(nick='Varimathras')
-        print(dir(self))
-        print(list(self.guilds))
+        log.debug(dir(self))
+        log.debug(list(self.guilds))
         for guild in self.guilds:
-            print('Checking for rejoin of {}'.format(guild.__repr__()))
+            log.debug('Checking for rejoin of {}'.format(guild.__repr__()))
             if self.state.get_voice_id(guild):
-                print('Rejoining prior voice channel')
+                log.debug('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('------')
+        log.debug(self.voice_clients)
+        log.debug('------')
 
     @property
     def voice_channels(self):
@@ -281,23 +290,23 @@ class GrahDiscordBot(discord.Client, HornClient):
             self.state.get_voice(guild).play(discord.FFmpegPCMAudio(filename))
             #self.player.start()
         else:
-            print('Was asked to play {} but no voice channel'.format(filename))
+            log.debug('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')
+        log.debug('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)))
+                log.debug('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)))
+                log.info('Joined voice channel {} {} (id: {})'.format(guild, vc.name, vc.id))
+                log.debug('Voice now {}'.format(self.state.get_voice(guild)))
 
-        print('Exit join voice')
+        log.debug('Exit join voice')
 
     def get_airhorn_filenames(self):
         retval = []
@@ -305,7 +314,7 @@ class GrahDiscordBot(discord.Client, HornClient):
             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)))
+        log.debug('Found airhorn files:\n{}'.format('\n'.join(retval)))
         return retval
 
         
@@ -318,24 +327,28 @@ class GrahDiscordBot(discord.Client, HornClient):
             cur_chars += len(yieldval)
             if index + 1 < len(filename_list):
                 if len(filename_list[index+1]) + cur_chars > char_limit:
-                    print('Yielding {}'.format(yieldval))
+                    log.debug('Yielding {}'.format(yieldval))
                     yield yieldval
                     yieldval = []
                     cur_chars = 0
 
         if yieldval:
-            print('Final yield {}'.format(yieldval))
+            log.debug('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))
+        log.debug("Exception: {}\t{}".format(channel, exc))
+        exc_string = 'Ruh Roh! {}'.format(str(exc))
         traceback.print_exc()
         await channel.send(exc_string)
 
     def print_call(self, message):
-        print('#{} <{}>: {}'.format(message.channel, message.author, message.content))
+        if isinstance(message.channel, discord.DMChannel):
+            guild_str = '' 
+        else:
+            guild_str = str(message.channel.guild)
+        log.info('{}#{} <{}>: {}'.format(guild_str, message.channel, message.author, message.content))
 
 
     def member_guild_list(self, member):
@@ -344,12 +357,12 @@ class GrahDiscordBot(discord.Client, HornClient):
     def contextual_guild(self, message):
         channel = message.channel
         if isinstance(channel, discord.DMChannel):
-            print('Is a DM with {}'.format(channel.recipient))
+            log.debug('Is a DM with {}'.format(channel.recipient))
             retval = self.member_guild_list(channel.recipient)
-            print('Member guilds: {}'.format(retval))
+            log.debug('Member guilds: {}'.format(retval))
             return retval
         else:
-            print('Guild: {}'.format(channel.guild))
+            log.debug('Guild: {}'.format(channel.guild))
             return [channel.guild]
 
 
@@ -359,70 +372,94 @@ class GrahDiscordBot(discord.Client, HornClient):
         else:
             return self.contextual_guild(message)
 
-    def select_guild(self, message):
-        print('Selectguild {}'.format(message.content))
+    async def command_selectguild(self, message, guild):
+        log.debug('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))
+                log.debug('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
-        
 
+    async def command_sleep(self, message, guild):
+        await asyncio.sleep(5)
+        await message.channel.send('Done sleeping')
+
+    async def command_join(self, message, guild):
+        log.info('Was instructed to join {} guild {}'.format(message.content.split(' ')[1], str(guild)))
+        await self.msg_join_voice_channel(message.content.split(' ')[1], guild=guild)
+
+    async def command_play(self, message, guild):
+        log.info('Was instructed to play {}'.format(message.content.split(' ')[1]))
+        await self.msg_play_sound(name=' '.join(message.content.split(' ')[1:]), guild=guild)
+
+    async def command_list(self, message, guild):
+        log.info('Asked for list')
+        for airhorn_files in self.chunk_it_up(self.get_airhorn_filenames()):
+            log.debug('Sending {}'.format(airhorn_files))
+            await message.channel.send('```\n{}```'.format('\n'.join(airhorn_files)))
+
+    async def command_leave(self, message, guild):
+        await self.state.get_voice(guild).disconnect()
+        self.state.set_voice(guild, None)
+
+    async def command_help(self, message, guild):
+        await message.channel.send('```\n!join [channel]\n!play [file]\n!list (for all of them)\n!leave```')
+
+
+    async def command_whoami(self, message, guild):
+        await message.channel.send('```{}```'.format(self.debug_info()))
 
     def debug_info(self):
         return socket.gethostname()
 
+
+    def get_command_names(self):
+        cmds = [self.CMD_SYMBOL+x.replace('command_', '') for x in dir(self) if x.startswith('command_')]
+
+    def get_command_func(self, command_name):
+        command_func_names = [x for x in dir(self) if x.startswith(self.CMD_PREFIX)]
+        log.debug('searching for command with name {}'.format(command_name))
+        log.debug('command func names {}'.format(command_func_names))
+        search_func_name = '{}{}'.format(self.CMD_PREFIX, command_name)
+        if search_func_name in command_func_names:
+            search_func = getattr(self, search_func_name)
+            return search_func
+        raise GrahDiscordException('Did not find {} in commands'.format(command_name))
+
+    async def handle_command(self, message):
+        self.print_call(message)
+        log.debug('gettin guilds here')
+        guilds = self.channel_guild(message)
+        log.debug(guilds)
+        potential_command = message.content[1:].split(' ')[0]
+        
+        if potential_command == 'selectguild':
+            func = self.get_command_func(potential_command)
+            await func(message, guild=None)
+            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]
+
+        func = self.get_command_func(potential_command)
+        await func(message, guild)
+
     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()))
+                if message.content.startswith(self.CMD_MARKER):
+                    await self.handle_command(message)
         except Exception as e:
             await self.oh_no(message.channel, e)