Merge pull request 'master -> develop' (#2) from develop into master

Reviewed-on: http://localhost:3000/git/knotteye/satyr/pulls/2
pull/24/head^2
knotteye 4 years ago
commit 97b6f50b7c
  1. 2
      README.md
  2. 10
      docs/CHAT.md
  3. 34
      docs/CONFIGURATION.md
  4. 4
      docs/INSTALLATION.md
  5. 1
      docs/REST.md
  6. 50
      docs/USAGE.md
  7. 7
      install/config.example.yml
  8. 25
      install/satyr.nginx
  9. 502
      package-lock.json
  10. 3
      package.json
  11. 5
      site/index.html
  12. 51
      site/index.js
  13. 2
      site/local.css
  14. 4
      src/api.ts
  15. 51
      src/chat.ts
  16. 8
      src/cluster.ts
  17. 6
      src/config.ts
  18. 1
      src/http.ts
  19. 2
      src/index.ts
  20. 23
      templates/base.njk
  21. 45
      templates/user.njk

@ -4,7 +4,7 @@ System dependencies: A stable version of node>=10, mysql3 (or a compatible imple
### Setup Instructions ### Setup Instructions
```bash ```bash
git clone https://gitlab.com/knotteye/satyr.git git clone https://pond.waldn.net/git/knotteye/satyr.git
cd satyr cd satyr
npm install npm install
npm run setup npm run setup

@ -3,7 +3,7 @@ This is not a guide to using the webchat, this a reference point for writing cli
Satyr's webchat is based on [socket.io](https://socket.io/), you can find clients for [Java](https://github.com/socketio/socket.io-client-java), [C++](https://github.com/socketio/socket.io-client-cpp), [Swift](https://github.com/socketio/socket.io-client-swift), [Dart](https://github.com/rikulo/socket.io-client-dart), and probably more. Satyr's webchat is based on [socket.io](https://socket.io/), you can find clients for [Java](https://github.com/socketio/socket.io-client-java), [C++](https://github.com/socketio/socket.io-client-cpp), [Swift](https://github.com/socketio/socket.io-client-swift), [Dart](https://github.com/rikulo/socket.io-client-dart), and probably more.
Socket.IO is loosely reminiscent of IRC in that you will receive events from the server and sent events to it. The following is a list of incoming and outgoing events you will need to handle or send. If you would like to see examples, templates/chat.html is implementation used in the webclient by satyr. Socket.IO is loosely reminiscent of IRC in that you will receive events from the server and sent events to it. The following is a list of incoming and outgoing events you will need to handle or send. If you would like to see examples, templates/chat.html is the implementation used in the webclient by satyr.
# Incoming Events # Incoming Events
These are events you will recieve from the server that need to be handled in some way. These are events you will recieve from the server that need to be handled in some way.
@ -53,12 +53,12 @@ This is a request to set the client's nickname. The data attached to a NICK even
password: "the optional password" password: "the optional password"
} }
``` ```
During the initial connect of the client, the server will check for the "Authorization" cookie. If the cookie is a valid, signed JWT, the client will be assigned the nickname of the user that cookie belongs to. If it doesn't exist or is invalid, the client will be assigned a nickname of the form Guest+some integer. During the initial connect of the client, the server will check for the "Authorization" cookie. If the cookie is a valid, signed JWT the client will be assigned the nickname of the user that cookie belongs to. If it doesn't exist or is invalid, the client will be assigned a nickname of the form Guest+some integer.
The server will send an alert notifying the client of either the nickname change, or some error. The server will send an alert notifying the client of either the nickname change, or some error.
## MSG ## MSG
This is a chat message to send to room. It should be a JSON object in the following format: This is a chat message to send to a room. It should be a JSON object in the following format:
``` ```
{ {
room: "the room to send the messag to", room: "the room to send the messag to",
@ -117,10 +117,10 @@ A request to unban an IP address. It can only be done by the owner of the room.
# Final Notes # Final Notes
Sending more than 10 messages a second will cause the server to kick your client. If kicked this way 3 times, the client will be banned for 20 minutes. Sending more than 10 messages per second will cause the server to kick your client. If kicked this way 3 times, the client will be banned for 20 minutes.
Kicked or banned users will not be notified of this through an event. Kicked or banned users will not be notified of this through an event.
The server *will* send your own MSG events back to you, you will need to parse them out if you want to append them immediately. The server *will* send your own MSG events back to you, you will need to parse them out if you want to display them immediately.
Clients who successfully authenticate as a registered user, through either a password or a signed JWT, can ignore nickname collision and have as many connections as they wish. Clients who successfully authenticate as a registered user, through either a password or a signed JWT, can ignore nickname collision and have as many connections as they wish.

@ -7,11 +7,23 @@ Some values you might want to change are
satyr: satyr:
registration: true registration: true
# allow new users to register # allow new users to register
port: 8000
# the port to serve http on
http: http:
hsts: true hsts: true
# enable strict transport security # enable strict transport security
rtmp:
port: 1935
# change the port to serve rtmp on
cluster: false
# enable clustering for the RTMP server
# clustering is an attempt to take better advantage of multi threaded systems in spite of node.js being single-threaded
# satyr will spawn one RTMP Worker per CPU core, and round-robin incoming connections between workers
# If you turn this on, satyr will no longer be able to reliably serve RTMP streams to clients
# Your users will have to use DASH instead
media: media:
record: true record: true
# allow users to record VODs # allow users to record VODs
@ -36,7 +48,7 @@ transcode:
# https://trac.ffmpeg.org/wiki/HWAccelIntro is a good place to start # https://trac.ffmpeg.org/wiki/HWAccelIntro is a good place to start
# having more than 4-5 variants will start giving diminishing returns on stream quality for cpu load # having more than 4-5 variants will start giving diminishing returns on stream quality for cpu load
# if you can't afford to generate at least 3 variants, it's reccomended to leave adaptive streaming off # if you can't afford to generate at least 3 variants, it's recommended to leave adaptive streaming off
crypto: crypto:
saltRounds: 12 saltRounds: 12
@ -44,12 +56,11 @@ crypto:
# if you don't understand the implications, don't change this # if you don't understand the implications, don't change this
chat: chat:
# the following settings are for chat mirroring bots # the following settings are for chat bridging bots
# users will still need to choose which channel to mirror # users will still need to choose which channel to bridge
# for their chat at /profile/chat # for their chat at /profile/chat
irc: irc:
enabled: true enabled: true
# enable irc mirroring
server: chat.freenode.net server: chat.freenode.net
port: 6697 port: 6697
tls: true tls: true
@ -61,7 +72,6 @@ chat:
discord: discord:
enabled: true enabled: true
# enabled discord integration
token: abcdefghijklmnopqrstuvwxyz token: abcdefghijklmnopqrstuvwxyz
# the access token for the bot # the access token for the bot
# note that the bot will mirror every channel matching the name the user has chosen # note that the bot will mirror every channel matching the name the user has chosen
@ -74,6 +84,20 @@ chat:
# access token for the twitch chat bot # access token for the twitch chat bot
# this is not the account password, you will need to generate a token here: # this is not the account password, you will need to generate a token here:
# https://twitchapps.com/tmi/ # https://twitchapps.com/tmi/
xmpp:
enabled: true
server: 'example.com'
port: 5222
jid: 'exampleBot@example.com'
password: 'abcde'
# connection settings for the bot
nickname: 'SatyrChat
# the nickname the bot will join MUCs with
# note that for the best experience you should set the default number of history messages to 0 for the MUC
# The bot will attempt to request 0 history messages anyway, and will also attempt to ignore any history messages it receives
# but both of these things are unreliable
``` ```
### Web Frontend ### Web Frontend

@ -4,14 +4,14 @@ A more detailed walkthrough.
### System Dependencies ### System Dependencies
Install ffmpeg(>= 4.2.1) and mysql through your distribution's package manager. Install ffmpeg(>= 4.2.1) and mysql through your distribution's package manager.
See [this page](https://nodejs.org/en/download/package-manager/) for instructions on installing node v10. See [this page](https://nodejs.org/en/download/package-manager/) for instructions on installing node v10.
If the version in your distro's package manager is different, you can install 'n' through npm to manage node versions. If the version in your distro's package manager is different, you can install [n](https://www.npmjs.com/package/n) through npm to manage node versions.
### Installing Satyr ### Installing Satyr
Before starting, you should create a system user to run the satyr service. Before starting, you should create a system user to run the satyr service.
Clone the repository and change to the directory Clone the repository and change to the directory
```bash ```bash
git clone https://gitlab.com/knotteye/satyr.git git clone https://pond.waldn.net/git/knotteye/satyr.git
cd satyr cd satyr
``` ```
Install nodejs dependencies Install nodejs dependencies

@ -32,6 +32,7 @@ Configuration of the instance relating to media
``` ```
{ {
rtmp: { rtmp: {
cluster: false,
port: 1935, port: 1935,
ping_timeout: 60 ping_timeout: 60
}, },

@ -1,30 +1,19 @@
## Satyr Usage ## Satyr Usage
### Administration ### Administration
Satyr needs access to port 1935 for RTMP streams, and will serve HTTP on port 8000. The ports can be changed with follow config lines. Satyr needs access to port 1935 for RTMP streams, and will serve HTTP on port 8000. See CONFIGURATION.md for details on changing this.
For HTTPS, run a reverse proxy in front of satyr. An example nginx block is shown below. For HTTPS, run a reverse proxy in front of satyr. An example nginx config can be found at install/satyr.nginx
``` An example systemd service can also be at install/satyr.service
server {
port 80; #### CLI
port [::]80; Satyr's CLI tool can be run with `npm run cli` or `node_modules/.bin/ts-node src/cli.ts`
server_name example.tld;
return https://$server_name$request_uri 301; It's not very complex. The following commands are available:
} * `npm run cli -- --adduser sally --password "hunter12"` to create user sally with the password hunter12
server { * `npm run cli -- --rmuser sally` to remove user sally
port 443 ssl; * `npm run cli -- --invite` to generate an invite code used for creating account even when registration is closed
port [::]443 ssl;
server_name example.tld;
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000/;
}
}
```
### Users ### Users
@ -35,13 +24,14 @@ Stream keys can be changed at example.tld/changesk, and passwords at /changepwd
#### Chat #### Chat
Chat is based on Socket.IO, and can be accessed through the webclient at /chat. Chat is based on Socket.IO, and can be accessed through the webclient at /chat.
Chatting and changing a nickname do not require authentication, but the usernames of streamers are reserved. Chatting and changing a nickname do not require authentication, but the usernames of streamers are reserved.
The following commands are available: The following commands are available:
`/nick sally (password)` Password is only required if sally is a registered user. * `/nick sally (password)` Password is only required if sally is a registered user.
`/join sally` Join the chatroom for sally's stream and leave the previous room. * `/join sally` Join the chatroom for sally's stream and leave the previous room.
`/kick bob` Available only in your own room if you are a streamer. Forcefully disconnect the user. * `/kick bob` Available only in your own room if you are a streamer. Forcefully disconnect the user.
`/ban bob (time)` Ban a user from your room. Bans are based on IP address. The optional time is in minutes. The default is 30. * `/ban bob (time)` Ban a user from your room. Bans are based on IP address. The optional time is in minutes. The default is 30.
`/banlist` List the IPs currently banned from your room. * `/banlist` List the IPs currently banned from your room.
`/unban (ip)` self explanatory * `/unban (ip)` self explanatory
You can set up mirroring to and from webchat rooms and IRC channels, twitch streams, and discord server channels. You can set up mirroring to and from webchat rooms and IRC channels, twitch streams, and discord server channels.
More information is in CONFIGURATION.md More information is in CONFIGURATION.md

@ -51,10 +51,11 @@ chat:
xmpp: xmpp:
enabled: false enabled: false
server: server:
port: 5222 port: 5222
nickname: 'SatyrChat' jid:
username: 'SatyrChat' password:
nickname:
twitch: twitch:
enabled: false enabled: false

@ -0,0 +1,25 @@
server {
port 80;
port [::]80;
server_name example.tld;
return https://$server_name$request_uri 301;
}
server {
port 443 ssl;
port [::]443 ssl;
server_name example.tld;
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000/;
}
location ~* \.(mpd|m4s|mp4)$ {
# nginx can serve static files faster than node
# this should improve performance
root /opt/satyr/site/;
}
}

502
package-lock.json generated

@ -22,6 +22,30 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.67.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.67.tgz",
"integrity": "sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg==" "integrity": "sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg=="
}, },
"@xmpp/jid": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@xmpp/jid/-/jid-0.0.2.tgz",
"integrity": "sha1-DVKMqdWNr8gzZlVk/+YvMyoxZ/I="
},
"@xmpp/streamparser": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@xmpp/streamparser/-/streamparser-0.0.6.tgz",
"integrity": "sha1-EYAz6p23yGoctGED8mnr/3n28eo=",
"requires": {
"@xmpp/xml": "^0.1.3",
"inherits": "^2.0.3",
"ltx": "^2.5.0"
}
},
"@xmpp/xml": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@xmpp/xml/-/xml-0.1.3.tgz",
"integrity": "sha1-HxQ5nlPkGWiFWGmPbGLnHjmoam4=",
"requires": {
"inherits": "^2.0.3",
"ltx": "^2.6.2"
}
},
"a-sync-waterfall": { "a-sync-waterfall": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
@ -46,6 +70,17 @@
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
}, },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@ -108,6 +143,14 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
}, },
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"asn1.js": { "asn1.js": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz",
@ -118,16 +161,41 @@
"minimalistic-assert": "^1.0.0" "minimalistic-assert": "^1.0.0"
} }
}, },
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"async-limiter": { "async-limiter": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
}, },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
},
"backo2": { "backo2": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
}, },
"backoff": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.3.0.tgz",
"integrity": "sha1-7nx+OAk/kuRyhZ22NedlJFT8Ieo="
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@ -157,6 +225,21 @@
"node-pre-gyp": "0.15.0" "node-pre-gyp": "0.15.0"
} }
}, },
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
},
"dependencies": {
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}
}
},
"better-assert": { "better-assert": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
@ -236,6 +319,11 @@
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
} }
}, },
"browser-request": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz",
"integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc="
},
"buffer-from": { "buffer-from": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@ -251,6 +339,11 @@
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
}, },
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"chalk": { "chalk": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@ -300,6 +393,14 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
}, },
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": { "commander": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
@ -390,6 +491,14 @@
"ts-toolbelt": "^6.9.0" "ts-toolbelt": "^6.9.0"
} }
}, },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"dateformat": { "dateformat": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@ -431,6 +540,11 @@
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
}, },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -506,6 +620,15 @@
} }
} }
}, },
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"ee-first": { "ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -685,6 +808,26 @@
} }
} }
}, },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -728,6 +871,21 @@
"resolved": "https://registry.npmjs.org/flags/-/flags-0.1.3.tgz", "resolved": "https://registry.npmjs.org/flags/-/flags-0.1.3.tgz",
"integrity": "sha1-lh0vyM3zZp1jBB4w5bJK2tNvV1g=" "integrity": "sha1-lh0vyM3zZp1jBB4w5bJK2tNvV1g="
}, },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -772,6 +930,14 @@
"wide-align": "^1.1.0" "wide-align": "^1.1.0"
} }
}, },
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"glob": { "glob": {
"version": "7.1.6", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@ -794,6 +960,20 @@
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
} }
}, },
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"requires": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
}
},
"has-binary2": { "has-binary2": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
@ -824,6 +1004,33 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"hash-base": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "^3.6.0",
"safe-buffer": "^5.2.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
"http-errors": { "http-errors": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@ -843,6 +1050,16 @@
} }
} }
}, },
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"iconv": { "iconv": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/iconv/-/iconv-2.2.3.tgz", "resolved": "https://registry.npmjs.org/iconv/-/iconv-2.2.3.tgz",
@ -955,11 +1172,21 @@
"resolved": "https://registry.npmjs.org/is-port-available/-/is-port-available-0.1.5.tgz", "resolved": "https://registry.npmjs.org/is-port-available/-/is-port-available-0.1.5.tgz",
"integrity": "sha512-/r7UZAQtfgDFdhxzM71jG0mkC4oSRA513cImMILdRe/+UOIe0Se/D/Z7XCua4AFg5k4Zt3ALMGaC1W3FzlrR2w==" "integrity": "sha512-/r7UZAQtfgDFdhxzM71jG0mkC4oSRA513cImMILdRe/+UOIe0Se/D/Z7XCua4AFg5k4Zt3ALMGaC1W3FzlrR2w=="
}, },
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
}, },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jose": { "jose": {
"version": "1.15.1", "version": "1.15.1",
"resolved": "https://registry.npmjs.org/jose/-/jose-1.15.1.tgz", "resolved": "https://registry.npmjs.org/jose/-/jose-1.15.1.tgz",
@ -968,11 +1195,47 @@
"asn1.js": "^5.2.0" "asn1.js": "^5.2.0"
} }
}, },
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"lodash": { "lodash": {
"version": "4.17.20", "version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
}, },
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
},
"lodash.camelcase": { "lodash.camelcase": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@ -988,6 +1251,14 @@
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
}, },
"ltx": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/ltx/-/ltx-2.10.0.tgz",
"integrity": "sha512-RB4zR6Mrp/0wTNS9WxMvpgfht/7u/8QAC9DpPD19opL/4OASPa28uoliFqeDkLUU8pQ4aeAfATBZmz1aSAHkMw==",
"requires": {
"inherits": "^2.0.4"
}
},
"make-error": { "make-error": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
@ -1001,6 +1272,16 @@
"make-error": "^1.3.5" "make-error": "^1.3.5"
} }
}, },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
"safe-buffer": "^5.1.2"
}
},
"media-typer": { "media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -1171,6 +1452,79 @@
"tar": "^4.4.2" "tar": "^4.4.2"
} }
}, },
"node-xmpp-client": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/node-xmpp-client/-/node-xmpp-client-3.2.0.tgz",
"integrity": "sha1-r0Un3wzFq9JpDLohOcwezcgeoYk=",
"requires": {
"browser-request": "^0.3.3",
"debug": "^2.2.0",
"md5.js": "^1.3.3",
"minimist": "^1.2.0",
"node-xmpp-core": "^5.0.9",
"request": "^2.65.0",
"ws": "^1.1.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"ws": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
"integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
"requires": {
"options": ">=0.0.5",
"ultron": "1.0.x"
}
}
}
},
"node-xmpp-core": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/node-xmpp-core/-/node-xmpp-core-5.0.9.tgz",
"integrity": "sha1-XCjCjtsfs/i+uixnYHd2E/SPNCo=",
"requires": {
"@xmpp/jid": "^0.0.2",
"@xmpp/streamparser": "^0.0.6",
"@xmpp/xml": "^0.1.3",
"debug": "^2.2.0",
"inherits": "^2.0.1",
"lodash.assign": "^4.0.0",
"node-xmpp-tls-connect": "^1.0.1",
"reconnect-core": "https://github.com/dodo/reconnect-core/tarball/merged"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"node-xmpp-tls-connect": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/node-xmpp-tls-connect/-/node-xmpp-tls-connect-1.0.1.tgz",
"integrity": "sha1-kazkOsJrE4hhsr5HjfnfGdYdxcM="
},
"nopt": { "nopt": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
@ -1236,6 +1590,11 @@
"commander": "^3.0.2" "commander": "^3.0.2"
} }
}, },
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -1262,6 +1621,11 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"options": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
"integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
},
"os-homedir": { "os-homedir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@ -1317,6 +1681,11 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
}, },
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"picomatch": { "picomatch": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
@ -1342,6 +1711,21 @@
"ipaddr.js": "1.9.0" "ipaddr.js": "1.9.0"
} }
}, },
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qbox": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/qbox/-/qbox-0.1.7.tgz",
"integrity": "sha1-6A8NxdCfhp2IghaMP2asjdKEDwI="
},
"qs": { "qs": {
"version": "6.7.0", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
@ -1418,6 +1802,13 @@
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
} }
}, },
"reconnect-core": {
"version": "https://github.com/dodo/reconnect-core/tarball/merged",
"integrity": "sha512-wZK/v5ZaNaSUs2Wnwh2YSX/Jqv6bQHKNEwojdzV11tByKziR9ikOssf5tvUhx+8/oCBz6AakOFAjZuqPoiRHJQ==",
"requires": {
"backoff": "~2.3.0"
}
},
"recursive-readdir": { "recursive-readdir": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@ -1426,6 +1817,40 @@
"minimatch": "3.0.4" "minimatch": "3.0.4"
} }
}, },
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"dependencies": {
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
}
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -1564,6 +1989,15 @@
} }
} }
}, },
"simple-xmpp": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/simple-xmpp/-/simple-xmpp-1.3.1.tgz",
"integrity": "sha512-o0wGVlI8Q4o0qTz6Kylbo1QPOMVn+DA/vyHHZecqcQ+LK4ZWGe3wtRON9QjHAkSyxB36PoagmiUz4pHADau8Mw==",
"requires": {
"node-xmpp-client": "^3.0.0",
"qbox": "0.1.x"
}
},
"snekfetch": { "snekfetch": {
"version": "3.6.4", "version": "3.6.4",
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz", "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
@ -1742,6 +2176,29 @@
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
}, },
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"dependencies": {
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}
}
},
"statuses": { "statuses": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@ -1829,6 +2286,15 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
}, },
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"ts-node": { "ts-node": {
"version": "8.5.4", "version": "8.5.4",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz",
@ -1846,6 +2312,14 @@
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.9.9.tgz", "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.9.9.tgz",
"integrity": "sha512-5a8k6qfbrL54N4Dw+i7M6kldrbjgDWb5Vit8DnT+gwThhvqMg8KtxLE5Vmnft+geIgaSOfNJyAcnmmlflS+Vdg==" "integrity": "sha512-5a8k6qfbrL54N4Dw+i7M6kldrbjgDWb5Vit8DnT+gwThhvqMg8KtxLE5Vmnft+geIgaSOfNJyAcnmmlflS+Vdg=="
}, },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": { "tweetnacl": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
@ -1865,11 +2339,24 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==" "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw=="
}, },
"ultron": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
},
"unpipe": { "unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
}, },
"uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
"integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
"requires": {
"punycode": "^2.1.0"
}
},
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -1880,11 +2367,26 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
}, },
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}, },
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"wide-align": { "wide-align": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",

@ -1,6 +1,6 @@
{ {
"name": "satyr", "name": "satyr",
"version": "0.10.0", "version": "0.10.1",
"description": "A livestreaming server.", "description": "A livestreaming server.",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"author": "knotteye", "author": "knotteye",
@ -33,6 +33,7 @@
"nunjucks": "^3.2.1", "nunjucks": "^3.2.1",
"parse-yaml": "^0.1.0", "parse-yaml": "^0.1.0",
"recursive-readdir": "^2.2.2", "recursive-readdir": "^2.2.2",
"simple-xmpp": "^1.3.1",
"socket-anti-spam": "^2.0.0", "socket-anti-spam": "^2.0.0",
"socket.io": "^2.3.0", "socket.io": "^2.3.0",
"strftime": "^0.10.0", "strftime": "^0.10.0",

@ -3,12 +3,13 @@
<link rel="stylesheet" type="text/css" href="/styles.css"> <link rel="stylesheet" type="text/css" href="/styles.css">
<link rel="stylesheet" type="text/css" href="/local.css"> <link rel="stylesheet" type="text/css" href="/local.css">
<link rel="icon" type="image/svg" href="/logo.svg"> <link rel="icon" type="image/svg" href="/logo.svg">
<script src="/nunjucks-slim.js"></script> <script src="/nunjucks-slim.js"></script>
<script src="/templates.js"></script> <script src="/templates.js"></script>
<script src="/dashjs/dash.all.min.js"></script>
<script> <script>
nunjucks.configure({ autoescape: true }); nunjucks.configure({ autoescape: true });
</script>
<script>
//should check for and refresh login tokens on pageload.. //should check for and refresh login tokens on pageload..
if(document.cookie.match(/^(.*;)?\s*X-Auth-As\s*=\s*[^;]+(.*)?$/) !== null) { if(document.cookie.match(/^(.*;)?\s*X-Auth-As\s*=\s*[^;]+(.*)?$/) !== null) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();

@ -1,5 +1,7 @@
async function render(path){ async function render(path, s){
var context = await getContext(); var context = await getContext();
if(!s)
history.pushState({}, context.sitename, location.protocol+'//'+location.host+path);
switch(path){ switch(path){
//nothing but context //nothing but context
case (path.match(/^\/about\/?$/) || {}).input: case (path.match(/^\/about\/?$/) || {}).input:
@ -80,6 +82,7 @@ async function render(path){
if(!config.title){document.body.innerHTML = nunjucks.render('404.njk', context); break;} if(!config.title){document.body.innerHTML = nunjucks.render('404.njk', context); break;}
document.body.innerHTML = nunjucks.render('user.njk', Object.assign({about: config.about, title: config.title, username: config.username}, context)); document.body.innerHTML = nunjucks.render('user.njk', Object.assign({about: config.about, title: config.title, username: config.username}, context));
modifyLinks(); modifyLinks();
startVideo();
break; break;
case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage
var usr = path.substring(6, (path.length - 7)); var usr = path.substring(6, (path.length - 7));
@ -103,6 +106,9 @@ async function render(path){
case "": case "":
render('/users/live'); render('/users/live');
break; break;
case "/index.html":
render('/users/live');
break;
//404 //404
default: default:
document.body.innerHTML = nunjucks.render('404.njk', context); document.body.innerHTML = nunjucks.render('404.njk', context);
@ -110,6 +116,10 @@ async function render(path){
} }
} }
window.addEventListener('popstate', (event) => {
render(document.location.pathname, true);
});
async function getContext(){ async function getContext(){
var info = JSON.parse(await makeRequest('GET', '/api/instance/info')); var info = JSON.parse(await makeRequest('GET', '/api/instance/info'));
info.sitename = info.name; info.sitename = info.name;
@ -167,4 +177,43 @@ function modifyLinks() {
function internalLink(path){ function internalLink(path){
this.render(path); this.render(path);
return false; return false;
}
//start dash.js
async function startVideo(){
//var url = "/live/{{username}}/index.mpd";
//var player = dashjs.MediaPlayer().create();
//player.initialize(document.querySelector("#videoPlayer"), url, true);
//console.log('called startvideo');
while(true){
if(!document.querySelector('#videoPlayer'))
break;
if(window.location.pathname.substring(window.location.pathname.length - 1) !== '/'){
var url = "/api/"+window.location.pathname.substring(7)+"/config";
console.log(url)
var xhr = JSON.parse(await makeRequest("GET", url));
if(xhr.live){
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
break;
}
}
else{
var url = "/api/"+window.location.pathname.substring(7, window.location.pathname.length - 1)+"/config";
console.log(url)
var xhr = JSON.parse(await makeRequest("GET", url));
if(xhr.live){
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
break;
}
}
await sleep(60000);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
} }

@ -0,0 +1,2 @@
/* This file is for themeing Satyr's frontend without worrying about changes
being overwritten. Feel free to make whatever local changes you want here. */

@ -86,7 +86,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
if(all) { if(all) {
let users = await db.query('SELECT stream_key,record_flag FROM users WHERE username='+db.raw.escape(username)); let users = await db.query('SELECT stream_key,record_flag FROM users WHERE username='+db.raw.escape(username));
if(users[0]) Object.assign(t, users[0]); if(users[0]) Object.assign(t, users[0]);
let usermeta = await db.query('SELECT title,about FROM user_meta WHERE username='+db.raw.escape(username)); let usermeta = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
if(usermeta[0]) Object.assign(t, usermeta[0]); if(usermeta[0]) Object.assign(t, usermeta[0]);
let ci = await db.query('SELECT irc,xmpp,twitch,discord FROM chat_integration WHERE username='+db.raw.escape(username)); let ci = await db.query('SELECT irc,xmpp,twitch,discord FROM chat_integration WHERE username='+db.raw.escape(username));
if(ci[0]) Object.assign(t, ci[0]); if(ci[0]) Object.assign(t, ci[0]);
@ -94,7 +94,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
if(tw[0]) t['twitch_mirror'] = Object.assign({}, tw[0]); if(tw[0]) t['twitch_mirror'] = Object.assign({}, tw[0]);
} }
else { else {
let um = await db.query('SELECT title,about FROM user_meta WHERE username='+db.raw.escape(username)); let um = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
if(um[0]) Object.assign(t, um[0]); if(um[0]) Object.assign(t, um[0]);
} }
return t; return t;

@ -4,9 +4,12 @@ import {io} from "./http";
import * as irc from "irc"; import * as irc from "irc";
import * as discord from "discord.js"; import * as discord from "discord.js";
import * as twitch from "dank-twitch-irc"; import * as twitch from "dank-twitch-irc";
import * as xmpp from "simple-xmpp";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
var ircClient; var ircClient;
var xmppClient; var xmppIgnore: Array<string> = [];
var xmppJoined: Array<string> = [];
var twitchClient; var twitchClient;
var twitchArr: Array<string> = []; var twitchArr: Array<string> = [];
var discordClient; var discordClient;
@ -51,7 +54,24 @@ async function init() {
}); });
} }
if(config['chat']['xmpp']['enabled']){ if(config['chat']['xmpp']['enabled']){
xmpp.on('online', (data) => {
console.log("XMPP Client Ready");
});
xmpp.on('groupchat', function(conference, from, message, stamp) {
if(xmppIgnore.findIndex((e) => { return e === conference }) !== -1) return false;
if(from === config['chat']['xmpp']['nickname']) return false;
console.log(from+'\n'+conference+'\n'+message+'\n'+stamp);
var lu = getUsr(conference, "xmpp");
for(var i=0;i<lu.length;i++){
sendAll(lu[i], [from, message], "xmpp")
}
});
xmpp.connect({
jid: config['chat']['xmpp']['jid'],
password: config['chat']['xmpp']['password'],
host: config['chat']['xmpp']['server'],
port: config['chat']['xmpp']['port']
});
} }
if(config['chat']['twitch']['enabled']){ if(config['chat']['twitch']['enabled']){
twitchClient = new twitch.ChatClient({ twitchClient = new twitch.ChatClient({
@ -96,12 +116,14 @@ async function updateInteg() {
chatIntegration = []; chatIntegration = [];
if(config['chat']['irc']['enabled']) updateIRCChan(); if(config['chat']['irc']['enabled']) updateIRCChan();
if(config['chat']['twitch']['enabled']) updateTwitchChan(); if(config['chat']['twitch']['enabled']) updateTwitchChan();
if(config['chat']['xmpp']['enabled']) updateXmppChan();
return; return;
} }
if(liveUsers.length === 1) { if(liveUsers.length === 1) {
chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+db.raw.escape(liveUsers[0]['username'])); chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+db.raw.escape(liveUsers[0]['username']));
if(config['chat']['irc']['enabled']) updateIRCChan(); if(config['chat']['irc']['enabled']) updateIRCChan();
if(config['chat']['twitch']['enabled']) updateTwitchChan(); if(config['chat']['twitch']['enabled']) updateTwitchChan();
if(config['chat']['xmpp']['enabled']) updateXmppChan();
return; return;
} }
var qs: string; var qs: string;
@ -112,6 +134,7 @@ async function updateInteg() {
chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+qs); chatIntegration = await db.query('SELECT * FROM chat_integration WHERE username='+qs);
if(config['chat']['irc']['enabled']) updateIRCChan(); if(config['chat']['irc']['enabled']) updateIRCChan();
if(config['chat']['twitch']['enabled']) updateTwitchChan(); if(config['chat']['twitch']['enabled']) updateTwitchChan();
if(config['chat']['xmpp']['enabled']) updateXmppChan();
} }
async function sendAll(user: string, msg: Array<string>, src: string) { async function sendAll(user: string, msg: Array<string>, src: string) {
@ -126,7 +149,7 @@ async function sendAll(user: string, msg: Array<string>, src: string) {
if(src !== "irc") sendIRC(getCh(user, "irc"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]); if(src !== "irc") sendIRC(getCh(user, "irc"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
if(src !== "twitch") sendTwitch(getCh(user, "twitch"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]); if(src !== "twitch") sendTwitch(getCh(user, "twitch"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
if(src !== "discord") sendDiscord(getCh(user, "discord"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]); if(src !== "discord") sendDiscord(getCh(user, "discord"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
//if(src !== "xmpp") sendXMPP(); if(src !== "xmpp") sendXMPP(getCh(user, "xmpp"), '['+src.toUpperCase()+']'+msg[0]+': '+msg[1]);
if(src !== "web") sendWeb(user, ['['+src.toUpperCase()+']'+msg[0], msg[1]]); if(src !== "web") sendWeb(user, ['['+src.toUpperCase()+']'+msg[0], msg[1]]);
} }
@ -146,6 +169,7 @@ async function sendDiscord(channel: string, msg: string) {
async function sendXMPP(channel: string, msg: string) { async function sendXMPP(channel: string, msg: string) {
if(!config['chat']['xmpp']['enabled']) return; if(!config['chat']['xmpp']['enabled']) return;
if(channel === null) return; if(channel === null) return;
xmpp.send(channel, msg, true);
} }
async function sendTwitch(channel: string, msg: string) { async function sendTwitch(channel: string, msg: string) {
@ -252,4 +276,25 @@ async function normalizeDiscordMsg(msg): Promise<string>{
return nmsg; return nmsg;
} }
function xmppJoin(room: string): void{
if(xmppJoined.findIndex((e) => { return e === room }) !== -1) return;
var stanza = new xmpp.Element('presence', {"to": room+'/'+config['chat']['xmpp']['nickname']}).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', { maxstanzas: 0, seconds: 0});
xmpp.conn.send(stanza);
xmppIgnore = xmppIgnore.concat([room]);
xmpp.join(room+'/'+config['chat']['xmpp']['nickname']);
xmppJoined = xmppJoined.concat([room]);
sleep(4000).then(() => {
xmppIgnore = xmppIgnore.filter((item) => {
return item !== room;
});
});
}
function updateXmppChan(): void{
for(var i=0;i<chatIntegration.length;i++){
if(chatIntegration[i]['xmpp'].trim() !== "" && chatIntegration[i]['xmpp'] !== null) xmppJoin(chatIntegration[i]['xmpp']);
}
//we can't really leave channels so I'll come back to that.
}
export { init, sendAll }; export { init, sendAll };

@ -133,14 +133,14 @@ if (cluster.isMaster) {
db.query('update user_meta set live=true where username=\''+results[0].username+'\' limit 1'); db.query('update user_meta set live=true where username=\''+results[0].username+'\' limit 1');
db.query('SELECT twitch_key,enabled from twitch_mirror where username='+db.raw.escape(results[0].username)+' limit 1').then(async (tm) => { db.query('SELECT twitch_key,enabled from twitch_mirror where username='+db.raw.escape(results[0].username)+' limit 1').then(async (tm) => {
if(!tm[0]['enabled'] || !config['twitch_mirror']['enabled'] || !config['twitch_mirror']['ingest']) return; if(!tm[0]['enabled'] || !config['twitch_mirror']['enabled'] || !config['twitch_mirror']['ingest']) return;
console.log('[NodeMediaServer] Mirroring to twitch for stream:',id) console.log(`[RTMP Cluster WORKER ${process.pid}] Mirroring to twitch for stream: ${id}`)
execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+wPort+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], { execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+wPort+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], {
detached: true, detached: true,
stdio : 'inherit', stdio : 'inherit',
maxBuffer: Infinity maxBuffer: Infinity
}).unref(); }).unref();
}); });
console.log('[NodeMediaServer] Stream key ok for stream:',id); console.log(`[RTMP Cluster WORKER ${process.pid}] Stream key ok for stream: ${id}`);
console.log(`[RTMP Cluster WORKER ${process.pid}] Stream key ok for stream: ${id}`); console.log(`[RTMP Cluster WORKER ${process.pid}] Stream key ok for stream: ${id}`);
//notify master process that we're handling the stream for this user //notify master process that we're handling the stream for this user
process.send({type: 'handle-publish', name:results[0].username}); process.send({type: 'handle-publish', name:results[0].username});
@ -171,14 +171,14 @@ if (cluster.isMaster) {
let key: string = StreamPath.split("/")[2]; let key: string = StreamPath.split("/")[2];
//correctly formatted urls again //correctly formatted urls again
if (StreamPath.split("/").length !== 3){ if (StreamPath.split("/").length !== 3){
console.log("[NodeMediaServer] Malformed URL, closing connection for stream:",id); console.log(`[RTMP Cluster WORKER ${process.pid}] Malformed URL, closing connection for stream: ${id}`);
session.reject(); session.reject();
return false; return false;
} }
//localhost can play from whatever endpoint //localhost can play from whatever endpoint
//other clients must use private endpoint //other clients must use private endpoint
if(app !== config['media']['publicEndpoint'] && !session.isLocal) { if(app !== config['media']['publicEndpoint'] && !session.isLocal) {
console.log("[NodeMediaServer] Non-local Play from private endpoint, rejecting client:",id); console.log(`[RTMP Cluster WORKER ${process.pid}] Non-local Play from private endpoint, rejecting client: ${id}`);
session.reject(); session.reject();
return false; return false;
} }

@ -30,6 +30,7 @@ const config: Object = {
insecureAuth: false, insecureAuth: false,
debug: false }, localconfig['database']), debug: false }, localconfig['database']),
rtmp: Object.assign({ rtmp: Object.assign({
cluster: false,
port: 1935, port: 1935,
chunk_size: 6000, chunk_size: 6000,
gop_cache: true, gop_cache: true,
@ -75,8 +76,9 @@ const config: Object = {
enabled: false, enabled: false,
server: null, server: null,
port: 5222, port: 5222,
nickname: 'SatyrChat', jid: null,
username: 'SatyrChat' password: null,
nickname: 'SatyrChat'
}, localconfig['chat']['xmpp']), }, localconfig['chat']['xmpp']),
twitch: Object.assign({ twitch: Object.assign({

@ -168,6 +168,7 @@ async function initAPI() {
app.get('/api/instance/config', (req, res) => { app.get('/api/instance/config', (req, res) => {
res.json({ res.json({
rtmp: { rtmp: {
cluster: config['rtmp']['cluster'],
port: config['rtmp']['port'], port: config['rtmp']['port'],
ping_timeout: config['rtmp']['ping_timeout'] ping_timeout: config['rtmp']['ping_timeout']
}, },

@ -10,7 +10,7 @@ async function run() {
await initDB(); await initDB();
await clean(); await clean();
await initHTTP(); await initHTTP();
config['rtmp']['cluster'] ? execFile(process.cwd()+'/node_modules/.bin/ts-node' [process.cwd()+'src/cluster.ts']) : await initRTMP(); config['rtmp']['cluster'] ? execFile(process.cwd()+'/node_modules/.bin/ts-node', [process.cwd()+'/src/cluster.ts']) : await initRTMP();
await initChat(); await initChat();
console.log(`Satyr v${config['satyr']['version']} ready`); console.log(`Satyr v${config['satyr']['version']} ready`);
} }

@ -12,6 +12,29 @@
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(""); xhr.send("");
} }
function makeRequest(method, url, payload) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
!payload ? xhr.send() : xhr.send(payload);
});
}
</script> </script>
{% block head %} {% block head %}
{% endblock %} {% endblock %}

@ -4,12 +4,43 @@
<link rel="stylesheet" type="text/css" href="/videojs/video-js.min.css">--> <link rel="stylesheet" type="text/css" href="/videojs/video-js.min.css">-->
<script src="/dashjs/dash.all.min.js"></script> <script src="/dashjs/dash.all.min.js"></script>
<script> <script>
function startVideo(){ async function startVideo(){
var url = "/live/{{username}}/index.mpd"; //var url = "/live/{{username}}/index.mpd";
var player = dashjs.MediaPlayer().create(); //var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true); //player.initialize(document.querySelector("#videoPlayer"), url, true);
console.log('called startvideo'); //console.log('called startvideo');
} while(true){
if(!document.querySelector('#videoPlayer'))
break;
if(window.location.pathname.substring(window.location.pathname.length - 1) !== '/'){
var url = "/api/"+window.location.pathname.substring(7)+"/config";
console.log(url)
var xhr = JSON.parse(await makeRequest("GET", url));
if(xhr.live){
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
break;
}
}
else{
var url = "/api/"+window.location.pathname.substring(7, window.location.pathname.length - 1)+"/config";
console.log(url)
var xhr = JSON.parse(await makeRequest("GET", url));
if(xhr.live){
var player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector("#videoPlayer"), url, true);
break;
}
}
await sleep(60000);
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
</script> </script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -28,7 +59,7 @@ function newPopup(url) {
<!--this spits errors fucking constantly after it tries to reload a video that's already running.. I dunno if it's bad or causing problems so let's just push it to develop and wait for issues!--> <!--this spits errors fucking constantly after it tries to reload a video that's already running.. I dunno if it's bad or causing problems so let's just push it to develop and wait for issues!-->
<!--it plays the stream without reloading the page tho lol--> <!--it plays the stream without reloading the page tho lol-->
<script>window.setInterval(startVideo, 60000)</script> <script>startVideo()</script>
</div> </div>
<div id="jschild" class="webchat" style="width: 30%;height: 100%;position: relative;"> <div id="jschild" class="webchat" style="width: 30%;height: 100%;position: relative;">
<iframe src="/chat?room={{ username }}" frameborder="0" style="width: 100%;height: 100%; min-height: 534px;" allowfullscreen></iframe> <iframe src="/chat?room={{ username }}" frameborder="0" style="width: 100%;height: 100%; min-height: 534px;" allowfullscreen></iframe>