UNPKG

3.3 kBJavaScriptView Raw
1var check = require('check-types');
2var verify = check.verify;
3var urls = require('url');
4var crypto = require('crypto');
5var _ = require('lodash');
6var param = encodeURIComponent;
7
8var events = [
9 'room_enter',
10 'room_exit',
11 'room_message',
12 'room_notification',
13 'room_topic_change'
14];
15
16var self = exports;
17
18exports.normalize = function (definition, baseUrl, defaultMountPath, token) {
19 check.map(definition, {
20 event: verify.string,
21 name: verify.maybe.string,
22 url: verify.maybe.string
23 });
24 verify.webUrl(baseUrl);
25 if (events.indexOf(definition.event) === -1) {
26 throw new Error('Unrecognized webhook event: ' + definition.event);
27 }
28 definition = _.extend({}, definition);
29 if (definition.event === 'room_message') {
30 verify.defined(definition.pattern)
31 if (_.isRegExp(definition.pattern)) {
32 definition.pattern = exports.regExpToPattern(definition.pattern);
33 } else {
34 verify.string(definition.pattern);
35 }
36 }
37 // parse salient path info from the url
38 if (definition.url) {
39 definition.url = exports.stripNameParam(definition.url);
40 }
41 var parsed = self.parseUrl(definition.url);
42 // re-assemble the webhook url with the base url, as a sanity check
43 var fullPath = parsed ? parsed.fullPath : defaultMountPath;
44 definition.url = urls.resolve(baseUrl, fullPath);
45 if (token) {
46 definition.url += '?token=' + param(token);
47 }
48 if (!definition.name) {
49 definition.name = exports.digest(definition);
50 }
51 definition.url += (definition.url.indexOf('?') > 0 ? '&' : '?') + 'name=' + param(definition.name);
52 return definition;
53};
54
55exports.parseUrl = function (url) {
56 var result;
57 if (check.webUrl(url)) {
58 // full urls
59 parsed = urls.parse(url);
60 result = {
61 fullPath: parsed.path,
62 mountPath: parsed.pathname
63 };
64 } else if (check.string(url)) {
65 // path-only shorthand
66 result = {
67 fullPath: url,
68 mountPath: url.split('?')[0]
69 };
70 }
71 return result;
72};
73
74exports.regExpToPattern = function (re) {
75 var pattern = re.toString();
76 var match = /^\/(.*)\/([gimy]+)?/.exec(pattern);
77 if (match) {
78 pattern = match[1];
79 if (match[2]) {
80 pattern = '(?' + match[2] + ')' + pattern;
81 }
82 }
83 return pattern;
84};
85
86exports.patternToRegExp = function (pattern) {
87 var match = /\(\?([gimy])+\)(.*)/.exec(pattern);
88 var flags;
89 if (match) {
90 flags = match[1];
91 pattern = match[2];
92 }
93 return new RegExp(pattern, flags);
94};
95
96exports.digest = function (webhook) {
97 webhook = _.extend({}, webhook);
98 if (webhook.url) {
99 webhook.url = exports.stripNameParam(webhook.url);
100 }
101 // url encode other webhook params to generate a stable hash as the name that
102 // will change if any of the fields change
103 var fields = ['event', 'url'];
104 if (webhook.event === 'room_message') {
105 verify.defined(webhook.pattern)
106 fields.push('pattern');
107 }
108 var str = fields.filter(function (key) {
109 return !!webhook[key];
110 }).map(function (key) {
111 return key + '=' + encodeURIComponent(webhook[key].toString());
112 }).join('&');
113 return crypto.createHash('sha1').update(str).digest('hex');
114};
115
116exports.stripNameParam = function (url) {
117 return url.replace(/(.*)(\?|&)name=[^&]+&?(.*)/, function ($0, $1, $2, $3) {
118 return $1 + ($3 ? $2 : '') + ($3 || '');
119 });
120};