Meet MusicBot

So, weeks 1 and 2 of RedLabs was a major success. It’s been great getting to know the other engineers and the business. But the greatest thing is getting to code all day. This concept is new to me, as coding has always been something I had to squeeze into my schedule. It’s almost unbelievable that this is what I’m supposed to be doing all day. I almost feel like I’m getting away with something.

After some orientation meetings early in the first week, we sat down with the RedLabs team leader and worked out our first projects. Myself and two other interns picked to work on building a Slackbot that would allow anyone in the office to easily collaborate on the music that plays in the office during the day.

The current setup for music in the office is a Sonos device wired to the office sound system. A Spotify playlist, curated collaboratively, is played on shuffle/repeat on the Sonos. A few individuals have installed the Sonos app on their phones, and occasionally skip tracks, adjust volume, etc.

Our goal was to develop a system that would allow anyone in the office to add to the playlist over Slack, thereby further democratizing the musical selections. We also had to be careful not to make the system easy to troll. With these general goals in mind, we sat down and speced out our bot. It would:

  • Allow anyone to add a track to be the next in the queue.
  • Get info for the current track playing.
  • See the next five tracks in the playlist.
  • Allow three unique users to veto the current track.

Additionally, certain people would be added as admins to the system and would be able to:

  • Adjust the volume
  • Play and pause the music.
  • Skip to the next track in the queue.
  • Go back to the previous track.
  • Remove items from the queue.

Our system is set up as so:

  • A Python/Flask web server running on a Digital Ocean droplet handles requests from Slack, and interacts with the Sonos and the Spotify API.
  • Slack integration is done through an Outgoing Web Hook. We created a channel for our musicbot. All communication on this channel is monitored by the bot, who is looking for messages starting with the word musicbot. This sends a POST request to our server, which springs into action.
  • Flask is listening for all POST requests on ‘/’. The text and user of the chat message are then processed by a command handler function, which calls the appropriate method on a class we’ve created to handle Spotify and Sonos interactions.
  • To work with our Sonos device, we found a library called SoCo, which is a Python wrapper for the Sonos device API. It provides a bunch of methods for controlling playback, and getting information from and editing the queue.
  • For Spotify integration, we are using their API to perform searches and pull from our office playlist if no one has suggested the next track.

We have our outgoing webhook set to listen to any communication on a separate channel we create. A typical interaction with the app works like this: A user types in add [search query] to search for a track to add. An ordered list of the top 5 results is returned to the user. The user types the desired to add that song to the next song in the queue. There is other functionality as well, which is outlined above.

Building this app has been a great learning experience, and has provided many challenges.

The biggest challenge is that we are writing the app in Python. Prior to last week, I knew very little Python. I have been impressed with how easy-to-learn and versatile the language is. I’ve got tripped up by a few things (no ++ and — operators for one), but for the most part it’s been going well. Also, this is the first time that I’ve had to work with a third party API, let alone three different ones. It’s been a slow process digging through the docs every step of the way.

Initially we had a challenge related to network architecture. When developing locally, we were able to access the Sonos (because it’s on the same LAN as our machines), but could not interact with Slack, as there was no easy way for Slack to reach our locally-running web server. Once we moved our app to the droplet, we could access Slack, but not the Sonos device, because we were no longer running it on our LAN. This problem was solved with the help of the IT administrator who set up port forwarding to the Sonos, which allows us to access it from outside of the LAN.

Another challenge is that we discovered some issues with the library we are using to access the Sonos. When a track is added to the queue with a Spotify URI, it adds the track with no metadata. The track will play, but this breaks our current and upnext commands. The issue is apparently with the library itself. There’s an open issue on the repo, but it hasn’t had any activity since February, so we are left on our own to solve the problem. We came up with a work-around to use for the time being. Since we have access to the metadata from Spotify, we are caching it in dictionary in our server that matches the data to the URI. This way, when our current upnext commands are run, the URI is read off of the Sonos, and then matched with cached metadata on the server before returning it to the user.

As of right now, our bot is up-and-running. During the course of the week, we have encountered bugs that we have had to fix. One bug in particular that was quite elusive at first. We discovered that certain tracks just would not play. It was always specific tracks, but there seemed to be no pattern to which ones would play, and which ones wouldn’t. We finally figured out it was that our searches were returning results for all markets, while our Sonos would only play songs tagged for the US market. Once we figured this would, it was just a matter of modifying our search query to only return song URIs that were playable in the US.

We also had a thorough code review of the project, which was humbling. I’ll save the details of the review for another post, but we got a lot of great feedback, and suggestions on how to implement best practices into our work. Looking forward to starting week three!

The postings on this site are my own and I take full responsibility for them. I am not speaking as a representative of my employer, any company or organization.

Leave a Reply

Your email address will not be published. Required fields are marked *