├── .gitignore ├── example ├── .meteor │ ├── .gitignore │ ├── release │ ├── .finished-upgraders │ ├── packages │ ├── .id │ └── versions ├── packages │ └── percolate:intercom ├── example.css ├── example.html └── example.js ├── .npm └── package │ ├── .gitignore │ ├── README │ └── npm-shrinkwrap.json ├── check_hash └── check_hash.js ├── package.js ├── .versions ├── intercom_server.js ├── intercom_loader.js ├── intercom_client.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /example/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /example/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@0.9.3.1 2 | -------------------------------------------------------------------------------- /example/packages/percolate:intercom: -------------------------------------------------------------------------------- 1 | ../.. -------------------------------------------------------------------------------- /example/example.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | -------------------------------------------------------------------------------- /example/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | -------------------------------------------------------------------------------- /example/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | meteor-platform 7 | autopublish 8 | insecure 9 | percolate:intercom 10 | -------------------------------------------------------------------------------- /example/example.html: -------------------------------------------------------------------------------- 1 |
2 |You've pressed the button {{counter}} times.
14 | 15 | -------------------------------------------------------------------------------- /example/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 7eeob6115931q28tpbj 8 | -------------------------------------------------------------------------------- /.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /check_hash/check_hash.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | 3 | var userId = process.argv.pop(); 4 | var secretKey = process.argv.pop(); 5 | 6 | if (! secretKey || ! userId) { 7 | return console.log("USAGE: node check_hash.js SECRET USER_ID"); 8 | } 9 | 10 | console.log(secretKey, userId) 11 | 12 | var hash = crypto.createHmac('sha256', secretKey) 13 | .update(userId.toString()).digest('hex'); 14 | 15 | 16 | console.log('The intercom hash for userId ' + userId + ' is ' + hash); -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | if (Meteor.isClient) { 2 | // counter starts at 0 3 | Session.setDefault("counter", 0); 4 | 5 | Template.hello.helpers({ 6 | counter: function () { 7 | return Session.get("counter"); 8 | } 9 | }); 10 | 11 | Template.hello.events({ 12 | 'click button': function () { 13 | // increment the counter when button is clicked 14 | Session.set("counter", Session.get("counter") + 1); 15 | } 16 | }); 17 | } 18 | 19 | if (Meteor.isServer) { 20 | Meteor.startup(function () { 21 | // code to run on server at startup 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'verso:intercom', 3 | summary: "Intercom.io basic integration (uses 'widget' snippet)", 4 | version: '1.5.0', 5 | git: 'https://github.com/versolearning/meteor-intercom.git' 6 | }); 7 | 8 | Npm.depends({ 'intercom-client': '2.8.3' }); 9 | 10 | Package.onUse(function(api) { 11 | if (api.versionsFrom) 12 | api.versionsFrom('1.3'); 13 | 14 | api.use(['underscore', 'accounts-base', 'ecmascript']); 15 | api.use(['session', 'tracker'], 'client'); 16 | 17 | api.add_files('intercom_server.js', 'server'); 18 | api.add_files(['intercom_loader.js', 'intercom_client.js'], 'client'); 19 | 20 | api.export('IntercomSettings'); 21 | api.export('IntercomHash', 'server'); 22 | api.export('IntercomClient', 'server'); 23 | }); 24 | -------------------------------------------------------------------------------- /.versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.4 2 | allow-deny@1.0.2 3 | babel-compiler@6.5.2 4 | babel-runtime@0.1.6 5 | base64@1.0.6 6 | binary-heap@1.0.6 7 | blaze@2.1.5 8 | blaze-tools@1.0.6 9 | boilerplate-generator@1.0.6 10 | callback-hook@1.0.6 11 | check@1.1.2 12 | ddp@1.2.3 13 | ddp-client@1.2.3 14 | ddp-common@1.2.3 15 | ddp-rate-limiter@1.0.2 16 | ddp-server@1.2.4 17 | deps@1.0.10 18 | diff-sequence@1.0.3 19 | ecmascript@0.4.1 20 | ecmascript-runtime@0.2.8 21 | ejson@1.0.9 22 | geojson-utils@1.0.6 23 | html-tools@1.0.7 24 | htmljs@1.0.7 25 | id-map@1.0.5 26 | jquery@1.11.6 27 | localstorage@1.0.7 28 | logging@1.0.10 29 | meteor@1.1.12 30 | minimongo@1.0.12 31 | modules@0.5.1 32 | modules-runtime@0.6.1 33 | mongo@1.1.5 34 | mongo-id@1.0.2 35 | npm-mongo@1.4.41 36 | observe-sequence@1.0.9 37 | ordered-dict@1.0.5 38 | promise@0.6.5 39 | random@1.0.7 40 | rate-limit@1.0.2 41 | reactive-dict@1.1.5 42 | reactive-var@1.0.7 43 | retry@1.0.5 44 | routepolicy@1.0.8 45 | service-configuration@1.0.7 46 | session@1.1.3 47 | spacebars@1.0.9 48 | spacebars-compiler@1.0.9 49 | tracker@1.0.11 50 | ui@1.0.9 51 | underscore@1.0.6 52 | verso:intercom@1.5.0 53 | webapp@1.2.6 54 | webapp-hashing@1.0.7 55 | -------------------------------------------------------------------------------- /example/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.1.1 2 | application-configuration@1.0.2 3 | autopublish@1.0.0 4 | autoupdate@1.1.1 5 | base64@1.0.0 6 | binary-heap@1.0.0 7 | blaze-tools@1.0.0 8 | blaze@2.0.1 9 | boilerplate-generator@1.0.0 10 | callback-hook@1.0.0 11 | check@1.0.1 12 | ctl-helper@1.0.3 13 | ctl@1.0.1 14 | ddp@1.0.9 15 | deps@1.0.4 16 | ejson@1.0.3 17 | fastclick@1.0.0 18 | follower-livedata@1.0.1 19 | geojson-utils@1.0.0 20 | html-tools@1.0.1 21 | htmljs@1.0.1 22 | http@1.0.6 23 | id-map@1.0.0 24 | insecure@1.0.0 25 | jquery@1.0.0 26 | json@1.0.0 27 | livedata@1.0.10 28 | localstorage@1.0.0 29 | logging@1.0.3 30 | meteor-platform@1.1.1 31 | meteor@1.1.1 32 | minifiers@1.1.0 33 | minimongo@1.0.3 34 | mobile-status-bar@1.0.0 35 | mongo@1.0.6 36 | observe-sequence@1.0.2 37 | ordered-dict@1.0.0 38 | percolate:intercom@1.1.1 39 | random@1.0.0 40 | reactive-dict@1.0.3 41 | reactive-var@1.0.2 42 | reload@1.1.0 43 | retry@1.0.0 44 | routepolicy@1.0.1 45 | service-configuration@1.0.1 46 | session@1.0.2 47 | spacebars-compiler@1.0.2 48 | spacebars@1.0.2 49 | templating@1.0.7 50 | tracker@1.0.2 51 | ui@1.0.3 52 | underscore@1.0.0 53 | url@1.0.0 54 | webapp-hashing@1.0.0 55 | webapp@1.1.2 56 | -------------------------------------------------------------------------------- /intercom_server.js: -------------------------------------------------------------------------------- 1 | // Generate an intercomHash for secure mode as outlined at 2 | // https://segment.io/docs/integrations/intercom/#secure-mode 3 | var crypto = Npm.require('crypto'); 4 | var Intercom = Npm.require('intercom-client'); 5 | 6 | IntercomClient = function IntercomClient() { 7 | //support for Personal Access token: 8 | const pat = Meteor._get(Meteor, 'settings', 'intercom', 'personalAccessToken'); 9 | 10 | //api 11 | const id = Meteor._get(Meteor, 'settings', 'public', 'intercom', 'id'); 12 | const apikey = Meteor._get(Meteor, 'settings', 'intercom', 'apikey'); 13 | 14 | //priority 15 | if (pat) { 16 | return new Intercom.Client({ token: pat }); 17 | } 18 | 19 | if (id && apikey) { 20 | return new Intercom.Client(id, apikey); 21 | } 22 | 23 | return console.warn(` 24 | You must set Meteor.settings.intercom.personalAccessToken 25 | or add Meteor.settings.public.intercom.id 26 | and Meteor.settings.intercom.apikey to use IntercomClient 27 | `); 28 | }; 29 | 30 | // returns undefined if there is no secret 31 | IntercomHash = function(userId) { 32 | var secret = Meteor.settings && 33 | Meteor.settings.intercom && Meteor.settings.intercom.secret; 34 | 35 | if (secret) { 36 | return crypto.createHmac('sha256', new Buffer(secret, 'utf8')) 37 | .update(userId).digest('hex'); 38 | } 39 | }; 40 | 41 | Meteor.publish('currentUserIntercomHash', function() { 42 | if (this.userId) { 43 | var intercomHash = IntercomHash(this.userId); 44 | 45 | if (intercomHash) 46 | this.added("users", this.userId, {intercomHash: intercomHash}); 47 | } 48 | this.ready(); 49 | }); 50 | -------------------------------------------------------------------------------- /intercom_loader.js: -------------------------------------------------------------------------------- 1 | Meteor.startup(function() { 2 | // If the app id is set, we should use the new url for the widget otherwise 3 | // fall back to the old url but apparantly in-app messaging will no longer 4 | // work 5 | var widgetUrl; 6 | var minimumUserInfo = IntercomSettings.minimumUserInfo(); 7 | var appId = minimumUserInfo && minimumUserInfo.app_id; 8 | 9 | if (appId) 10 | widgetUrl = 'https://widget.intercom.io/widget/' + appId; 11 | else 12 | widgetUrl = 'https://static.intercomcdn.com/intercom.v1.js'; 13 | 14 | // ------ The snippet below is almost identical to the official one however 15 | // ------ we substitute in widgetUrl instead of a statically defined one. 16 | // ------ In addition, it has been found that the window.onload event is unreliable 17 | // ------ in some browsers (specifically on mobile), where it may never fire. 18 | // ------ We have replaced waiting on window.onload with simply executing the script loader. 19 | // ------ As we are inside of Meteor.startup, this should be ok. 20 | 21 | (function(){ 22 | var w=window; 23 | var ic=w.Intercom; 24 | if (typeof ic==="function") { 25 | ic('reattach_activator'); 26 | ic('update',intercomSettings); 27 | } else { 28 | var d=document; 29 | var i=function(){ 30 | i.c(arguments)}; 31 | i.q=[]; 32 | i.c=function(args) { 33 | i.q.push(args) 34 | }; 35 | w.Intercom=i; 36 | function l() { 37 | var s=d.createElement('script'); 38 | s.type='text/javascript'; 39 | s.async=true; 40 | s.src=widgetUrl; 41 | var x=d.getElementsByTagName('script')[0]; 42 | x.parentNode.insertBefore(s,x); 43 | } 44 | l(); 45 | } 46 | })(); 47 | // ------ 48 | }); 49 | -------------------------------------------------------------------------------- /intercom_client.js: -------------------------------------------------------------------------------- 1 | // We *must* have the intercom id set otherwise the intercom loader script throws 2 | // exceptions. Warn people about this. 3 | Meteor.startup(function() { 4 | if (! Meteor._get(Meteor, 'settings', 'public', 'intercom', 'id')) { 5 | console.warn("You must set Meteor.settings.public.intercom.id to use percolate:intercom"); 6 | } 7 | }); 8 | 9 | Meteor.subscribe('currentUserIntercomHash'); 10 | 11 | var minimumUserInfo = function(user) { 12 | var info = { 13 | app_id: Meteor._get(Meteor, 'settings', 'public', 'intercom', 'id') 14 | }; 15 | 16 | if (user) 17 | _.extend(info, { 18 | user_id: user._id, 19 | created_at: Math.round(user.createdAt/1000), 20 | user_hash: user.intercomHash 21 | }); 22 | 23 | return info; 24 | }; 25 | 26 | IntercomSettings = { 27 | // if you want to manually call it 28 | minimumUserInfo: minimumUserInfo 29 | }; 30 | 31 | var booted = false; 32 | 33 | // send data to intercom 34 | Meteor.startup(function() { 35 | Tracker.autorun(function() { 36 | var user = Meteor.user(); 37 | 38 | var info = IntercomSettings.minimumUserInfo(user); 39 | var ready; 40 | 41 | if (!user) { 42 | if (Meteor._get(Meteor, 'settings', 'public', 'intercom', 'allowAnonymous') === true) { 43 | if (IntercomSettings.anonymousInfo) { 44 | ready = IntercomSettings.anonymousInfo(info); 45 | if (ready === false) 46 | return; 47 | } 48 | } else { 49 | // console.log('shutdown') 50 | booted = false; 51 | return Intercom('shutdown'); 52 | } 53 | } else { 54 | if (IntercomSettings.userInfo) { 55 | ready = IntercomSettings.userInfo(user, info); 56 | if (ready === false) 57 | return; 58 | } 59 | } 60 | 61 | if (info) { 62 | var type = booted ? 'update' : 'boot'; 63 | 64 | // console.log(type, info) 65 | Intercom(type, info); 66 | booted = true; 67 | } 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meteor Intercom 2 | 3 | A package to use [Intercom.io](http://intercom.io) easily with Meteor. 4 | 5 | NOTE: Package updates are released under `verso:intercom` as it replaces `percolate:intercom`. 6 | 7 | ## Installation 8 | 9 | Intercom can be installed with Meteor's package manager: 10 | 11 | ``` sh 12 | $ meteor add verso:intercom 13 | ``` 14 | 15 | ## API 16 | 17 | Ensure you have `Meteor.settings.intercom.secret` and `Meteor.settings.public.intercom.id` defined to the values provided to you by Intercom. *API Keys are being deprecated*. You can define the `Meteor.settings.intercom.personalAccessToken` if you intend to use Intercom on the server side, otherwise you can still define `Meteor.settings.intercom.apikey`. 18 | 19 | Some Intercom subscription packages allow for anonymous users. To enable the use of anonymous users, set `Meteor.settings.public.intercom.allowAnonymous` to `true`. 20 | 21 | By default, the package will send up the user's id, creation date, and hash (if defined, see below). To send custom data, set: 22 | 23 | ```js 24 | IntercomSettings.userInfo = function(user, info) { 25 | // add properties to the info object, for instance: 26 | if (user.services.google) { 27 | info.email = user.services.google.email; 28 | info['name'] = user.services.google.given_name + ' ' + user.services.google.family_name; 29 | } 30 | } 31 | ``` 32 | 33 | `email` and `name` are preset fields provided by Intercom. You may add any additional custom user attribute. 34 | 35 | If you need to wait on a user subscription for e.g. the hash to come down, you can return `false` in your `userInfo` function to tell the package to wait. 36 | 37 | ```js 38 | IntercomSettings.userInfo = function (user, info) { 39 | if (!user.intercomHash) { 40 | return false; // the hash isn't loaded to the users collection yet. come back later. 41 | } 42 | // ... 43 | } 44 | ``` 45 | 46 | If you want to add fields sent to intercom for an anonymous user, you can add them through in the `anonymousInfo` function. 47 | 48 | ```js 49 | IntercomSettings.anonymousInfo = function ( info ) { 50 | // check all router subscriptions have loaded 51 | if (!Router.current() || !Router.current().ready()) 52 | return false; 53 | 54 | info.anonymous = true; 55 | return; 56 | }; 57 | ``` 58 | 59 | On the server, you can also interact with the Intercom API via the official [intercom-node client](https://github.com/intercom/intercom-node). 60 | ```js 61 | const client = IntercomClient(); 62 | 63 | // Create an event for a user 64 | client.events.create({ 65 | event_name: 'Test event', 66 | created_at: Math.floor(new Date().getTime() / 1000), 67 | user_id: '