First, the webhooks API is completely undocumented. As such, I have no idea how to properly register a webhook via the API to ensure it listens to only the events I'm interested in, in the specific category or categories I'm interested in. As such, I had to choose not to allow registering new webhooks via Slack; we'll need to register hooks manually, mapping them to the appropriate room in the Slack.
Second, the Discourse API does not return sub-categories for its "categories" endpoint, making discovery of all categories pretty much impossible. This is particularly problematic as the webhooks provide category identifiers in their payloads, but no other category details. We could fetch this information on the fly (by introspecting the first topic returned for a given category endpoint, e.g., /c/{slug}.json), but since the HTTP client shipped with hubot is async, this leads to callback hell.
What I opted for was the following architecture:
discourse-categories contains and exports a hard-coded list of category information (ids, slugs, and names). This list won't change often, and we can update the bot when it does.
discourse-category contains logic to try and match a category identifier to that list; if it cannot, it uses a default ("Uncategorized").
discourse-verify-signature creates an hmac-sha256 signature of the incoming payload and compares it to the one sent via the X-Discourse-Event-Signature header.
discourse-topic responds to incoming topic events and conditionally emits a message to the room to which the webhook corresponds.
discourse-post responds to incoming post (comment) events and conditionally emits a message to the room to which the webhook corresponds.
The discourse script now registers the above two scripts as route handlers, placing the signature verification as middleware prior to both.
I used the slack-htmlify package to make the post (comment) text work within Slack (as it comes in as HTML).
Finally, I updated the nginx configuration to allow forwarding /discourse URIs to the bot to manage.
TODO
[x] Remove legacy discourse-listener
[x] Rebuild and tag each of the nginx and hubot containers
There were a couple big stumbling blocks.
First, the webhooks API is completely undocumented. As such, I have no idea how to properly register a webhook via the API to ensure it listens to only the events I'm interested in, in the specific category or categories I'm interested in. As such, I had to choose not to allow registering new webhooks via Slack; we'll need to register hooks manually, mapping them to the appropriate room in the Slack.
Second, the Discourse API does not return sub-categories for its "categories" endpoint, making discovery of all categories pretty much impossible. This is particularly problematic as the webhooks provide category identifiers in their payloads, but no other category details. We could fetch this information on the fly (by introspecting the first topic returned for a given category endpoint, e.g.,
/c/{slug}.json
), but since the HTTP client shipped with hubot is async, this leads to callback hell.What I opted for was the following architecture:
X-Discourse-Event-Signature
header.discourse
script now registers the above two scripts as route handlers, placing the signature verification as middleware prior to both.I used the slack-htmlify package to make the post (comment) text work within Slack (as it comes in as HTML).
Finally, I updated the nginx configuration to allow forwarding
/discourse
URIs to the bot to manage.TODO