From 88b44390bf471a8aa7dda70f73296cf4a8997bac Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Tue, 17 Sep 2019 01:14:29 -0700 Subject: [PATCH 1/7] Desktop Analytics --- js/background.js | 2 ++ js/modules/loki_message_api.js | 6 ++++++ js/modules/loki_mixpanel.js | 17 +++++++++++++++++ js/modules/loki_public_chat_api.js | 7 +++++++ js/modules/loki_snode_api.js | 5 +++++ js/views/inbox_view.js | 9 +++++++++ libtextsecure/account_manager.js | 4 ++++ package.json | 1 + preload.js | 2 ++ ts/components/SearchResults.tsx | 4 +++- yarn.lock | 9 ++++++++- 11 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 js/modules/loki_mixpanel.js diff --git a/js/background.js b/js/background.js index 5e656247c..1c58b43c1 100644 --- a/js/background.js +++ b/js/background.js @@ -230,6 +230,8 @@ window.feeds = []; window.lokiMessageAPI = new window.LokiMessageAPI(ourKey); window.lokiPublicChatAPI = new window.LokiPublicChatAPI(ourKey); + window.mixpanel = new window.LokiMixpanelAPI(); + // window.mixpanel.track("Desktop boot"); window.lokiP2pAPI = new window.LokiP2pAPI(ourKey); window.lokiP2pAPI.on('pingContact', pubKey => { const isPing = true; diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index f9c3610d6..80d403dbf 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -4,6 +4,9 @@ const _ = require('lodash'); const { rpc } = require('./loki_rpc'); +const LokiMixpanelAPI = require('./loki_mixpanel.js'); + +const Mixpanel = new LokiMixpanelAPI(); const DEFAULT_CONNECTIONS = 3; const MAX_ACCEPTABLE_FAILURES = 1; @@ -178,6 +181,7 @@ class LokiMessageAPI { try { // eslint-disable-next-line more/no-then success = await firstTrue(promises); + Mixpanel.track('Sent Message Using Swarm API'); } catch (e) { if (e instanceof textsecure.WrongDifficultyError) { // Force nonce recalculation @@ -191,6 +195,7 @@ class LokiMessageAPI { throw e; } if (!success) { + Mixpanel.track('Failed to Send Message Using Swarm API'); throw new window.textsecure.EmptySwarmError( pubKey, 'Ran out of swarm nodes to query' @@ -255,6 +260,7 @@ class LokiMessageAPI { } catch (e) { log.warn('Loki send message:', e); if (e instanceof textsecure.WrongSwarmError) { + Mixpanel.track('Migrated Snode'); const { newSwarm } = e; await lokiSnodeAPI.updateSwarmNodes(params.pubKey, newSwarm); this.sendingData[params.timestamp].swarm = newSwarm; diff --git a/js/modules/loki_mixpanel.js b/js/modules/loki_mixpanel.js new file mode 100644 index 000000000..681452982 --- /dev/null +++ b/js/modules/loki_mixpanel.js @@ -0,0 +1,17 @@ +/* eslint-disable class-methods-use-this */ + +const EventEmitter = require('events'); +const Mixpanel = require('mixpanel'); +// require('setimmediate'); + +class LokiMixpanelAPI extends EventEmitter { + constructor() { + super(); + this.mixpanel = Mixpanel.init('736cd9a854a157591153efacd1164e9a'); + } + track(label) { + this.mixpanel.track(label); + } +} + +module.exports = LokiMixpanelAPI; diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index 4823b0c1d..b6738dfe7 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -3,6 +3,9 @@ clearTimeout, MessageController */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); +const LokiMixpanelAPI = require('./loki_mixpanel.js'); + +const Mixpanel = new LokiMixpanelAPI(); // Can't be less than 1200 if we have unauth'd requests const PUBLICCHAT_MSG_POLL_EVERY = 1.5 * 1000; // 1.5s @@ -663,8 +666,12 @@ class LokiPublicChannelAPI { objBody: payload, }); if (!res.err && res.response) { + Mixpanel.track('Public Message Sent'); return res.response.data.id; } + // there's no retry on desktop + // this is supposed to be after retries + Mixpanel.track('Failed to Send Public Message'); return false; } } diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index 6483727d7..c94598316 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -6,6 +6,9 @@ const dns = require('dns'); const process = require('process'); const { rpc } = require('./loki_rpc'); const natUpnp = require('nat-upnp'); +const LokiMixpanelAPI = require('./loki_mixpanel.js'); + +const Mixpanel = new LokiMixpanelAPI(); const resolve4 = url => new Promise((resolve, reject) => { @@ -118,6 +121,7 @@ class LokiSnodeAPI { port: snode.storage_port, })); } catch (e) { + Mixpanel.track('Seed Node Failed'); if (seedNodes.length === 0) { throw new window.textsecure.SeedNodeError( 'Failed to contact seed node' @@ -133,6 +137,7 @@ class LokiSnodeAPI { const filteredNodes = swarmNodes.filter( node => node.address !== nodeUrl && node.ip !== nodeUrl ); + Mixpanel.track('Unreachable Snode'); await conversation.updateSwarmNodes(filteredNodes); } diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 16c613c8a..04c24c9d1 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -302,6 +302,15 @@ } if (conversation) { + if (conversation.isRss()) { + window.mixpanel.track('RSS Feed Opened'); + } + if (conversation.isPublic()) { + window.mixpanel.track('Loki Public Chat Opened'); + } + if (conversation.isPrivate()) { + window.mixpanel.track('Conversation Opened'); + } conversation.updateProfileName(); } diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 0d64a2a6a..bd7843922 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -27,6 +27,8 @@ function AccountManager(username, password) { // this.server = window.WebAPI.connect({ username, password }); this.pending = Promise.resolve(); + // set up mixpanel + window.mixpanel = new window.LokiMixpanelAPI(); } function getNumber(numberId) { @@ -136,8 +138,10 @@ ).toArrayBuffer(); return libsignal.Curve.async.createKeyPair(privKey); }; + window.mixpanel.track('Seed Restored'); } else { generateKeypair = libsignal.KeyHelper.generateIdentityKeyPair; + window.mixpanel.track('Seed Created'); } return this.queueTask(() => generateKeypair().then(async identityKeyPair => diff --git a/package.json b/package.json index 11fdafdf4..ae2b283c9 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "libsodium-wrappers": "^0.7.4", "linkify-it": "2.0.3", "lodash": "4.17.11", + "mixpanel": "^0.10.2", "mkdirp": "0.5.1", "moment": "2.21.0", "mustache": "2.3.0", diff --git a/preload.js b/preload.js index fc5b12ca0..fca170532 100644 --- a/preload.js +++ b/preload.js @@ -328,6 +328,8 @@ window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiRssAPI = require('./js/modules/loki_rss_api'); +window.LokiMixpanelAPI = require('./js/modules/loki_mixpanel.js'); + window.LocalLokiServer = require('./libloki/modules/local_loki_server'); window.localServerPort = config.localServerPort; diff --git a/ts/components/SearchResults.tsx b/ts/components/SearchResults.tsx index ae5d4b6b5..8ebb25274 100644 --- a/ts/components/SearchResults.tsx +++ b/ts/components/SearchResults.tsx @@ -11,6 +11,8 @@ import { StartNewConversation } from './StartNewConversation'; import { LocalizerType } from '../types/Util'; +declare var mixpanel: any; + export type PropsData = { contacts: Array; friends: Array; @@ -36,7 +38,7 @@ type Props = PropsData & PropsHousekeeping; export class SearchResults extends React.Component { public handleStartNewConversation = () => { const { regionCode, searchTerm, startNewConversation } = this.props; - + mixpanel.track('New Conversation Started'); startNewConversation(searchTerm, { regionCode }); }; diff --git a/yarn.lock b/yarn.lock index 72d1e6646..f81384a2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4466,7 +4466,7 @@ https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" -https-proxy-agent@^2.2.1: +https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== @@ -6022,6 +6022,13 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mixpanel@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.10.2.tgz#10ff6cd76034b262d469094ad3d8c99039345376" + integrity sha512-+zbBQGd/Q5LLRooqJ2iyEDzKz2/ly4TipH5tE9te0BDMJpROxUMGffPulyHbh4FtMcbJuPmIUSIfy//JhhnlnA== + dependencies: + https-proxy-agent "2.2.1" + mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" From c921d3e3b3abf085ceb99b9ed4aec11510d0a373 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Wed, 18 Sep 2019 16:12:16 -0700 Subject: [PATCH 2/7] allow mixpanel to be already set up elsewhere --- js/background.js | 3 ++- libtextsecure/account_manager.js | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/js/background.js b/js/background.js index 1c58b43c1..04be3a822 100644 --- a/js/background.js +++ b/js/background.js @@ -230,7 +230,8 @@ window.feeds = []; window.lokiMessageAPI = new window.LokiMessageAPI(ourKey); window.lokiPublicChatAPI = new window.LokiPublicChatAPI(ourKey); - window.mixpanel = new window.LokiMixpanelAPI(); + // set up mixpanel + window.mixpanel = window.mixpanel || new window.LokiMixpanelAPI(); // incase account manager already set it up // window.mixpanel.track("Desktop boot"); window.lokiP2pAPI = new window.LokiP2pAPI(ourKey); window.lokiP2pAPI.on('pingContact', pubKey => { diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index bd7843922..e12f5db41 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -22,13 +22,14 @@ (function() { window.textsecure = window.textsecure || {}; + // set up mixpanel + window.mixpanel = window.mixpanel || new window.LokiMixpanelAPI(); + const ARCHIVE_AGE = 7 * 24 * 60 * 60 * 1000; function AccountManager(username, password) { // this.server = window.WebAPI.connect({ username, password }); this.pending = Promise.resolve(); - // set up mixpanel - window.mixpanel = new window.LokiMixpanelAPI(); } function getNumber(numberId) { From 51eaa9082320b79b8cd64e1fb6ab8eafcba9d0f2 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 19 Sep 2019 19:57:26 -0700 Subject: [PATCH 3/7] use window.mixpanel, remove eventEmitter --- js/background.js | 3 +-- js/modules/loki_message_api.js | 9 +++------ js/modules/loki_mixpanel.js | 6 +----- js/modules/loki_public_chat_api.js | 7 ++----- js/modules/loki_snode_api.js | 7 ++----- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/js/background.js b/js/background.js index 04be3a822..6adbc9420 100644 --- a/js/background.js +++ b/js/background.js @@ -230,8 +230,7 @@ window.feeds = []; window.lokiMessageAPI = new window.LokiMessageAPI(ourKey); window.lokiPublicChatAPI = new window.LokiPublicChatAPI(ourKey); - // set up mixpanel - window.mixpanel = window.mixpanel || new window.LokiMixpanelAPI(); // incase account manager already set it up + // are there limits on tracking, is this unneeded? // window.mixpanel.track("Desktop boot"); window.lokiP2pAPI = new window.LokiP2pAPI(ourKey); window.lokiP2pAPI.on('pingContact', pubKey => { diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index 80d403dbf..626a034db 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -4,9 +4,6 @@ const _ = require('lodash'); const { rpc } = require('./loki_rpc'); -const LokiMixpanelAPI = require('./loki_mixpanel.js'); - -const Mixpanel = new LokiMixpanelAPI(); const DEFAULT_CONNECTIONS = 3; const MAX_ACCEPTABLE_FAILURES = 1; @@ -181,7 +178,7 @@ class LokiMessageAPI { try { // eslint-disable-next-line more/no-then success = await firstTrue(promises); - Mixpanel.track('Sent Message Using Swarm API'); + window.mixpanel.track('Sent Message Using Swarm API'); } catch (e) { if (e instanceof textsecure.WrongDifficultyError) { // Force nonce recalculation @@ -195,7 +192,7 @@ class LokiMessageAPI { throw e; } if (!success) { - Mixpanel.track('Failed to Send Message Using Swarm API'); + window.mixpanel.track('Failed to Send Message Using Swarm API'); throw new window.textsecure.EmptySwarmError( pubKey, 'Ran out of swarm nodes to query' @@ -260,7 +257,7 @@ class LokiMessageAPI { } catch (e) { log.warn('Loki send message:', e); if (e instanceof textsecure.WrongSwarmError) { - Mixpanel.track('Migrated Snode'); + window.mixpanel.track('Migrated Snode'); const { newSwarm } = e; await lokiSnodeAPI.updateSwarmNodes(params.pubKey, newSwarm); this.sendingData[params.timestamp].swarm = newSwarm; diff --git a/js/modules/loki_mixpanel.js b/js/modules/loki_mixpanel.js index 681452982..4632a0bc9 100644 --- a/js/modules/loki_mixpanel.js +++ b/js/modules/loki_mixpanel.js @@ -1,12 +1,8 @@ -/* eslint-disable class-methods-use-this */ - const EventEmitter = require('events'); const Mixpanel = require('mixpanel'); -// require('setimmediate'); -class LokiMixpanelAPI extends EventEmitter { +class LokiMixpanelAPI { constructor() { - super(); this.mixpanel = Mixpanel.init('736cd9a854a157591153efacd1164e9a'); } track(label) { diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index b6738dfe7..aa0823723 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -3,9 +3,6 @@ clearTimeout, MessageController */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); -const LokiMixpanelAPI = require('./loki_mixpanel.js'); - -const Mixpanel = new LokiMixpanelAPI(); // Can't be less than 1200 if we have unauth'd requests const PUBLICCHAT_MSG_POLL_EVERY = 1.5 * 1000; // 1.5s @@ -666,12 +663,12 @@ class LokiPublicChannelAPI { objBody: payload, }); if (!res.err && res.response) { - Mixpanel.track('Public Message Sent'); + window.mixpanel.track('Public Message Sent'); return res.response.data.id; } // there's no retry on desktop // this is supposed to be after retries - Mixpanel.track('Failed to Send Public Message'); + window.mixpanel.track('Failed to Send Public Message'); return false; } } diff --git a/js/modules/loki_snode_api.js b/js/modules/loki_snode_api.js index c94598316..51da421a1 100644 --- a/js/modules/loki_snode_api.js +++ b/js/modules/loki_snode_api.js @@ -6,9 +6,6 @@ const dns = require('dns'); const process = require('process'); const { rpc } = require('./loki_rpc'); const natUpnp = require('nat-upnp'); -const LokiMixpanelAPI = require('./loki_mixpanel.js'); - -const Mixpanel = new LokiMixpanelAPI(); const resolve4 = url => new Promise((resolve, reject) => { @@ -121,7 +118,7 @@ class LokiSnodeAPI { port: snode.storage_port, })); } catch (e) { - Mixpanel.track('Seed Node Failed'); + window.mixpanel.track('Seed Node Failed'); if (seedNodes.length === 0) { throw new window.textsecure.SeedNodeError( 'Failed to contact seed node' @@ -137,7 +134,7 @@ class LokiSnodeAPI { const filteredNodes = swarmNodes.filter( node => node.address !== nodeUrl && node.ip !== nodeUrl ); - Mixpanel.track('Unreachable Snode'); + window.mixpanel.track('Unreachable Snode'); await conversation.updateSwarmNodes(filteredNodes); } From 587df45086cc1f48a4acfd8f21870fb074f637c6 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 19 Sep 2019 20:03:15 -0700 Subject: [PATCH 4/7] create window.mixpanel here --- preload.js | 1 + 1 file changed, 1 insertion(+) diff --git a/preload.js b/preload.js index fca170532..a82482870 100644 --- a/preload.js +++ b/preload.js @@ -329,6 +329,7 @@ window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiRssAPI = require('./js/modules/loki_rss_api'); window.LokiMixpanelAPI = require('./js/modules/loki_mixpanel.js'); +window.mixpanel = new LokiMixpanelAPI(); window.LocalLokiServer = require('./libloki/modules/local_loki_server'); From d4ca49ae9584c30a4d485c3e827656408cf16311 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 19 Sep 2019 20:06:42 -0700 Subject: [PATCH 5/7] separate out note to self --- js/views/inbox_view.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 04c24c9d1..3a3abe68f 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -309,7 +309,11 @@ window.mixpanel.track('Loki Public Chat Opened'); } if (conversation.isPrivate()) { - window.mixpanel.track('Conversation Opened'); + if (conversation.isMe()) { + window.mixpanel.track('Note To Self Opened'); + } else { + window.mixpanel.track('Conversation Opened'); + } } conversation.updateProfileName(); } From 3150a6ce11aefd49a09220b1698c1fab86d41bd6 Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 19 Sep 2019 20:18:03 -0700 Subject: [PATCH 6/7] separate out new/open, more lint/cleanup --- js/modules/loki_mixpanel.js | 1 - js/modules/loki_public_chat_api.js | 2 +- js/views/inbox_view.js | 5 ++++- preload.js | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/js/modules/loki_mixpanel.js b/js/modules/loki_mixpanel.js index 4632a0bc9..bf9b10e23 100644 --- a/js/modules/loki_mixpanel.js +++ b/js/modules/loki_mixpanel.js @@ -1,4 +1,3 @@ -const EventEmitter = require('events'); const Mixpanel = require('mixpanel'); class LokiMixpanelAPI { diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index aa0823723..080a3f408 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -1,5 +1,5 @@ /* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController, -clearTimeout, MessageController */ +clearTimeout, MessageController, window */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 3a3abe68f..6ef84e7d3 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -292,6 +292,9 @@ $target.toggleClass('section-toggle-visible'); }, async openConversation(id, messageId) { + const conversationExists = await ConversationController.getConversation( + id + ); const conversation = await ConversationController.getOrCreateAndWait( id, 'private' @@ -311,7 +314,7 @@ if (conversation.isPrivate()) { if (conversation.isMe()) { window.mixpanel.track('Note To Self Opened'); - } else { + } else if (conversationExists) { window.mixpanel.track('Conversation Opened'); } } diff --git a/preload.js b/preload.js index a82482870..42475394a 100644 --- a/preload.js +++ b/preload.js @@ -328,7 +328,8 @@ window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiRssAPI = require('./js/modules/loki_rss_api'); -window.LokiMixpanelAPI = require('./js/modules/loki_mixpanel.js'); +const LokiMixpanelAPI = require('./js/modules/loki_mixpanel.js'); + window.mixpanel = new LokiMixpanelAPI(); window.LocalLokiServer = require('./libloki/modules/local_loki_server'); From db8ac6611813ced5a9b0e92c0184612e794a8b7f Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 19 Sep 2019 20:22:23 -0700 Subject: [PATCH 7/7] fix method --- js/views/inbox_view.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 6ef84e7d3..01bb43136 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -292,9 +292,7 @@ $target.toggleClass('section-toggle-visible'); }, async openConversation(id, messageId) { - const conversationExists = await ConversationController.getConversation( - id - ); + const conversationExists = await ConversationController.get(id); const conversation = await ConversationController.getOrCreateAndWait( id, 'private'