Further iteration on audio, now obeys the airhorn webpage if it's fed an output_type...
authorjweigele <jweigele@local>
Tue, 25 Jul 2017 01:01:53 +0000 (18:01 -0700)
committerjweigele <jweigele@local>
Tue, 25 Jul 2017 01:01:53 +0000 (18:01 -0700)
wigglydiscord.py

index 6d7413d62483488672aee3cf2c5167b26bd97d1f..40e285084301589d6770e09fa268fa6791283ed5 100755 (executable)
 
 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
 
+SOUND_DIR="soundboard/"
+
+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 WigglyDiscordException(Exception):
     pass
 
-class WigglyDiscordBot(discord.Client):
+
+
+class WigglyDiscordBot(discord.Client, HornClient):
+    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 = None
         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):
+        if self.voice:
+            if self.player:
+                if self.player.is_playing():
+                    self.player.stop()
 
-        super().__init__()
-        self.run(self.config['token'])
+    def get_sample_name(self):
+        selections = os.listdir(self.config['airhorn_directory'])
+        selections = [x for x in selections if '.mp3' in 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')
-        print(self.user.name)
-        print(self.user.id)
+        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]))
@@ -40,21 +169,22 @@ class WigglyDiscordBot(discord.Client):
     def text_channels(self):
         return [x for x in self.get_all_channels() if x.type == discord.ChannelType.text]
 
-
     @asyncio.coroutine
     async def msg_play_sound(self, name=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)
+
+    @asyncio.coroutine
+    async def msg_play_filename(self, filename, exclusive=False):
         if self.voice:
-            if self.player:
-                if self.player.is_playing():
-                    self.player.stop()
-            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))
-            else:
-                filename = '{}/coool.mp3'.format(self.config['airhorn_directory'])
+            self.terminate_all()
             self.player = self.voice.create_ffmpeg_player(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):