Customize and Extend Komodo with Userscripts (Slack Edition)

Slack Userscript

Morning (or afternoon) all. Not too long ago I wrote an uncharacteristically long blog post about the Slack integration I had written for the Komodo 10.2.0 release. As I was writing the blog, and doing a bit of backtracking of the work I had done, I ended up finding the four userscripts I had written that ultimately became the full fledged feature, Slack Sharing in Komodo.

Today I'll walk through creating one of these userscripts to demonstrate how customizable Komodo is. With userscripts, you can add powerful functionality to Komodo to make it do whatever you need for developing your web apps (in our case, pushing a file snippet to a Slack channel!).

Customize & Extend Komodo

As many of you may know, and many more may not, Komodo is extra-emely customizable. Between the Addon mechanic, Javascript and Python userscripts, and the Color Scheme Editor introduced in Komodo 10, Komodo is likely one of the most extensible IDEs available. Obviously I'm biased...

Throughout this blog there will be snippets of code that you can run for yourself to see what they do. Just open the JS Console and paste the commands there: View menu > Tabs & Sidebars > Console.

You can also create a new userscript in your toolbox and follow along there: View menu > Tabs & Sidebars > Toolbox: Gear icon > New Userscript.

You can either edit the userscript in the properties dialog that appears or save and close the dialog then right click the userscript in the toolbox and Edit Userscript which will open it in an editor view. Note: for the Slack code to work, you will have to create an App for your Slack team and return the authentication key provided for API calls. Check out their docs for more information

For the rest of the blog, I'm going to focus on a subset of the above mentioned areas of customization and further, a subset of the code and tools available therein. I'm talking of course about userscripts and the Komodo CommonJS style SDK.

What I used

For this userscript I used 4 different SDKs, one of which is provided by the Mozilla code Komodo is built on top of.

The Userscript

I'm going to go over the userscript as if I'm just writing it. I'll go section by section then post the whole script at the bottom. As we go you can use console.log() to output things in the JS console if you want to have a better understanding of what's going on. Remember, console.log(object) will dump the object to the console so you can click through it and see what it is.

Authentication

Instantiate your API key.

token = "xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXX";

As mention above you will need to get your own API key to run this script. See https://api.slack.com/ for more information.

Required SDKs

Load our SDKs. As you can see it's as easy as writing NodeJS.

var ajax = require("ko/ajax"); // To make API calls to Slack
var view = require("ko/views").current().get(); // To get meta information about the file
var editor = require("ko/editor"); // To get information about the contents of the file
var qString = require("sdk/querystring"); // create an query string for our API call

Content to Send

The most logical thing to me was to get either the selection or the entire file if there was no selection. We also want a file name to identify the code and the programming language of the file so Slack knows how to style the code when it posts the snippet to your channel.

var content;
var filename;
if (editor.getSelection.length === 0) {
    // If we have nothing selected then get the entire contents of the file
    content = view.scimoz.text; // Everything in the file
    filename = view.title; // The name of the file including extension
} else {
    // otherwise, lets get the selection and indicate in the name that it's
    // part of a larger file.
    content = editor.getSelection(); // Gives us the selection
    var viewTitlesplit = view.title.split("."); // view.title gives us the filename
    var name = viewTitlesplit.shift() + "-snippet";
    var extension = viewTitlesplit.toString();
    filename = [name,extension].join(".");
}
var language = editor.getLanguage() || "text";

Try printing the results of the above with and without a selection in your open file: console.log("content:"+content+" Filename: "+filename)

Create the API Call String

Now that we have what we want to send, we can create the query string using the stringify function of the querystring SDK then append it to the API method URL.

var params = qString.stringify(
{
    token: token, // I hope you have this by now ;)
    content: content, 
    filetype: language,
    filename: filename,
    channels: "MyChannel", // I hope you know what channel you want cause I can't see the future nor read your mind :P
    // but that's a little out of scope here.
});
var baseUrl = "https://slack.com/api/files.upload?"; // https://api.slack.com/methods/files.upload
var request = baseUrl+params; // Append the query string to the URL

Make the API Call

And we're basically done.

Using the AJAX SDK in Komodo makes this last step a breeze.

ajax.post(request,(code, response) =>
{
    var  response = JSON.parse(response);
    console.log("code: "+code+"
ok: "+response.ok+"
File: "+JSON.stringify(response.file));
});

After a brief delay, that will dump the server code (was the API server able to handle your query?) and the status from the Slack API (Did the query make sense to the API code?).

The Final Product

And that's it! Your userscript should be working and should look like this:

token = "xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXX";  //Replace this line with your own API key
var ajax = require("ko/ajax"); // To make API calls to Slack
var view = require("ko/views").current().get(); // To get meta information about the file
var editor = require("ko/editor"); // To get information about the contents of the file
var qString = require("sdk/querystring"); // create an query string for our API call
var content;
var filename;
if (editor.getSelection.length === 0) {
    // If we have nothing selected then get the entire contents of the file
    content = view.scimoz.text; // Everything in the file
    filename = view.title; // The name of the file including extension
} else {
    // otherwise, lets get the selection and indicate in the name that it's
    // part of a larger file.
    content = editor.getSelection(); // Gives us the selection
    var viewTitlesplit = view.title.split("."); // view.title gives us the filename
    var name = viewTitlesplit.shift() + "-snippet";
    var extension = viewTitlesplit.toString();
    filename = [name,extension].join(".");
}
var language = editor.getLanguage() || "text";
var params = qString.stringify(
{
    token: token, // I hope you have this by now ;)
    content: content, 
    filetype: language,
    filename: filename,
    channels: "MyChannel", // Replace this line with your channel. 
// but that's a little out of scope here. }); var baseUrl = "https://slack.com/api/files.upload?"; // https://api.slack.com/methods/files.upload var request = baseUrl+params; // Append the query string to the URL

ajax.post(request,(code, response) => { console.log("code: "+code+" Response: "+response); });

Troubleshooting

There were a couple places above that you have to enter information specific to your Slack team and channel. These are the errors you'll get if you didn't do that.

not_authed

code: 200 Response: {"ok":false,"error":"not_authed"}

If you get a response from the API server that looks like the above then you didn't get an API key. Again, see the Slack Docs for this information.

invalid_channel

code: 200 Response: {"ok":false,"error":"invalid_channel","channel":"MyChannel"}

This means you didn't add a real channel to the request we created in the Create the API Call String section.

Final Words

I say this all the time to people when I’m talking about my job, but I’m insanely lucky to be where I am. Komodo is such a huge beast of a program (if you don’t think it’s huge then we’re doing our job well) that I’m constantly learning new things. This was one of those cases where I got to learn something new and really cool. I’m really glad that I could write this blog and share what I learned with you.

Now take what we’ve covered and make something cool! That’s what Komodo IDE is for! Enjoy!

PS. Licensing

Just a heads up to anyone reading this and sampling the code above: all code in this blog is licensed under the MIT license so feel free to use it how you like.