Wiki
Clone wikiac-koa-hipchat / FAQ
FAQ
How do I run my add-on on Heroku?
See Paas Deployment.
How can I add analytics to my add-on?
Try ac-koa-hipchat-keenio -- simple Keen.io analytics integration for ac-koa-hipchat
based add-on projects. If you're using Heroku to host your add-ons, integration with the Keen.io add-on for Heroku is particularly easy.
How can I easily render Handlebar templates?
Take a look at the ac-koa-hipchat-notifier library.
How do I send room notifications from a third party webhook?
// This is a simplified example; to see a fully functional example add-on that achieves this, see: // https://bitbucket.org/atlassianlabs/ac-koa-hipchat-mailroom/src/master/lib/mailroom.js var MemoryStore = require('ac-node').MemoryStore; var RestClient = require('ac-node-hipchat').RestClient; // Share this with all rest clients in your app to avoid generating tokens for every notification var tokenCache = MemoryStore(); // This assumes that you have a 'tenant' object somewhere equivalent to this.tenant in a normal HC webhook callback; // see the following for an example of how to save the tenant object at installation time: // https://bitbucket.org/atlassianlabs/ac-koa-hipchat-mailroom/src/master/web.js#cl-35 var tenantClient = RestClient().forTenant(tenant, tokenCache, ['send_notification']); // You need a room id to send a notification with the tenant client. If the add-on is installed in a room, // you can use `tenant.room`, otherwise you need to determine the target room id somehow here as well... yield tenantClient.sendNotification(tenant.room, 'Hello!');
I'm new to ES6 generators -- how do I combine them with vanilla JavaScript callbacks?
#!js // How do I send a notification to a room when not in inside a generator function? For example... addon.webhook('room_message', /^\/sup$/, function *() { var query = { 'q' : '#Atlassian', 'result_type' : 'recent', 'count' : 1 }; twitter.search(query, twitter.apiToken, twitter.apiTokenSecret, function(error, jsonObject, response, url) { if (error) { console.log('Error: ' + JSON.stringify(error)); } else { var statuses = jsonObject['statuses']; var status = statuses[0]; var username = status['user']['name']; var text = status['text']; console.log(username + ' said: ' + text); // TODO: Now, how do I send to the room? 'yield' does not work in a non-generator // yield this.roomClient.sendNotification('Looking...'); } }); yield this.roomClient.sendNotification('Looking...'); }); // Solution #1a: Don't worry about suspending the current function, since you probably don't really // care if the send succeeded or not. addon.webhook('room_message', /^\/sup$/, function *() { var query = { 'q' : '#Atlassian', 'result_type' : 'recent', 'count' : 1 }; twitter.search(query, twitter.apiToken, twitter.apiTokenSecret, function(error, jsonObject, response, url) { if (error) { console.log('Error: ' + JSON.stringify(error)); } else { var statuses = jsonObject['statuses']; var status = statuses[0]; var username = status['user']['name']; var text = status['text']; // Note that the lack of a yield (or anything else) simply fires off the notification, but // doesn't bother to check the result. If you care, see solution #1b. this.roomClient.sendNotification(username + ' said: ' + text); } // Note the added 'bind' here propagates the original 'this' to the callback. You could instead capture // the 'this' in the enclosing generator in a variable such as self, then use that inside the callback -- // which you use is largely a matter of style and circumstance. }.bind(this)); yield this.roomClient.sendNotification('Looking...'); }); // Solution #1b: But what if I want to know if it succeeded or not? Maybe I needed a result from // another roomClient or tenantClient call? addon.webhook('room_message', /^\/sup$/, function *() { var query = { 'q' : '#Atlassian', 'result_type' : 'recent', 'count' : 1 }; // Demonstrating the style of capturing the 'this' ref rather than binding the callback. var self = this; twitter.search(query, twitter.apiToken, twitter.apiTokenSecret, function(error, jsonObject, response, url) { if (error) { console.log('Error: ' + JSON.stringify(error)); } else { var statuses = jsonObject['statuses']; var status = statuses[0]; var username = status['user']['name']; var text = status['text']; // The roomClient and tenantClient APIs return promises. When in a Koa generator, you can conveniently // yield on promises (among other things). When in a normal function, you can use Promises/A+-compatible // callbacks. (Actually, the underlying ac-node-hipchat module uses RSVP for promises, which provides // a richer API than pure Promises/A+.) self.roomClient.sendNotification(username + ' said: ' + text).then(function () { console.log('Woot!'); }, function (err) { console.error('Doh!', err.stack || err); }); } }); yield this.roomClient.sendNotification('Looking...'); }); // Solution #2a: Ok, but solutions #1a and #1b lose the power of generators by resorting to normal // callbacks. That sucks; can we do anything to stay in generator-land? Well, yes, there are several // viable approaches as it turns out. This case is ripe for the use of a thunk. var thunkify = require('thunkify'); // https://github.com/tj/node-thunkify var search = thunkify(twitter.search); addon.webhook('room_message', /^\/sup$/, function *() { var query = { 'q' : '#Atlassian', 'result_type' : 'recent', 'count' : 1 }; yield this.roomClient.sendNotification('Looking...'); var jsonObject = yield search(query); var statuses = jsonObject['statuses']; var status = statuses[0]; var username = status['user']['name']; var text = status['text']; yield this.roomClient.sendNotification(username + ' said: ' + text); }); // Solution #2b: Ok, #2a was tight... but what if I need to use an API with callbacks that don't // follow the typical Node.js callback pattern that thunkify can work with? // You can resort to using promises again, but this time, manually create your own adapter. var Promise = require('rsvp').Promise; function search(query) { return new Promise(function (resolve, reject) { twitter.search(query, twitter.apiToken, twitter.apiTokenSecret, function(error, jsonObject, response, url) { if (error) { return reject(error); } resolve(jsonObject, response, url); }); }); } // Then use the custom adapter the same way we used the thunk in #2a. addon.webhook('room_message', /^\/sup$/, function *() { var query = { 'q' : '#Atlassian', 'result_type' : 'recent', 'count' : 1 }; yield this.roomClient.sendNotification('Looking...'); var jsonObject = yield search(query); var statuses = jsonObject['statuses']; var status = statuses[0]; var username = status['user']['name']; var text = status['text']; yield this.roomClient.sendNotification(username + ' said: ' + text); }); // There are other solutions as well, but this should be enough tools to satisfy most needs.
Updated