Saturday 4 July 2020

Tutorial: Making Slack Apps with Python

I wrote this tutorial during Unity Hackweek 2020. I helped lead a group that focused on learning rather than producing (but to also hopefully create some small tools to help out the Documentation team!), and I created this to help the team get started with Slack apps. A few people in the group spent the week learning Python, and while I was looking for resources to share, I noticed that a lot of the tutorials available really over complicated the process! Thank you so much to Siobhan who edited this tutorial as she worked through it herself.

This tutorial covers: 

  • How to send a simple message to Slack
  • How to request API data from a service (such as Favro or Jira)
  • How to send that API data in a Slack message
This tutorial assumes at least a beginner-level understanding of writing Python scripts and working in the command line. Some knowledge of manipulating JSON will also be helpful for the later parts of this tutorial.

Part 1: Send a simple message to Slack

First, let’s learn how to send a simple message to Slack. To send messages and data to Slack, you need to use an HTTP POST request. An HTTP POST request is a method that asks a server to accept data. It carries this data in the body of the request message. 


To get set up, work through Slack's guide to Sending messages using Incoming Webhooks until you’ve completed step 4. You should now have a Slack App, and on the Incoming Webhooks page of your slack app you should see something like this:




Set up a webhook, and point it at a private test channel or your own slack user for now, while you test it.


You can copy these curl commands into your command line/terminal to run them and perform the action they represent. You can also translate them to Python and use Python’s Requests library to send POST requests. To use the Requests library, you need to install pip (if it is not already installed), and then open your command line terminal and type pip install requests.


This table demonstrates the curl command in the image above, and a Python translation. 


curl command
curl =X POST -H 'Content-type: application/json' --data '{"text:"Hello, World!"}'
YOUR_SLACK_WEBHOOK_URL_HERE
Python
import requests (put this line at the top of your python file)
slack_response = requests.post('YOUR_SLACK_WEBHOOK_URL_HERE', data='{"text":"Hello, World! I am posting from a Python script. Magic!"}', headers={'Content-type': 'application/json'})


In the translation, the curl command's -H parameter corresponds to headers in Python, and the --data parameter corresponds to data.


Run your Python script in the command line terminal. If everything is configured correctly, the message "I am posting from a Python script. Magic!" should appear in the target Slack channel. You did it!

Part 2: Get API data from a service and send it to Slack

Now that you know how to use Python to send a message to a Slack channel, let’s try to get some data from a service and send it in a Slack message. 

Part 2.1: Get API data from a service

To request data from a service's API, you need to send an HTTP GET request. An HTTP GET request is a method that requests data from a specified resource (in this case, the service API). 

For this tutorial, I use the Favro API as an example.


When you look at a service's API docs, you might see some examples in the form of CURL commands. CURL is a command line tool to transfer data to or from a server.




You can copy these curl commands into your command line/terminal to run them and perform the action they represent. You can also translate them to Python and use Python’s Requests library to send GET requests. This is useful if you want to do this repeatedly, or if you want to use the information you get from the output into another command.


This table demonstrates the curl command in the image above, and a Python translation. 


curl command
curl =X GET "https://favro.com/api/v1/users"
-H "'organizationId': YOUR_ORG_ID"
-u "user@example.com":"password"
Python
import requests (put this line at the top of your python file)
request = requests.get('https://favro.com/api/v1/users', headers={'organizationId': 'YOUR_ORG_ID', auth= ('user@example.com', 'password'))


In the translation, the curl command's -H parameter corresponds to headers in Python, and the -u parameter corresponds to auth.


NOTE: Don't put your Slack or service password in this file! Most API services let you generate a secret API key that you can revoke at any time. Use the API key in place of a password here.


Once you have completed this translation, you can print the output to check the HTTP Status Code that it returns. To do this, insert the following underneath your requests.get code:


print(r)


Run your file in the command line terminal. Usually, an HTTP Status Code of 200 means your code works as expected.

Part 2.2: Send API data in a Slack message



It is common for an API to return data in JSON format, but you can send any data back to Slack as long as you format it.


The Slack Bot Kit Builder is a really convenient way to design how your message will look and then use the JSON it generates (on the right).


I won’t go into to how to parse JSON using Python in this tutorial, but you can replace the “text” sections that you generated using the Block Kit Builder with the information you got from the API you're getting data from (in my case, Favro). Here’s an example:


JSON as a multi-line Python string
data_to_send = '''{
"blocks": [
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "%s",
"emoji": true
}
},
{
"type": "divider"
}
]
}''' % favro_card_name
Python
slack_response = requests.post('YOUR_SLACK_WEBHOOK_URL_HERE', data=data_to_send, headers={'Content-type': 'application/json'})


Sending a response like the above will send a Slack message that looks something like this;
And you're done! Hopefully now you have a better understanding of how to use Python to get data from one API and send it to Slack using Slack's API.

Sunday 15 July 2018

Making musical toys at Unity Hackweek

Initial Idea & Inspiration

At Unity Hackweek 2018, a group of us worked on a music maker, which was eventually named Super Sound Synthesizer 3000. The aim was to create a music sequencer that lives in the Unity Editor as an editor window.

The idea for this came from mentoring at Girl Game Maker Day. We had provided code and art for the girls to make their game, but music was an afterthought (as it too often is in game jam environments, unfortunately). Even asking the girls to download some free music from the internet was a challenge, as most free music sites require registering, and many of them were too young to have an email address of their own. We needed something simpler to create music without learning a new tool.

After this initial idea, a lot of the early inspiration came from Doodle Studio 95. It’s fun, it’s playful, and it’s simple to create sprites and animations that can go directly into your Unity project. One of the main things we wanted to achieve with our music maker was that it should be easy for a beginner to create something, but someone more skilled can also create something great if they want to; Doodle Studio does this so well (as shown by the trailer for it!)

The team

I started rounding up the team a couple of months before Hackweek even started. First, I approached Andy Selby, since I knew he enjoys making digital music, and he had made music-related Unity projects in the past. He said “oh, yeah alright then”. One down.

Next I asked Siobhan Gibson and Hope Rudd. I can't remember which order I asked them in, but they both said something along the lines of “oh, that sounds fun”. Ideal.

Siobhan is really passionate about fun and simple game-making tools, so I hoped she’d be keen. Hope is new to programming and I knew this would be a great chance for them to deep dive into a project and work collaboratively with a team, so I’m glad they agreed! Hope is also in a band, so extra points for them.



Other people joined our Slack channel to ask questions, but it ended up just being the 4 of us in the team. I didn’t realise until later on how beneficial this would be. We all knew each other’s strengths and weaknesses, so we knew how to work together (even though none of us had actually worked in a group together before).

Further research

On the first day of Hackweek, we planned to only research, experiment and prototype. This gave us a chance to get used to the atmosphere of the hacking hall, and all decide how the project should look and feel.

Siobhan spent the day looking at other playful music tools (because us saying ‘Doodle Studio but music’ is not a sufficient design spec).

One of the stand-out tools that Siobhan found was Song Maker. We all fell in love with it pretty much immediately.

Toc and roll is also great. We especially loved the way you could use your voice and a sample, and that is has effects pedals! Siobhan was able to quickly made a lovely song (that actually got stuck in my head) and had a fun time showing us how to use it, which the the exact sort of thing we wanted from our music maker.

Siobhan used these tools as inspiration and created a mock-up in excel for how ours could look.



Andy has been making digital music for years, so he was able to provide great insights from the more professional angle, too. On the more professional side, we looked at tools like FLStudio and Reaper.

Microphone Recorder

As a sort of coding warm-up, Hope and I pair-programmed a very mini prototype of one of the desired features. We created an editor window where you enter how long you’d like to record for in seconds, then press record to start a microphone recording. This recording is saved as a .wav file inside the Unity project. We did this so we could get an idea how the workflow could work, and how wav files are created and saved. It also gave Hope a chance to make Editor Windows, and learn about the process that goes into creating tools. (Side note: Hope is a total joy to work with. They made notes about everything we went over, and asked good questions as we went. Not only did they learn a lot (hopefully!) but it made me slow down what I was doing and think about it.)



We used this code to save the AudioClip as a .wav file, and we had a lot from the Unity Manual to learn about how Microphones work with Unity.

UI elements

Super Sound Synthesizer 3000 was made using Unity’s UI Elements feature. We chose this instead of the existing Editor GUI so that we’d have more flexibility and a chance to play with a new area of Unity. UI Elements is much closer to web development than EditorGUI. This intimidated me, but Andy was great at learning the information and explaining it back to me. We had to do some odd tricks to make things work (for example, we had to manually flip through frames of a sprite animation because there is no support for gifs out of the box).

Buttons using GUILayout are simple to implements, as shown here: GUILayout.Button. A button in UI Elements requires a few more lines of code, as shown in their example project: UIElementsExamples.

Wav saving fail

The biggest hurdle was trying to get the audio created with our synthesizer to save as a .wav file. I investigated a few different methods:

  • Joining audioclips
    • This gets confusing when more than one clip plays at a time
  • An asset package: Audio Clip Combiner
  • Recording from “in-game” audio
    • We couldn’t figure out how to access it without using something like Soundflower

We ended up using a hacky version of the microphone recorder that Hope and I had worked on earlier in the week. It waits until the audio loop is back at the beginning, and it records for the length of time of one loop. It was terrible and included all the background noise from the room, but creating our own way to record in-editor audio somehow could have been another Hackweek project! Definitely not something to do on a Thursday evening.

Last few hours

Earlier in the week, I needed a sound to test the synthesizer. Luckily, I could just easily record a small clip using the Microphone Recorder window. Naturally, I meowed. I did not think about how this would be played whenever someone opened the window. It sort of became a meme within the team. We HAD to include this in the final product, so in the last few hours, Siobhan created a cat icon, and I added The Cat Button to our window.

We also wanted to add tempo buttons, as we pretty much already had the code to do it. To make it beginner-friendly, we opted for a tortoise, a heartbeat, and a hare to illustrate slow, medium, fast. It worked really well and looked cute!

The Final Product

The Future

We all had loads of fun working on this, and we'd like to keep it up! One of the things Hope started working on in the week was a way to add effects to the audio (like Reverb, for example) but we didn't have time to get this added. Hopefully, we can make some cool guitar-pedal-esque UI and get this hooked up :)
As UI Elements will eventually be usable for in-game UI, we'd like to make a small in-browser demo for this toy, too.



Wednesday 7 March 2018

Using Linq to count how many times an object occurs in a list


You have a list full of objects. Some of the objects are duplicates, and you want know know how many of each distinct item you have in the list.

I searched around for some answers, and I did find some StackOverflow answers that almost got me to the result, but it wasn’t quite right.

In my example, the list I want to sort is a list of custom type Run ie List<Run>(). In my code, Run is a struct that looks like:

public struct Run
{
     public string RunID;
     public string Config;
}

The StackOverflow responses were mostly long and hard-to-read Linq statements. I like Linq a lot, but it’s not always easy on the eyes, especially once you start adding extensions.

Instead, I came up with the following:

void ProcessList (List<Run> runConfigs)
{
     var grouping = runConfigs.GroupBy(o => o.Config);
     foreach (var configGroup in grouping)
     {
          string configName = configGroup.Key.ToString();   
          string count = configGroup.Count<Run>().ToString();
     }
}

Doing the above code allows me to use configName and count however I please (which in my case was outputting them to a csv).

Here’s the same example but for a List<string>():

var grouping = listOfStrings.GroupBy(x => x);
foreach (var stringGroup in grouping)
{
    string name = stringGroup.Key.ToString();
    string count = stringGroup.Count().ToString();
}

If you step through this code with the debugger, it will show that 'name' is one of the distinct values in the list, and 'count' is how many times that object appears in the list. These values can now be used however you like.

Thursday 1 March 2018

What is dynamic batching, and why is it off by default?

While reading this tutorial, I came to a section where it said something like “as you can see, dynamic batching has stopped working”. I did not see that. In the image in the tutorial “Saved by batching” on the stats panel had dropped to 0, but my stats panel still showed a number in the 1000s.

When I checked the player settings, I noticed dynamic batching was turned off. After a bit of investigation, I learned that dynamic batching is now off by default for 3d projects as of Unity 2018.1; but why is this useful?

A mesh is the part of a 3D object that describes its shape. When certain criteria are met, Unity is able to combine meshes from different GameObjects into one big mesh before they are drawn. This is called dynamic batching. The "dynamic" part means it happens while the game is running.

Sometimes (but not always!), it takes less time to draw one big mesh than it would to draw lots of smaller meshes. Dynamic batching occurs on the CPU, and drawing meshes occurs on the GPU. You can think of dynamic batching as spending CPU time to save GPU time. This means that it becomes more likely that the game becomes CPU-bound (meaning that the frame rate is lower than you want because the CPU is taking the most time), but you should ideally want as much as possible to happen on the GPU; that’s what it’s there for.

If your game is already CPU-bound, then dynamic batching will not help. But if your game is GPU-bound (meaning that the frame rate is lower than you want because the GPU is taking the most time), then it may help.

(FYI: The GPU equivalent of dynamic batching is GPU instancing.)

For these reasons, dynamic batching is desirable for old mobile devices that don’t have a GPU (or have a bad GPU), but it is not efficient for games intended for more modern devices.

Here is a helpful article that goes into a bit more detail: Why Are My Batches (Draw Calls) So High?
Also: https://docs.unity3d.com/Manual/DrawCallBatching.html