mirror of
https://github.com/spacebarchat/server.git
synced 2026-04-25 09:42:10 +00:00
Merge 'webrtc' into 'dev/staging_webrtc'
This commit is contained in:
13
.babelrc
Normal file
13
.babelrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"transform-imports",
|
||||
{
|
||||
"my-library": {
|
||||
"transform": "../../path/to/transform.js",
|
||||
"preventFullImport": true
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
26
.swcrc
Normal file
26
.swcrc
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"jsc": {
|
||||
"paths": {
|
||||
"@fosscord/api*": ["./src/api/index"],
|
||||
"@fosscord/gateway*": ["./src/gateway/index"],
|
||||
"@fosscord/cdn*": ["./src/cdn/index"],
|
||||
"@fosscord/util*": ["./src/util/index"],
|
||||
"@fosscord/webrtc*": ["./src/webrtc/index"]
|
||||
},
|
||||
"target": "es5",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true,
|
||||
"dynamicImport": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": false,
|
||||
"decoratorMetadata": true
|
||||
}
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: build",
|
||||
"detail": "node scripts/build.js swc"
|
||||
}
|
||||
]
|
||||
}
|
||||
74
assets/inline-plugins/webrtc.js
Normal file
74
assets/inline-plugins/webrtc.js
Normal file
@@ -0,0 +1,74 @@
|
||||
(this.webpackChunkdiscord_app = this.webpackChunkdiscord_app || []).push([
|
||||
[[228974]],
|
||||
{
|
||||
632540: (module, exports, req) => {
|
||||
window.find = (filter, options = {}) => {
|
||||
const { cacheOnly = false } = options;
|
||||
for (let i in req.c) {
|
||||
if (req.c.hasOwnProperty(i)) {
|
||||
let m = req.c[i].exports;
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
|
||||
if (m && filter(m)) return m;
|
||||
}
|
||||
}
|
||||
if (cacheOnly) {
|
||||
console.warn("Cannot find loaded module in cache");
|
||||
return null;
|
||||
}
|
||||
console.warn("Cannot find loaded module in cache. Loading all modules may have unexpected side effects");
|
||||
for (let i = 0; i < req.m.length; ++i) {
|
||||
let m = req(i);
|
||||
if (m && m.__esModule && m.default && filter(m.default)) return m.default;
|
||||
if (m && filter(m)) return m;
|
||||
}
|
||||
console.warn("Cannot find module");
|
||||
return null;
|
||||
};
|
||||
window.findByUniqueProperties = (propNames, options) =>
|
||||
find((module) => propNames.every((prop) => module[prop] !== undefined), options);
|
||||
window.findByDisplayName = (displayName, options) => find((module) => module.displayName === displayName, options);
|
||||
window.req = req;
|
||||
|
||||
init();
|
||||
}
|
||||
},
|
||||
(t) => t(632540)
|
||||
]);
|
||||
|
||||
function retry(callback) {
|
||||
return new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
const mod = callback();
|
||||
if (!mod) return;
|
||||
|
||||
clearInterval(interval);
|
||||
resolve(mod);
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const SDP = await retry(() => findByUniqueProperties(["truncateSDP"]));
|
||||
const StringManipulator = findByUniqueProperties(["uniq"]);
|
||||
|
||||
const truncateSDP = SDP.truncateSDP;
|
||||
SDP.truncateSDP = (e) => {
|
||||
const result = truncateSDP(e);
|
||||
const i = result.codecs.find((x) => x.name === "VP8");
|
||||
const a = new RegExp("^a=ice|a=extmap|opus|VP8|fingerprint|" + i?.rtxPayloadType + " rtx", "i");
|
||||
return {
|
||||
sdp: StringManipulator(e)
|
||||
.split(/\r\n/)
|
||||
.filter(function (e) {
|
||||
return a.test(e);
|
||||
})
|
||||
.uniq()
|
||||
.join("\n"),
|
||||
codecs: result.codecs
|
||||
};
|
||||
};
|
||||
// SDP.generateUnifiedSessionDescription = (e) => {
|
||||
// console.log(e);
|
||||
// return new RTCSessionDescription({ sdp: e.baseSDP.replace(/sendonly/g, "recvonly"), type: "answer" });
|
||||
// };
|
||||
}
|
||||
420
assets/sdp.json
Normal file
420
assets/sdp.json
Normal file
@@ -0,0 +1,420 @@
|
||||
{
|
||||
"version": 0,
|
||||
"streams": [],
|
||||
"medias": [
|
||||
{
|
||||
"id": "0",
|
||||
"type": "audio",
|
||||
"direction": "sendrecv",
|
||||
"codecs": [
|
||||
{
|
||||
"codec": "opus",
|
||||
"type": 111,
|
||||
"channels": 2,
|
||||
"params": {
|
||||
"minptime": "10",
|
||||
"useinbandfec": "1"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "transport-cc"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extensions": {
|
||||
"1": "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
|
||||
"2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
|
||||
"3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
"4": "urn:ietf:params:rtp-hdrext:sdes:mid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"type": "video",
|
||||
"direction": "sendrecv",
|
||||
"codecs": [
|
||||
{
|
||||
"codec": "VP8",
|
||||
"type": 96,
|
||||
"rtx": 97,
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "VP9",
|
||||
"type": 98,
|
||||
"rtx": 99,
|
||||
"params": {
|
||||
"profile-id": "0"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "VP9",
|
||||
"type": 100,
|
||||
"rtx": 101,
|
||||
"params": {
|
||||
"profile-id": "2"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "VP9",
|
||||
"type": 102,
|
||||
"rtx": 122,
|
||||
"params": {
|
||||
"profile-id": "1"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 127,
|
||||
"rtx": 121,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "1",
|
||||
"profile-level-id": "42001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 125,
|
||||
"rtx": 107,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "0",
|
||||
"profile-level-id": "42001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 108,
|
||||
"rtx": 109,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "1",
|
||||
"profile-level-id": "42e01f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 124,
|
||||
"rtx": 120,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "0",
|
||||
"profile-level-id": "42e01f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 123,
|
||||
"rtx": 119,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "1",
|
||||
"profile-level-id": "4d001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 35,
|
||||
"rtx": 36,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "0",
|
||||
"profile-level-id": "4d001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 37,
|
||||
"rtx": 38,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "1",
|
||||
"profile-level-id": "f4001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 39,
|
||||
"rtx": 40,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "0",
|
||||
"profile-level-id": "f4001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"codec": "H264",
|
||||
"type": 114,
|
||||
"rtx": 115,
|
||||
"params": {
|
||||
"level-asymmetry-allowed": "1",
|
||||
"packetization-mode": "1",
|
||||
"profile-level-id": "64001f"
|
||||
},
|
||||
"rtcpfbs": [
|
||||
{
|
||||
"id": "goog-remb"
|
||||
},
|
||||
{
|
||||
"id": "transport-cc"
|
||||
},
|
||||
{
|
||||
"id": "ccm",
|
||||
"params": ["fir"]
|
||||
},
|
||||
{
|
||||
"id": "nack"
|
||||
},
|
||||
{
|
||||
"id": "nack",
|
||||
"params": ["pli"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extensions": {
|
||||
"2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
|
||||
"3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
"4": "urn:ietf:params:rtp-hdrext:sdes:mid",
|
||||
"5": "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
|
||||
"6": "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type",
|
||||
"7": "http://www.webrtc.org/experiments/rtp-hdrext/video-timing",
|
||||
"8": "http://www.webrtc.org/experiments/rtp-hdrext/color-space",
|
||||
"10": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
|
||||
"11": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
|
||||
"13": "urn:3gpp:video-orientation",
|
||||
"14": "urn:ietf:params:rtp-hdrext:toffset"
|
||||
}
|
||||
}
|
||||
],
|
||||
"candidates": []
|
||||
}
|
||||
@@ -16,3 +16,5 @@
|
||||
| DB_VERBOSE | any | Log database queries, enabled if defined |
|
||||
| DB_MIGRATE | any | Exit fosscord after connecting to and migrating database, used internally |
|
||||
| LOG_INVALID_BODY | any | Log request method, path and body if invalid |
|
||||
| WEBRTC_PORT_RANGE | min-max | Minimum and maximum port for the WebRTC server |
|
||||
| PUBLIC_IP | string | Public ip of the WebRTC server |
|
||||
|
||||
5
plugins/example-plugin/build.sh
Executable file
5
plugins/example-plugin/build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#rm -rf dist/
|
||||
#mkdir dist
|
||||
rm -rfv *.js *.js.map
|
||||
ln -s ../../bundle/node_modules node_modules
|
||||
tsc -p .
|
||||
7
plugins/example-plugin/index.ts
Normal file
7
plugins/example-plugin/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Plugin } from "@fosscord/util";
|
||||
|
||||
export default class TestPlugin extends Plugin {
|
||||
onPluginLoaded(): void {
|
||||
console.log("Hello from test plugin! IT WORKS!!!!!!!");
|
||||
}
|
||||
}
|
||||
7
plugins/example-plugin/plugin.json
Normal file
7
plugins/example-plugin/plugin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": "example-plugin",
|
||||
"name": "Fosscord example plugin",
|
||||
"authors": ["The Arcane Brony"],
|
||||
"repository": "https://github.com/fosscord/fosscord-server",
|
||||
"license": ""
|
||||
}
|
||||
41
scripts/db_migrations.sh
Normal file
41
scripts/db_migrations.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -z "$1" ]
|
||||
then
|
||||
FILENAME="$1"
|
||||
echo "Using filename: $FILENAME"
|
||||
else
|
||||
read -p "Enter migration filename: " FILENAME
|
||||
fi
|
||||
|
||||
[ -f ".env" ] && (
|
||||
mv .env .env.tmp 2>/dev/null
|
||||
source .env.tmp 2>/dev/null
|
||||
)
|
||||
npm run build clean logerrors pretty-errors
|
||||
|
||||
make_migration() {
|
||||
echo "Creating migrations for $2"
|
||||
mkdir "src/util/migrations/$2" 2>/dev/null
|
||||
# npm run build clean logerrors pretty-errors
|
||||
THREADS=1 DATABASE="$1" DB_MIGRATE=a npm run start:bundle
|
||||
THREADS=1 DATABASE="$1" DB_MIGRATE=a npx typeorm-ts-node-commonjs migration:generate "src/util/migrations/$2/$FILENAME" -d src/util/util/Database.ts -p
|
||||
#npm run build clean logerrors pretty-errors
|
||||
#THREADS=1 DATABASE="$1" DB_MIGRATE=a npm run start:bundle
|
||||
}
|
||||
|
||||
npm i sqlite3
|
||||
make_migration "database.db" "sqlite"
|
||||
|
||||
[ -z "$FC_DB_POSTGRES" ] || (
|
||||
npm i pg
|
||||
make_migration "$FC_DB_POSTGRES" "postgres"
|
||||
)
|
||||
|
||||
[ -z "$FC_DB_MARIADB" ] || (
|
||||
npm i mysql2
|
||||
make_migration "$FC_DB_MARIADB" "mariadb"
|
||||
)
|
||||
|
||||
[ -f ".env.tmp" ] && mv .env.tmp .env 2>/dev/null
|
||||
|
||||
172
scripts/webrtc/browser_client.ini
Normal file
172
scripts/webrtc/browser_client.ini
Normal file
@@ -0,0 +1,172 @@
|
||||
v=0
|
||||
o=- 2111259993848660015 2 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1
|
||||
a=extmap-allow-mixed
|
||||
a=msid-semantic: WMS MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl
|
||||
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:KRoB
|
||||
a=ice-pwd:k44fKSe3PpPYjljQaY2YObSu
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 E2:B8:09:30:0F:80:7F:97:82:2B:A9:97:AD:67:8B:D1:C8:48:99:5D:26:ED:2C:B2:4D:41:E6:5E:62:FC:99:C1
|
||||
a=setup:actpass
|
||||
a=mid:0
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendrecv
|
||||
a=msid:MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl 8314115b-9e3f-49eb-9183-22146b9bfe2c
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:103 ISAC/16000
|
||||
a=rtpmap:104 ISAC/32000
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:106 CN/32000
|
||||
a=rtpmap:105 CN/16000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:112 telephone-event/32000
|
||||
a=rtpmap:113 telephone-event/16000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:1910637016 cname:211W6KVUXfLFKBkS
|
||||
a=ssrc:1910637016 msid:MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl 8314115b-9e3f-49eb-9183-22146b9bfe2c
|
||||
m=video 9 UDP/TLS/RTP/SAVPF 96 97 127 121 125 107 108 109 124 120 123 119 35 36 41 42 98 99 100 101 114 115 116 117 118
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:KRoB
|
||||
a=ice-pwd:k44fKSe3PpPYjljQaY2YObSu
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 E2:B8:09:30:0F:80:7F:97:82:2B:A9:97:AD:67:8B:D1:C8:48:99:5D:26:ED:2C:B2:4D:41:E6:5E:62:FC:99:C1
|
||||
a=setup:actpass
|
||||
a=mid:1
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=sendrecv
|
||||
a=msid:MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl 3cc31637-6e0f-4b57-8480-35c04f86bba3
|
||||
a=rtcp-mux
|
||||
a=rtcp-rsize
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtcp-fb:96 goog-remb
|
||||
a=rtcp-fb:96 transport-cc
|
||||
a=rtcp-fb:96 ccm fir
|
||||
a=rtcp-fb:96 nack
|
||||
a=rtcp-fb:96 nack pli
|
||||
a=rtpmap:97 rtx/90000
|
||||
a=fmtp:97 apt=96
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:121 apt=127
|
||||
a=rtpmap:125 H264/90000
|
||||
a=rtcp-fb:125 goog-remb
|
||||
a=rtcp-fb:125 transport-cc
|
||||
a=rtcp-fb:125 ccm fir
|
||||
a=rtcp-fb:125 nack
|
||||
a=rtcp-fb:125 nack pli
|
||||
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
|
||||
a=rtpmap:107 rtx/90000
|
||||
a=fmtp:107 apt=125
|
||||
a=rtpmap:108 H264/90000
|
||||
a=rtcp-fb:108 goog-remb
|
||||
a=rtcp-fb:108 transport-cc
|
||||
a=rtcp-fb:108 ccm fir
|
||||
a=rtcp-fb:108 nack
|
||||
a=rtcp-fb:108 nack pli
|
||||
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=rtpmap:109 rtx/90000
|
||||
a=fmtp:109 apt=108
|
||||
a=rtpmap:124 H264/90000
|
||||
a=rtcp-fb:124 goog-remb
|
||||
a=rtcp-fb:124 transport-cc
|
||||
a=rtcp-fb:124 ccm fir
|
||||
a=rtcp-fb:124 nack
|
||||
a=rtcp-fb:124 nack pli
|
||||
a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
|
||||
a=rtpmap:120 rtx/90000
|
||||
a=fmtp:120 apt=124
|
||||
a=rtpmap:123 H264/90000
|
||||
a=rtcp-fb:123 goog-remb
|
||||
a=rtcp-fb:123 transport-cc
|
||||
a=rtcp-fb:123 ccm fir
|
||||
a=rtcp-fb:123 nack
|
||||
a=rtcp-fb:123 nack pli
|
||||
a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
|
||||
a=rtpmap:119 rtx/90000
|
||||
a=fmtp:119 apt=123
|
||||
a=rtpmap:35 H264/90000
|
||||
a=rtcp-fb:35 goog-remb
|
||||
a=rtcp-fb:35 transport-cc
|
||||
a=rtcp-fb:35 ccm fir
|
||||
a=rtcp-fb:35 nack
|
||||
a=rtcp-fb:35 nack pli
|
||||
a=fmtp:35 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
|
||||
a=rtpmap:36 rtx/90000
|
||||
a=fmtp:36 apt=35
|
||||
a=rtpmap:41 AV1/90000
|
||||
a=rtcp-fb:41 goog-remb
|
||||
a=rtcp-fb:41 transport-cc
|
||||
a=rtcp-fb:41 ccm fir
|
||||
a=rtcp-fb:41 nack
|
||||
a=rtcp-fb:41 nack pli
|
||||
a=rtpmap:42 rtx/90000
|
||||
a=fmtp:42 apt=41
|
||||
a=rtpmap:98 VP9/90000
|
||||
a=rtcp-fb:98 goog-remb
|
||||
a=rtcp-fb:98 transport-cc
|
||||
a=rtcp-fb:98 ccm fir
|
||||
a=rtcp-fb:98 nack
|
||||
a=rtcp-fb:98 nack pli
|
||||
a=fmtp:98 profile-id=0
|
||||
a=rtpmap:99 rtx/90000
|
||||
a=fmtp:99 apt=98
|
||||
a=rtpmap:100 VP9/90000
|
||||
a=rtcp-fb:100 goog-remb
|
||||
a=rtcp-fb:100 transport-cc
|
||||
a=rtcp-fb:100 ccm fir
|
||||
a=rtcp-fb:100 nack
|
||||
a=rtcp-fb:100 nack pli
|
||||
a=fmtp:100 profile-id=2
|
||||
a=rtpmap:101 rtx/90000
|
||||
a=fmtp:101 apt=100
|
||||
a=rtpmap:114 H264/90000
|
||||
a=rtcp-fb:114 goog-remb
|
||||
a=rtcp-fb:114 transport-cc
|
||||
a=rtcp-fb:114 ccm fir
|
||||
a=rtcp-fb:114 nack
|
||||
a=rtcp-fb:114 nack pli
|
||||
a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
|
||||
a=rtpmap:115 rtx/90000
|
||||
a=fmtp:115 apt=114
|
||||
a=rtpmap:116 red/90000
|
||||
a=rtpmap:117 rtx/90000
|
||||
a=fmtp:117 apt=116
|
||||
a=rtpmap:118 ulpfec/90000
|
||||
a=ssrc-group:FID 1863819511 3594276773
|
||||
a=ssrc:1863819511 cname:211W6KVUXfLFKBkS
|
||||
a=ssrc:1863819511 msid:MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl 3cc31637-6e0f-4b57-8480-35c04f86bba3
|
||||
a=ssrc:3594276773 cname:211W6KVUXfLFKBkS
|
||||
a=ssrc:3594276773 msid:MwWzwzG0U0kdKhH0cMlNMyuU9Q7BmjjLYpQl 3cc31637-6e0f-4b57-8480-35c04f86bba3
|
||||
19
scripts/webrtc/discord_client.ini
Normal file
19
scripts/webrtc/discord_client.ini
Normal file
@@ -0,0 +1,19 @@
|
||||
a=extmap-allow-mixed
|
||||
a=ice-ufrag:Ets9
|
||||
a=ice-pwd:CKGC4jufinWBOiKgn9iUji0l
|
||||
a=ice-options:trickle
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtpmap:97 rtx/90000
|
||||
2069
scripts/webrtc/discord_client_large.ini
Normal file
2069
scripts/webrtc/discord_client_large.ini
Normal file
File diff suppressed because it is too large
Load Diff
47
scripts/webrtc/discord_client_reduced.ini
Normal file
47
scripts/webrtc/discord_client_reduced.ini
Normal file
@@ -0,0 +1,47 @@
|
||||
v=0
|
||||
o=- 2875442641301787241 4 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
||||
a=extmap-allow-mixed
|
||||
a=msid-semantic: WMS
|
||||
m=audio 58625 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
|
||||
c=IN IP4 192.168.178.21
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=candidate:2188944245 1 udp 2122262783 2a02:2455:16e0:b800:2948:7f13:404e:79d9 58131 typ host generation 0 network-id 2 network-cost 10
|
||||
a=candidate:1967680543 1 udp 2122194687 192.168.178.21 58625 typ host generation 0 network-id 1 network-cost 10
|
||||
a=candidate:3439026053 1 tcp 1518283007 2a02:2455:16e0:b800:2948:7f13:404e:79d9 9 typ host tcptype active generation 0 network-id 2 network-cost 10
|
||||
a=candidate:1003196655 1 tcp 1518214911 192.168.178.21 9 typ host tcptype active generation 0 network-id 1 network-cost 10
|
||||
a=ice-ufrag:4vO7
|
||||
a=ice-pwd:SFNoE73NpBg3ABrSge26wJwo
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 2C:E3:F2:AE:F3:5B:69:32:A9:14:33:40:B3:A8:25:BE:67:A2:58:94:65:0C:9D:55:87:28:94:B6:DC:81:8F:63
|
||||
a=setup:actpass
|
||||
a=mid:0
|
||||
b=AS:96
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendrecv
|
||||
a=msid:- 7ffd5d58-a23a-4d2e-ba03-c5c567fc5608
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:103 ISAC/16000
|
||||
a=rtpmap:104 ISAC/32000
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:106 CN/32000
|
||||
a=rtpmap:105 CN/16000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:112 telephone-event/32000
|
||||
a=rtpmap:113 telephone-event/16000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:119334120 cname:rVy8IByaD2Z8419E
|
||||
a=ssrc:119334120 msid:- 7ffd5d58-a23a-4d2e-ba03-c5c567fc5608
|
||||
8
scripts/webrtc/discord_server.ini
Normal file
8
scripts/webrtc/discord_server.ini
Normal file
@@ -0,0 +1,8 @@
|
||||
m=audio 50025 ICE/SDP
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtcp:50025
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
468
scripts/webrtc/discord_server_large.ini
Normal file
468
scripts/webrtc/discord_server_large.ini
Normal file
@@ -0,0 +1,468 @@
|
||||
v=0
|
||||
o=- 1420070400000 0 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=msid-semantic: WMS *
|
||||
a=group:BUNDLE 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:0
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:1
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:2
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:3
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:4
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:5
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:6
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:7
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:8
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:9
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:10
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=audio 50025 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fmtp:111 minptime=10;useinbandfec=1;usedtx=1
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=setup:passive
|
||||
a=mid:11
|
||||
a=maxptime:60
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:12
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:13
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:14
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:15
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:16
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:17
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:18
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:19
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:20
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
m=video 50025 UDP/TLS/RTP/SAVPF 127 121
|
||||
c=IN IP4 66.22.238.141
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtpmap:121 rtx/90000
|
||||
a=fmtp:127 x-google-max-bitrate=2500;level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=fmtp:121 apt=127
|
||||
a=rtcp:50025
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=setup:passive
|
||||
a=mid:21
|
||||
a=inactive
|
||||
a=ice-ufrag:VHK9
|
||||
a=ice-pwd:FNTPHMd2XNegYm+xwcWJWK
|
||||
a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87
|
||||
a=candidate:1 1 UDP 4261412862 66.22.238.141 50025 typ host
|
||||
a=rtcp-mux
|
||||
33
scripts/webrtc/sdp.js
Normal file
33
scripts/webrtc/sdp.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const { writeFileSync } = require("fs");
|
||||
const SemanticSDP = require("semantic-sdp");
|
||||
var data = `m=audio
|
||||
a=extmap-allow-mixed
|
||||
a=ice-ufrag:Ets9
|
||||
a=ice-pwd:CKGC4jufinWBOiKgn9iUji0l
|
||||
a=ice-options:trickle
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=fingerprint:sha-256 2C:E3:F2:AE:F3:5B:69:32:A9:14:33:40:B3:A8:25:BE:67:A2:58:94:65:0C:9D:55:87:28:94:B6:DC:81:8F:63
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtpmap:97 rtx/90000
|
||||
`;
|
||||
const sdp = SemanticSDP.SDPInfo.parse(data);
|
||||
|
||||
writeFileSync("sdp.json", JSON.stringify(sdp.plain(), null, 2));
|
||||
|
||||
const x = require("./sdp.json");
|
||||
|
||||
const y = SemanticSDP.SDPInfo.expand(x);
|
||||
|
||||
console.log(y);
|
||||
@@ -5,6 +5,7 @@ import * as Api from "@fosscord/api";
|
||||
import { CDNServer } from "@fosscord/cdn";
|
||||
import * as Gateway from "@fosscord/gateway";
|
||||
import { Config, getOrInitialiseDatabase } from "@fosscord/util";
|
||||
import * as WebRTC from "@fosscord/webrtc";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import * as Tracing from "@sentry/tracing";
|
||||
import express from "express";
|
||||
@@ -18,12 +19,10 @@ const port = Number(process.env.PORT) || 3001;
|
||||
const production = process.env.NODE_ENV == "development" ? false : true;
|
||||
server.on("request", app);
|
||||
|
||||
// @ts-ignore
|
||||
const api = new Api.FosscordServer({ server, port, production, app });
|
||||
// @ts-ignore
|
||||
const cdn = new CDNServer({ server, port, production, app });
|
||||
// @ts-ignore
|
||||
const gateway = new Gateway.Server({ server, port, production });
|
||||
const webrtc = new WebRTC.Server({ server, port, production });
|
||||
|
||||
//this is what has been added for the /stop API route
|
||||
process.on("SIGTERM", () => {
|
||||
@@ -52,7 +51,7 @@ async function main() {
|
||||
app.use(Sentry.Handlers.requestHandler());
|
||||
app.use(Sentry.Handlers.tracingHandler());
|
||||
}
|
||||
await Promise.all([api.start(), cdn.start(), gateway.start()]);
|
||||
await Promise.all([api.start(), cdn.start(), gateway.start(), webrtc.start()]);
|
||||
if (Config.get().sentry.enabled) {
|
||||
app.use(Sentry.Handlers.errorHandler());
|
||||
app.use(function onError(err: any, req: any, res: any, next: any) {
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) {
|
||||
|
||||
for (let ar of availableRegions) {
|
||||
//TODO the endpoint location should be saved in the database if not already present to prevent IPAnalysis call
|
||||
const dist = distanceBetweenLocations(clientIpAnalysis, ar.location || (await IPAnalysis(ar.endpoint)));
|
||||
const dist = distanceBetweenLocations(clientIpAnalysis, ar.location || (await IPAnalysis(ar.endpoint!)));
|
||||
|
||||
if (dist < min) {
|
||||
min = dist;
|
||||
|
||||
@@ -23,6 +23,7 @@ export class Server {
|
||||
}
|
||||
|
||||
this.server.on("upgrade", (request, socket, head) => {
|
||||
if (request.url?.includes("voice")) return;
|
||||
// @ts-ignore
|
||||
this.ws.handleUpgrade(request, socket, head, (socket) => {
|
||||
this.ws.emit("connection", socket, request);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { WebSocket } from "@fosscord/gateway";
|
||||
import { emitEvent, PresenceUpdateEvent, PrivateSessionProjection, Session, SessionsReplace, User } from "@fosscord/util";
|
||||
|
||||
export async function Close(this: WebSocket, code: number, reason: string) {
|
||||
console.log("[WebSocket] closed", code, reason);
|
||||
console.log("[Gateway] closed", code, reason);
|
||||
if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout);
|
||||
if (this.readyTimeout) clearTimeout(this.readyTimeout);
|
||||
this.deflate?.close();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IncomingMessage } from "http";
|
||||
import { URL } from "url";
|
||||
import WS from "ws";
|
||||
import { createDeflate } from "zlib";
|
||||
import { CLOSECODES, OPCODES } from "../util/Constants";
|
||||
import { CloseCodes, GatewayOPCodes } from "../util/Constants";
|
||||
import { setHeartbeat } from "../util/Heartbeat";
|
||||
import { Send } from "../util/Send";
|
||||
import { Close } from "./Close";
|
||||
@@ -35,7 +35,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In
|
||||
"pong",
|
||||
"unexpected-response"
|
||||
].forEach((x) => {
|
||||
socket.on(x, (y) => console.log(x, y));
|
||||
socket.on(x, (y) => console.log("[Gateway]", x, y));
|
||||
});
|
||||
|
||||
console.log(`[Gateway] Connections: ${this.clients.size}`);
|
||||
@@ -47,17 +47,17 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In
|
||||
if (socket.encoding === "etf" && erlpack) {
|
||||
throw new Error("Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'");
|
||||
}
|
||||
return socket.close(CLOSECODES.Decode_error);
|
||||
return socket.close(CloseCodes.Decode_error);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
socket.version = Number(searchParams.get("version")) || 8;
|
||||
if (socket.version != 8) return socket.close(CLOSECODES.Invalid_API_version);
|
||||
if (socket.version != 8) return socket.close(CloseCodes.Invalid_API_version);
|
||||
|
||||
// @ts-ignore
|
||||
socket.compress = searchParams.get("compress") || "";
|
||||
if (socket.compress) {
|
||||
if (socket.compress !== "zlib-stream") return socket.close(CLOSECODES.Decode_error);
|
||||
if (socket.compress !== "zlib-stream") return socket.close(CloseCodes.Decode_error);
|
||||
socket.deflate = createDeflate({ chunkSize: 65535 });
|
||||
socket.deflate.on("data", (chunk) => socket.send(chunk));
|
||||
}
|
||||
@@ -69,18 +69,18 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In
|
||||
|
||||
setHeartbeat(socket);
|
||||
|
||||
socket.readyTimeout = setTimeout(() => {
|
||||
return socket.close(CloseCodes.Session_timed_out);
|
||||
}, 1000 * 30);
|
||||
|
||||
await Send(socket, {
|
||||
op: OPCODES.Hello,
|
||||
op: GatewayOPCodes.Hello,
|
||||
d: {
|
||||
heartbeat_interval: 1000 * 30
|
||||
}
|
||||
});
|
||||
|
||||
socket.readyTimeout = setTimeout(() => {
|
||||
return socket.close(CLOSECODES.Session_timed_out);
|
||||
}, 1000 * 30);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return socket.close(CLOSECODES.Unknown_error);
|
||||
return socket.close(CloseCodes.Unknown_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
import OPCodeHandlers from "../opcodes";
|
||||
import { check } from "../opcodes/instanceOf";
|
||||
import { CLOSECODES } from "../util/Constants";
|
||||
import { CloseCodes } from "../util/Constants";
|
||||
let erlpack: any;
|
||||
try {
|
||||
erlpack = require("@yukikaze-bot/erlpack");
|
||||
@@ -29,13 +29,13 @@ export async function Message(this: WebSocket, buffer: Buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.WS_VERBOSE) console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`);
|
||||
if (process.env.WS_VERBOSE) console.log(`[Gateway] Incomming message: ${JSON.stringify(data)}`);
|
||||
if (data.op !== 1) check.call(this, PayloadSchema, data);
|
||||
else {
|
||||
//custom validation for numbers, because heartbeat
|
||||
if (data.s || data.t || (typeof data.d !== "number" && data.d)) {
|
||||
console.log("Invalid heartbeat...");
|
||||
this.close(CLOSECODES.Decode_error);
|
||||
this.close(CloseCodes.Decode_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +44,14 @@ export async function Message(this: WebSocket, buffer: Buffer) {
|
||||
if (!OPCodeHandler) {
|
||||
console.error("[Gateway] Unkown opcode " + data.op);
|
||||
// TODO: if all opcodes are implemented comment this out:
|
||||
// this.close(CLOSECODES.Unknown_opcode);
|
||||
// this.close(CloseCodes.Unknown_opcode);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
return await OPCodeHandler.call(this, data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (!this.CLOSED && this.CLOSING) return this.close(CLOSECODES.Unknown_error);
|
||||
console.error("[Gateway]", error);
|
||||
if (!this.CLOSED && this.CLOSING) return this.close(CloseCodes.Unknown_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
RelationshipType
|
||||
} from "@fosscord/util";
|
||||
import { Channel as AMQChannel } from "amqplib";
|
||||
import { OPCODES } from "../util/Constants";
|
||||
import { GatewayOPCodes } from "../util/Constants";
|
||||
import { Send } from "../util/Send";
|
||||
|
||||
// TODO: close connection on Invalidated Token
|
||||
@@ -27,7 +27,7 @@ export function handlePresenceUpdate(this: WebSocket, { event, acknowledge, data
|
||||
acknowledge?.();
|
||||
if (event === EVENTEnum.PresenceUpdate) {
|
||||
return Send(this, {
|
||||
op: OPCODES.Dispatch,
|
||||
op: GatewayOPCodes.Dispatch,
|
||||
t: event,
|
||||
d: data,
|
||||
s: this.sequence++
|
||||
@@ -212,7 +212,7 @@ async function consume(this: WebSocket, opts: EventOpts) {
|
||||
}
|
||||
|
||||
Send(this, {
|
||||
op: OPCODES.Dispatch,
|
||||
op: GatewayOPCodes.Dispatch,
|
||||
t: event,
|
||||
d: data,
|
||||
s: this.sequence++
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
UserSettings
|
||||
} from "@fosscord/util";
|
||||
import { setupListener } from "../listener/listener";
|
||||
import { CLOSECODES, OPCODES } from "../util/Constants";
|
||||
import { CloseCodes, GatewayOPCodes } from "../util/Constants";
|
||||
import { Send } from "../util/Send";
|
||||
import { genSessionId } from "../util/SessionUtils";
|
||||
import { check } from "./instanceOf";
|
||||
@@ -46,7 +46,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
var { decoded } = await checkToken(identify.token, jwtSecret); // will throw an error if invalid
|
||||
} catch (error) {
|
||||
console.error("invalid token", error);
|
||||
return this.close(CLOSECODES.Authentication_failed);
|
||||
return this.close(CloseCodes.Authentication_failed);
|
||||
}
|
||||
this.user_id = decoded.id;
|
||||
|
||||
@@ -87,15 +87,15 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
Application.findOne({ where: { id: this.user_id } })
|
||||
]);
|
||||
|
||||
if (!user) return this.close(CLOSECODES.Authentication_failed);
|
||||
if (!user) return this.close(CloseCodes.Authentication_failed);
|
||||
if (!user.settings) {
|
||||
//settings may not exist after updating...
|
||||
user.settings = new UserSettings();
|
||||
user.settings.id = user.id;
|
||||
//await (user.settings as UserSettings).save();
|
||||
await user.settings.save();
|
||||
}
|
||||
|
||||
if (!identify.intents) identify.intents = "30064771071";
|
||||
if (!identify.intents) identify.intents = "0x6ffffffff";
|
||||
this.intents = new Intents(identify.intents);
|
||||
if (identify.shard) {
|
||||
this.shard_id = identify.shard[0];
|
||||
@@ -108,7 +108,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
this.shard_count <= 0
|
||||
) {
|
||||
console.log(identify.shard);
|
||||
return this.close(CLOSECODES.Invalid_shard);
|
||||
return this.close(CloseCodes.Invalid_shard);
|
||||
}
|
||||
}
|
||||
let users: PublicUser[] = [];
|
||||
@@ -130,7 +130,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
if (user.bot) {
|
||||
setTimeout(() => {
|
||||
Send(this, {
|
||||
op: OPCODES.Dispatch,
|
||||
op: GatewayOPCodes.Dispatch,
|
||||
t: EVENTEnum.GuildCreate,
|
||||
s: this.sequence++,
|
||||
d: guild
|
||||
@@ -223,7 +223,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
|
||||
const d: ReadyEventData = {
|
||||
v: 8,
|
||||
application: { id: application?.id ?? "", flags: application?.flags ?? 0 }, //TODO: check this code!
|
||||
application: { id: application?.id ?? "", flags: application?.flags ?? "" }, //TODO: check this code!
|
||||
user: privateUser,
|
||||
user_settings: user.settings,
|
||||
// @ts-ignore
|
||||
@@ -268,7 +268,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
|
||||
// TODO: send real proper data structure
|
||||
await Send(this, {
|
||||
op: OPCODES.Dispatch,
|
||||
op: GatewayOPCodes.Dispatch,
|
||||
t: EVENTEnum.Ready,
|
||||
s: this.sequence++,
|
||||
d
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { handlePresenceUpdate, Payload, WebSocket } from "@fosscord/gateway";
|
||||
import { getOrInitialiseDatabase, getPermission, LazyRequest, listenEvent, Member, Role } from "@fosscord/util";
|
||||
import { OPCODES } from "../util/Constants";
|
||||
import { GatewayOPCodes } from "../util/Constants";
|
||||
import { Send } from "../util/Send";
|
||||
import { check } from "./instanceOf";
|
||||
|
||||
@@ -129,7 +129,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
|
||||
});
|
||||
|
||||
return Send(this, {
|
||||
op: OPCODES.Dispatch,
|
||||
op: GatewayOPCodes.Dispatch,
|
||||
s: this.sequence++,
|
||||
t: "GUILD_MEMBER_LIST_UPDATE",
|
||||
d: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
|
||||
export function onRequestGuildMembers(this: WebSocket, data: Payload) {
|
||||
// return this.close(CLOSECODES.Unknown_error);
|
||||
// return this.close(CloseCodes.Unknown_error);
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ export async function onResume(this: WebSocket, data: Payload) {
|
||||
d: false
|
||||
});
|
||||
|
||||
// return this.close(CLOSECODES.Invalid_session);
|
||||
// return this.close(CloseCodes.Invalid_session);
|
||||
}
|
||||
|
||||
@@ -20,20 +20,16 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
check.call(this, VoiceStateUpdateSchema, data.d);
|
||||
const body = data.d as VoiceStateUpdateSchema;
|
||||
|
||||
if (body.guild_id == null) {
|
||||
console.log(`[Gateway] VoiceStateUpdate called with guild_id == null by user ${this.user_id}!`);
|
||||
return;
|
||||
}
|
||||
|
||||
let onlySettingsChanged = false;
|
||||
let voiceState: VoiceState;
|
||||
try {
|
||||
voiceState = await VoiceState.findOneOrFail({
|
||||
where: { user_id: this.user_id }
|
||||
});
|
||||
if (voiceState.session_id !== this.session_id && body.channel_id === null) {
|
||||
//Should we also check guild_id === null?
|
||||
//changing deaf or mute on a client that's not the one with the same session of the voicestate in the database should be ignored
|
||||
return;
|
||||
if (voiceState.session_id !== this.session_id) {
|
||||
// new session
|
||||
} else {
|
||||
if (voiceState.channel_id === body.channel_id) onlySettingsChanged = true;
|
||||
}
|
||||
|
||||
//If a user change voice channel between guild we should send a left event first
|
||||
@@ -70,7 +66,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
if (voiceState.session_id !== this.session_id) voiceState.token = genVoiceToken();
|
||||
voiceState.session_id = this.session_id;
|
||||
|
||||
const { id, ...newObj } = voiceState;
|
||||
const { id, token, ...newObj } = voiceState;
|
||||
|
||||
await Promise.all([
|
||||
voiceState.save(),
|
||||
@@ -82,7 +78,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
]);
|
||||
|
||||
//If it's null it means that we are leaving the channel and this event is not needed
|
||||
if (voiceState.channel_id !== null) {
|
||||
if (voiceState.channel_id !== null && !onlySettingsChanged) {
|
||||
const guild = await Guild.findOne({ where: { id: voiceState.guild_id } });
|
||||
const regions = Config.get().regions;
|
||||
let guildRegion: Region;
|
||||
@@ -95,11 +91,11 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
await emitEvent({
|
||||
event: "VOICE_SERVER_UPDATE",
|
||||
data: {
|
||||
token: voiceState.token,
|
||||
token: token,
|
||||
guild_id: voiceState.guild_id,
|
||||
endpoint: guildRegion.endpoint
|
||||
endpoint: guildRegion.endpoint ? guildRegion.endpoint + "/voice" : `localhost:${process.env.PORT || 3001}/voice`
|
||||
},
|
||||
guild_id: voiceState.guild_id
|
||||
user_id: this.user_id
|
||||
} as VoiceServerUpdateEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { WebSocket } from "@fosscord/gateway";
|
||||
import { instanceOf } from "@fosscord/util";
|
||||
import { CLOSECODES } from "../util/Constants";
|
||||
import { CloseCodes } from "../util/Constants";
|
||||
|
||||
export function check(this: WebSocket, schema: any, data: any) {
|
||||
try {
|
||||
@@ -12,7 +12,7 @@ export function check(this: WebSocket, schema: any, data: any) {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// invalid payload
|
||||
this.close(CLOSECODES.Decode_error);
|
||||
this.close(CloseCodes.Decode_error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export enum OPCODES {
|
||||
import { VoiceOPCodes } from "@fosscord/webrtc";
|
||||
|
||||
export enum GatewayOPCodes {
|
||||
Dispatch = 0,
|
||||
Heartbeat = 1,
|
||||
Identify = 2,
|
||||
@@ -22,28 +24,59 @@ export enum OPCODES {
|
||||
Stream_Watch = 20,
|
||||
Stream_Ping = 21,
|
||||
Stream_Set_Paused = 22,
|
||||
Request_Application_Commands = 24
|
||||
Request_Application_Commands = 24,
|
||||
Embedded_Activity_Launch = 25,
|
||||
Embedded_Activity_Close = 26,
|
||||
Embedded_Activity_Update = 27,
|
||||
Request_Forum_Unreads = 28,
|
||||
Remote_Command = 29
|
||||
}
|
||||
export enum CLOSECODES {
|
||||
|
||||
export enum GatewayOPCodes {
|
||||
DISPATCH = 0,
|
||||
HEARTBEAT = 1,
|
||||
IDENTIFY = 2,
|
||||
PRESENCE_UPDATE = 3,
|
||||
VOICE_STATE_UPDATE = 4,
|
||||
VOICE_SERVER_PING = 5,
|
||||
RESUME = 6,
|
||||
RECONNECT = 7,
|
||||
REQUEST_GUILD_MEMBERS = 8,
|
||||
INVALID_SESSION = 9,
|
||||
HELLO = 10,
|
||||
HEARTBEAT_ACK = 11,
|
||||
CALL_CONNECT = 13,
|
||||
GUILD_SUBSCRIPTIONS = 14,
|
||||
LOBBY_CONNECT = 15,
|
||||
LOBBY_DISCONNECT = 16,
|
||||
LOBBY_VOICE_STATES_UPDATE = 17,
|
||||
STREAM_CREATE = 18,
|
||||
STREAM_DELETE = 19,
|
||||
STREAM_WATCH = 20,
|
||||
STREAM_PING = 21,
|
||||
STREAM_SET_PAUSED = 22
|
||||
}
|
||||
|
||||
export enum CloseCodes {
|
||||
Unknown_error = 4000,
|
||||
Unknown_opcode,
|
||||
Decode_error,
|
||||
Not_authenticated,
|
||||
Authentication_failed,
|
||||
Already_authenticated,
|
||||
Invalid_session,
|
||||
Invalid_seq,
|
||||
Rate_limited,
|
||||
Session_timed_out,
|
||||
Invalid_shard,
|
||||
Sharding_required,
|
||||
Invalid_API_version,
|
||||
Invalid_intent,
|
||||
Disallowed_intent
|
||||
Unknown_opcode = 4001,
|
||||
Decode_error = 4002,
|
||||
Not_authenticated = 4003,
|
||||
Authentication_failed = 4004,
|
||||
Already_authenticated = 4005,
|
||||
Invalid_session = 4006,
|
||||
Invalid_seq = 4007,
|
||||
Rate_limited = 4008,
|
||||
Session_timed_out = 4009,
|
||||
Invalid_shard = 4010,
|
||||
Sharding_required = 4011,
|
||||
Invalid_API_version = 4012,
|
||||
Invalid_intent = 4013,
|
||||
Disallowed_intent = 4014
|
||||
}
|
||||
|
||||
export interface Payload {
|
||||
op: OPCODES;
|
||||
op: GatewayOPCodes | VoiceOPCodes;
|
||||
d?: any;
|
||||
s?: number;
|
||||
t?: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CLOSECODES } from "./Constants";
|
||||
import { CloseCodes } from "./Constants";
|
||||
import { WebSocket } from "./WebSocket";
|
||||
|
||||
// TODO: make heartbeat timeout configurable
|
||||
@@ -6,6 +6,6 @@ export function setHeartbeat(socket: WebSocket) {
|
||||
if (socket.heartbeatTimeout) clearTimeout(socket.heartbeatTimeout);
|
||||
|
||||
socket.heartbeatTimeout = setTimeout(() => {
|
||||
return socket.close(CLOSECODES.Session_timed_out);
|
||||
return socket.close(CloseCodes.Session_timed_out);
|
||||
}, 1000 * 45);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ try {
|
||||
import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
|
||||
export async function Send(socket: WebSocket, data: Payload) {
|
||||
if (process.env.WS_VERBOSE) console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`);
|
||||
if (process.env.WS_VERBOSE) console.log(`[Gateway] Outgoing message: ${JSON.stringify(data)}`);
|
||||
let buffer: Buffer | string;
|
||||
if (socket.encoding === "etf") buffer = erlpack.pack(data);
|
||||
// TODO: encode circular object
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Intents, Permissions } from "@fosscord/util";
|
||||
import { Client } from "@fosscord/webrtc";
|
||||
import WS from "ws";
|
||||
import { Deflate } from "zlib";
|
||||
|
||||
@@ -19,4 +20,5 @@ export interface WebSocket extends WS {
|
||||
events: Record<string, Function>;
|
||||
member_events: Record<string, Function>;
|
||||
listen_options: any;
|
||||
client: Client;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export class RegionConfiguration {
|
||||
{
|
||||
id: "fosscord",
|
||||
name: "Fosscord",
|
||||
endpoint: "127.0.0.1:3004",
|
||||
endpoint: undefined,
|
||||
vip: false,
|
||||
custom: false,
|
||||
deprecated: false
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { URL } from "url";
|
||||
import { deleteFile } from "../util/cdn";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Message } from "./Message";
|
||||
|
||||
@Entity("attachments")
|
||||
export class Attachment extends BaseClass {
|
||||
@@ -34,7 +36,7 @@ export class Attachment extends BaseClass {
|
||||
@ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
message: import("./Message").Message;
|
||||
message: Relation<Message>;
|
||||
|
||||
@BeforeRemove()
|
||||
onDelete() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { ChannelPermissionOverwrite } from "./Channel";
|
||||
import { User } from "./User";
|
||||
@@ -97,7 +98,7 @@ export enum AuditLogEvents {
|
||||
export class AuditLog extends BaseClass {
|
||||
@JoinColumn({ name: "target_id" })
|
||||
@ManyToOne(() => User)
|
||||
target?: User;
|
||||
target?: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((auditlog: AuditLog) => auditlog.user)
|
||||
@@ -105,7 +106,7 @@ export class AuditLog extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "user_id" })
|
||||
@ManyToOne(() => User, (user: User) => user.id)
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column({ type: "int" })
|
||||
action_type: AuditLogEvents;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { User } from "./User";
|
||||
export class BackupCode extends BaseClass {
|
||||
@JoinColumn({ name: "user_id" })
|
||||
@ManyToOne(() => User, { onDelete: "CASCADE" })
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column()
|
||||
code: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
import { User } from "./User";
|
||||
@@ -13,7 +14,7 @@ export class Ban extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((ban: Ban) => ban.guild)
|
||||
@@ -23,7 +24,7 @@ export class Ban extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((ban: Ban) => ban.executor)
|
||||
@@ -31,7 +32,7 @@ export class Ban extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "executor_id" })
|
||||
@ManyToOne(() => User)
|
||||
executor: User;
|
||||
executor: Relation<User>;
|
||||
|
||||
@Column()
|
||||
ip: string;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class Channel extends BaseClass {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
recipients?: Recipient[];
|
||||
recipients?: Relation<Recipient[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
last_message_id: string;
|
||||
@@ -69,7 +69,7 @@ export class Channel extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((channel: Channel) => channel.parent)
|
||||
@@ -77,7 +77,7 @@ export class Channel extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "parent_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
parent?: Channel;
|
||||
parent?: Relation<Channel>;
|
||||
|
||||
// for group DMs and owned custom channel types
|
||||
@Column({ nullable: true })
|
||||
@@ -86,7 +86,7 @@ export class Channel extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "owner_id" })
|
||||
@ManyToOne(() => User)
|
||||
owner: User;
|
||||
owner: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
last_pin_timestamp?: number;
|
||||
@@ -112,9 +112,6 @@ export class Channel extends BaseClass {
|
||||
@Column({ nullable: true })
|
||||
nsfw?: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
rate_limit_per_user?: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
topic?: string;
|
||||
|
||||
@@ -122,7 +119,7 @@ export class Channel extends BaseClass {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
invites?: Invite[];
|
||||
invites?: Relation<Invite[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
retention_policy_id?: string;
|
||||
@@ -131,25 +128,25 @@ export class Channel extends BaseClass {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
messages?: Message[];
|
||||
messages?: Relation<Message[]>;
|
||||
|
||||
@OneToMany(() => VoiceState, (voice_state: VoiceState) => voice_state.channel, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
voice_states?: VoiceState[];
|
||||
voice_states?: Relation<VoiceState[]>;
|
||||
|
||||
@OneToMany(() => ReadState, (read_state: ReadState) => read_state.channel, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
read_states?: ReadState[];
|
||||
read_states?: Relation<ReadState[]>;
|
||||
|
||||
@OneToMany(() => Webhook, (webhook: Webhook) => webhook.channel, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
webhooks?: Webhook[];
|
||||
webhooks?: Relation<Webhook[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
flags?: number = 0;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { User } from "./User";
|
||||
|
||||
@@ -14,7 +15,7 @@ export class ConnectedAccount extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column({ select: false })
|
||||
access_token: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { User } from ".";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
@@ -18,7 +19,7 @@ export class Emoji extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((emoji: Emoji) => emoji.user)
|
||||
@@ -26,7 +27,7 @@ export class Emoji extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "user_id" })
|
||||
@ManyToOne(() => User)
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column()
|
||||
managed: boolean;
|
||||
|
||||
@@ -41,8 +41,8 @@ export class Guild extends BaseClass {
|
||||
afk_channel_id?: string;
|
||||
|
||||
@JoinColumn({ name: "afk_channel_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
afk_channel?: Channel;
|
||||
@ManyToOne(() => Channel, { nullable: true })
|
||||
afk_channel?: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
afk_timeout?: number = Config.get().defaults.guild.afkTimeout;
|
||||
@@ -57,7 +57,7 @@ export class Guild extends BaseClass {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
bans: Ban[];
|
||||
bans: Relation<Ban[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
banner?: string;
|
||||
@@ -107,7 +107,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
members: Member[];
|
||||
members: Relation<Member[]>;
|
||||
|
||||
@JoinColumn({ name: "role_ids" })
|
||||
@OneToMany(() => Role, (role: Role) => role.guild, {
|
||||
@@ -115,14 +115,14 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
roles: Role[];
|
||||
roles: Relation<Role[]>;
|
||||
|
||||
@JoinColumn({ name: "channel_ids" })
|
||||
@OneToMany(() => Channel, (channel: Channel) => channel.guild, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
channels: Channel[];
|
||||
channels: Relation<Channel[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((guild: Guild) => guild.template)
|
||||
@@ -130,7 +130,7 @@ export class Guild extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "template_id", referencedColumnName: "id" })
|
||||
@ManyToOne(() => Template)
|
||||
template: Template;
|
||||
template: Relation<Template>;
|
||||
|
||||
@JoinColumn({ name: "emoji_ids" })
|
||||
@OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild, {
|
||||
@@ -138,7 +138,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
emojis: Emoji[];
|
||||
emojis: Relation<Emoji[]>;
|
||||
|
||||
@JoinColumn({ name: "sticker_ids" })
|
||||
@OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild, {
|
||||
@@ -146,7 +146,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
stickers: Sticker[];
|
||||
stickers: Relation<Sticker[]>;
|
||||
|
||||
@JoinColumn({ name: "invite_ids" })
|
||||
@OneToMany(() => Invite, (invite: Invite) => invite.guild, {
|
||||
@@ -154,7 +154,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
invites: Invite[];
|
||||
invites: Relation<Invite[]>;
|
||||
|
||||
@JoinColumn({ name: "voice_state_ids" })
|
||||
@OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild, {
|
||||
@@ -162,7 +162,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
voice_states: VoiceState[];
|
||||
voice_states: Relation<VoiceState[]>;
|
||||
|
||||
@JoinColumn({ name: "webhook_ids" })
|
||||
@OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild, {
|
||||
@@ -170,7 +170,7 @@ export class Guild extends BaseClass {
|
||||
orphanedRowAction: "delete",
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
webhooks: Webhook[];
|
||||
webhooks: Relation<Webhook[]>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
mfa_level?: number;
|
||||
@@ -183,8 +183,8 @@ export class Guild extends BaseClass {
|
||||
owner_id?: string; // optional to allow for ownerless guilds
|
||||
|
||||
@JoinColumn({ name: "owner_id", referencedColumnName: "id" })
|
||||
@ManyToOne(() => User)
|
||||
owner?: User; // optional to allow for ownerless guilds
|
||||
@ManyToOne(() => User, { nullable: true })
|
||||
owner?: Relation<User>; // optional to allow for ownerless guilds
|
||||
|
||||
@Column({ nullable: true })
|
||||
preferred_locale?: string;
|
||||
@@ -200,16 +200,16 @@ export class Guild extends BaseClass {
|
||||
public_updates_channel_id: string;
|
||||
|
||||
@JoinColumn({ name: "public_updates_channel_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
public_updates_channel?: Channel;
|
||||
@ManyToOne(() => Channel, { nullable: true })
|
||||
public_updates_channel?: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((guild: Guild) => guild.rules_channel)
|
||||
rules_channel_id?: string;
|
||||
|
||||
@JoinColumn({ name: "rules_channel_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
rules_channel?: string;
|
||||
@ManyToOne(() => Channel, { nullable: true })
|
||||
rules_channel?: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
region?: string;
|
||||
@@ -222,8 +222,8 @@ export class Guild extends BaseClass {
|
||||
system_channel_id?: string;
|
||||
|
||||
@JoinColumn({ name: "system_channel_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
system_channel?: Channel;
|
||||
@ManyToOne(() => Channel, { nullable: true })
|
||||
system_channel?: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
system_channel_flags?: number;
|
||||
@@ -251,8 +251,8 @@ export class Guild extends BaseClass {
|
||||
widget_channel_id?: string;
|
||||
|
||||
@JoinColumn({ name: "widget_channel_id" })
|
||||
@ManyToOne(() => Channel)
|
||||
widget_channel?: Channel;
|
||||
@ManyToOne(() => Channel, { nullable: true })
|
||||
widget_channel?: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
widget_enabled?: boolean;
|
||||
@@ -333,13 +333,13 @@ export class Guild extends BaseClass {
|
||||
|
||||
const ids = new Map();
|
||||
|
||||
body.channels.forEach((x) => {
|
||||
if (x.id) {
|
||||
ids.set(x.id, Snowflake.generate());
|
||||
body.channels!.forEach((x) => {
|
||||
if (x!.id) {
|
||||
ids.set(x!.id, Snowflake.generate());
|
||||
}
|
||||
});
|
||||
|
||||
for (const channel of body.channels?.sort((a, b) => (a.parent_id ? 1 : -1))) {
|
||||
for (const channel of body.channels!.sort((a, b) => (a.parent_id ? 1 : -1))) {
|
||||
let id = ids.get(channel.id) || Snowflake.generate();
|
||||
|
||||
let parent_id = ids.get(channel.parent_id);
|
||||
|
||||
@@ -39,7 +39,7 @@ export class Invite extends BaseClassWithoutId {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((invite: Invite) => invite.channel)
|
||||
@@ -49,7 +49,7 @@ export class Invite extends BaseClassWithoutId {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((invite: Invite) => invite.inviter)
|
||||
@@ -69,7 +69,7 @@ export class Invite extends BaseClassWithoutId {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62
|
||||
target_user?: Relation<User>; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62
|
||||
|
||||
@Column({ nullable: true })
|
||||
target_user_type?: number;
|
||||
|
||||
@@ -40,7 +40,7 @@ export class Member extends BaseClassWithoutId {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column()
|
||||
@RelationId((member: Member) => member.guild)
|
||||
@@ -50,7 +50,7 @@ export class Member extends BaseClassWithoutId {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
nick?: string;
|
||||
@@ -64,7 +64,7 @@ export class Member extends BaseClassWithoutId {
|
||||
}
|
||||
})
|
||||
@ManyToMany(() => Role, { cascade: true })
|
||||
roles: Role[];
|
||||
roles: Relation<Role[]>;
|
||||
|
||||
@Column()
|
||||
joined_at: Date;
|
||||
@@ -106,7 +106,7 @@ export class Member extends BaseClassWithoutId {
|
||||
}
|
||||
|
||||
static async removeFromGuild(user_id: string, guild_id: string) {
|
||||
const guild = await Guild.findOneOrFail({ select: ["owner_id", "member_count"], where: { id: guild_id } });
|
||||
const guild = await require("./Guild").Guild.findOneOrFail({ select: ["owner_id", "member_count"], where: { id: guild_id } });
|
||||
if (guild.owner_id === user_id) throw new Error("The owner cannot be removed of the guild");
|
||||
const member = await Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user"] });
|
||||
|
||||
@@ -224,7 +224,7 @@ export class Member extends BaseClassWithoutId {
|
||||
throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403);
|
||||
}
|
||||
|
||||
const guild = await Guild.findOneOrFail({
|
||||
const guild = await require("./Guild").Guild.findOneOrFail({
|
||||
where: {
|
||||
id: guild_id
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ export class Message extends BaseClass {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.guild)
|
||||
@@ -61,7 +61,7 @@ export class Message extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild?: Guild;
|
||||
guild?: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.author)
|
||||
@@ -72,7 +72,7 @@ export class Message extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
author?: User;
|
||||
author?: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.member)
|
||||
@@ -82,7 +82,7 @@ export class Message extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
member?: Member;
|
||||
member?: Relation<Member>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.webhook)
|
||||
@@ -90,7 +90,7 @@ export class Message extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "webhook_id" })
|
||||
@ManyToOne(() => Webhook)
|
||||
webhook?: Webhook;
|
||||
webhook?: Relation<Webhook>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((message: Message) => message.application)
|
||||
@@ -98,7 +98,7 @@ export class Message extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "application_id" })
|
||||
@ManyToOne(() => Application)
|
||||
application?: Application;
|
||||
application?: Relation<Application>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
content?: string;
|
||||
@@ -118,25 +118,25 @@ export class Message extends BaseClass {
|
||||
|
||||
@JoinTable({ name: "message_user_mentions" })
|
||||
@ManyToMany(() => User)
|
||||
mentions: User[];
|
||||
mentions: Relation<User[]>;
|
||||
|
||||
@JoinTable({ name: "message_role_mentions" })
|
||||
@ManyToMany(() => Role)
|
||||
mention_roles: Role[];
|
||||
mention_roles: Relation<Role[]>;
|
||||
|
||||
@JoinTable({ name: "message_channel_mentions" })
|
||||
@ManyToMany(() => Channel)
|
||||
mention_channels: Channel[];
|
||||
mention_channels: Relation<Channel[]>;
|
||||
|
||||
@JoinTable({ name: "message_stickers" })
|
||||
@ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" })
|
||||
sticker_items?: Sticker[];
|
||||
sticker_items?: Relation<Sticker[]>;
|
||||
|
||||
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
attachments?: Attachment[];
|
||||
attachments?: Relation<Attachment[]>;
|
||||
|
||||
@Column({ type: "simple-json" })
|
||||
embeds: Embed[];
|
||||
@@ -170,7 +170,7 @@ export class Message extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "message_reference_id" })
|
||||
@ManyToOne(() => Message)
|
||||
referenced_message?: Message;
|
||||
referenced_message?: Relation<Message>;
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
interaction?: {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { BaseClassWithoutId } from ".";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Unique } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, Unique } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { User } from "./User";
|
||||
|
||||
@@ -7,11 +8,11 @@ import { User } from "./User";
|
||||
export class Note extends BaseClass {
|
||||
@JoinColumn({ name: "owner_id" })
|
||||
@ManyToOne(() => User, { onDelete: "CASCADE" })
|
||||
owner: User;
|
||||
owner: Relation<User>;
|
||||
|
||||
@JoinColumn({ name: "target_id" })
|
||||
@ManyToOne(() => User, { onDelete: "CASCADE" })
|
||||
target: User;
|
||||
target: Relation<User>;
|
||||
|
||||
@Column()
|
||||
content: string;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Channel } from "./Channel";
|
||||
import { User } from "./User";
|
||||
@@ -18,7 +19,7 @@ export class ReadState extends BaseClass {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column()
|
||||
@RelationId((read_state: ReadState) => read_state.user)
|
||||
@@ -28,7 +29,7 @@ export class ReadState extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
// fully read marker
|
||||
@Column({ nullable: true })
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Channel } from "./Channel";
|
||||
import { User } from "./User";
|
||||
|
||||
@Entity("recipients")
|
||||
export class Recipient extends BaseClass {
|
||||
@@ -11,7 +14,7 @@ export class Recipient extends BaseClass {
|
||||
@ManyToOne(() => require("./Channel").Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: import("./Channel").Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column()
|
||||
@RelationId((recipient: Recipient) => recipient.user)
|
||||
@@ -21,7 +24,7 @@ export class Recipient extends BaseClass {
|
||||
@ManyToOne(() => require("./User").User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: import("./User").User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column({ default: false })
|
||||
closed: boolean;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { User } from "./User";
|
||||
|
||||
@@ -20,7 +21,7 @@ export class Relationship extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
from: User;
|
||||
from: Relation<User>;
|
||||
|
||||
@Column({})
|
||||
@RelationId((relationship: Relationship) => relationship.to)
|
||||
@@ -30,7 +31,7 @@ export class Relationship extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
to: User;
|
||||
to: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
nickname?: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
@@ -13,7 +14,7 @@ export class Role extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column()
|
||||
color: number;
|
||||
|
||||
@@ -16,7 +16,7 @@ export class Session extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
//TODO check, should be 32 char long hex string
|
||||
@Column({ nullable: false, select: false })
|
||||
|
||||
@@ -34,11 +34,11 @@ export class Sticker extends BaseClass {
|
||||
pack_id?: string;
|
||||
|
||||
@JoinColumn({ name: "pack_id" })
|
||||
@ManyToOne(() => require("./StickerPack").StickerPack, {
|
||||
@ManyToOne(() => StickerPack, {
|
||||
onDelete: "CASCADE",
|
||||
nullable: true
|
||||
})
|
||||
pack: import("./StickerPack").StickerPack;
|
||||
pack: Relation<StickerPack>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
guild_id?: string;
|
||||
@@ -47,7 +47,7 @@ export class Sticker extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild?: Guild;
|
||||
guild?: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
user_id?: string;
|
||||
@@ -56,7 +56,7 @@ export class Sticker extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user?: User;
|
||||
user?: Relation<User>;
|
||||
|
||||
@Column({ type: "int" })
|
||||
type: StickerType;
|
||||
|
||||
@@ -17,7 +17,7 @@ export class StickerPack extends BaseClass {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
stickers: Sticker[];
|
||||
stickers: Relation<Sticker[]>;
|
||||
|
||||
// sku_id: string
|
||||
|
||||
@@ -27,5 +27,5 @@ export class StickerPack extends BaseClass {
|
||||
|
||||
@ManyToOne(() => Sticker, { nullable: true })
|
||||
@JoinColumn()
|
||||
cover_sticker?: Sticker;
|
||||
cover_sticker?: Relation<Sticker>;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export class Team extends BaseClass {
|
||||
@OneToMany(() => TeamMember, (member: TeamMember) => member.team, {
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
members: TeamMember[];
|
||||
members: Relation<TeamMember[]>;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
@@ -23,5 +23,5 @@ export class Team extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "owner_user_id" })
|
||||
@ManyToOne(() => User)
|
||||
owner_user: User;
|
||||
owner_user: Relation<User>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Team } from "./Team";
|
||||
import { User } from "./User";
|
||||
|
||||
export enum TeamMemberState {
|
||||
@@ -23,7 +25,7 @@ export class TeamMember extends BaseClass {
|
||||
@ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
team: import("./Team").Team;
|
||||
team: Relation<Team>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((member: TeamMember) => member.user)
|
||||
@@ -33,5 +35,5 @@ export class TeamMember extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
import { User } from "./User";
|
||||
@@ -23,7 +24,7 @@ export class Template extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "creator_id" })
|
||||
@ManyToOne(() => User)
|
||||
creator: User;
|
||||
creator: Relation<User>;
|
||||
|
||||
@Column()
|
||||
created_at: Date;
|
||||
@@ -37,7 +38,7 @@ export class Template extends BaseClass {
|
||||
|
||||
@JoinColumn({ name: "source_guild_id" })
|
||||
@ManyToOne(() => Guild)
|
||||
source_guild: Guild;
|
||||
source_guild: Relation<Guild>;
|
||||
|
||||
@Column({ type: "simple-json" })
|
||||
serialized_source_guild: Guild;
|
||||
|
||||
@@ -141,21 +141,21 @@ export class User extends BaseClass {
|
||||
rights: string = Config.get().register.defaultRights; // Rights
|
||||
|
||||
@OneToMany(() => Session, (session: Session) => session.user)
|
||||
sessions: Session[];
|
||||
sessions: Relation<Session[]>;
|
||||
|
||||
@JoinColumn({ name: "relationship_ids" })
|
||||
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
relationships: Relationship[];
|
||||
relationships: Relation<Relationship[]>;
|
||||
|
||||
@JoinColumn({ name: "connected_account_ids" })
|
||||
@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
|
||||
cascade: true,
|
||||
orphanedRowAction: "delete"
|
||||
})
|
||||
connected_accounts: ConnectedAccount[];
|
||||
connected_accounts: Relation<ConnectedAccount[]>;
|
||||
|
||||
@Column({ type: "simple-json", select: false })
|
||||
data: {
|
||||
@@ -264,6 +264,8 @@ export class User extends BaseClass {
|
||||
// appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
|
||||
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
|
||||
const language = req?.language === "en" ? "en-US" : req?.language || "en-US";
|
||||
const settings = new UserSettings();
|
||||
settings.locale = language;
|
||||
|
||||
const user = OrmUtils.mergeDeep(new User(), {
|
||||
//required:
|
||||
@@ -280,11 +282,14 @@ export class User extends BaseClass {
|
||||
|
||||
//await (user.settings as UserSettings).save();
|
||||
await user.save();
|
||||
await user.settings.save();
|
||||
|
||||
setImmediate(async () => {
|
||||
if (Config.get().guild.autoJoin.enabled) {
|
||||
for (const guild of Config.get().guild.autoJoin.guilds || []) {
|
||||
await Member.addToGuild(user.id, guild).catch((e) => {});
|
||||
await require("./Member")
|
||||
.Member.addToGuild(user.id, guild)
|
||||
.catch((e: any) => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Channel } from "./Channel";
|
||||
import { Guild } from "./Guild";
|
||||
@@ -16,7 +17,7 @@ export class VoiceState extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild?: Guild;
|
||||
guild?: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((voice_state: VoiceState) => voice_state.channel)
|
||||
@@ -26,7 +27,7 @@ export class VoiceState extends BaseClass {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((voice_state: VoiceState) => voice_state.user)
|
||||
@@ -36,20 +37,20 @@ export class VoiceState extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
// @JoinColumn([{ name: "user_id", referencedColumnName: "id" },{ name: "guild_id", referencedColumnName: "guild_id" }])
|
||||
// @ManyToOne(() => Member, {
|
||||
// onDelete: "CASCADE",
|
||||
// })
|
||||
//TODO find a way to make it work without breaking Guild.voice_states
|
||||
member: Member;
|
||||
member: Relation<Member>;
|
||||
|
||||
@Column()
|
||||
session_id: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
token: string;
|
||||
token?: string;
|
||||
|
||||
@Column()
|
||||
deaf: boolean;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||
import "reflect-metadata";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, Relation, RelationId } from "typeorm";
|
||||
import { Application } from "./Application";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Channel } from "./Channel";
|
||||
@@ -32,7 +33,7 @@ export class Webhook extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
guild: Guild;
|
||||
guild: Relation<Guild>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((webhook: Webhook) => webhook.channel)
|
||||
@@ -42,7 +43,7 @@ export class Webhook extends BaseClass {
|
||||
@ManyToOne(() => Channel, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
channel: Channel;
|
||||
channel: Relation<Channel>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((webhook: Webhook) => webhook.application)
|
||||
@@ -52,7 +53,7 @@ export class Webhook extends BaseClass {
|
||||
@ManyToOne(() => Application, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
application: Application;
|
||||
application: Relation<Application>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((webhook: Webhook) => webhook.user)
|
||||
@@ -62,7 +63,7 @@ export class Webhook extends BaseClass {
|
||||
@ManyToOne(() => User, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
user: User;
|
||||
user: Relation<User>;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((webhook: Webhook) => webhook.guild)
|
||||
@@ -72,5 +73,5 @@ export class Webhook extends BaseClass {
|
||||
@ManyToOne(() => Guild, {
|
||||
onDelete: "CASCADE"
|
||||
})
|
||||
source_guild: Guild;
|
||||
source_guild: Relation<Guild>;
|
||||
}
|
||||
|
||||
231
src/util/migrations/mariadb/1660258393551-CodeCleanup3.ts
Normal file
231
src/util/migrations/mariadb/1660258393551-CodeCleanup3.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class CodeCleanup31660258393551 implements MigrationInterface {
|
||||
name = "CodeCleanup31660258393551";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP FOREIGN KEY \`FK_2ce5a55796fe4c2f77ece57a647\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX \`REL_2ce5a55796fe4c2f77ece57a64\` ON \`applications\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE TABLE \`user_settings\` (
|
||||
\`id\` varchar(255) NOT NULL,
|
||||
\`afk_timeout\` int NULL,
|
||||
\`allow_accessibility_detection\` tinyint NULL,
|
||||
\`animate_emoji\` tinyint NULL,
|
||||
\`animate_stickers\` int NULL,
|
||||
\`contact_sync_enabled\` tinyint NULL,
|
||||
\`convert_emoticons\` tinyint NULL,
|
||||
\`custom_status\` text NULL,
|
||||
\`default_guilds_restricted\` tinyint NULL,
|
||||
\`detect_platform_accounts\` tinyint NULL,
|
||||
\`developer_mode\` tinyint NULL,
|
||||
\`disable_games_tab\` tinyint NULL,
|
||||
\`enable_tts_command\` tinyint NULL,
|
||||
\`explicit_content_filter\` int NULL,
|
||||
\`friend_source_flags\` text NULL,
|
||||
\`gateway_connected\` tinyint NULL,
|
||||
\`gif_auto_play\` tinyint NULL,
|
||||
\`guild_folders\` text NULL,
|
||||
\`guild_positions\` text NULL,
|
||||
\`inline_attachment_media\` tinyint NULL,
|
||||
\`inline_embed_media\` tinyint NULL,
|
||||
\`locale\` varchar(255) NULL,
|
||||
\`message_display_compact\` tinyint NULL,
|
||||
\`native_phone_integration_enabled\` tinyint NULL,
|
||||
\`render_embeds\` tinyint NULL,
|
||||
\`render_reactions\` tinyint NULL,
|
||||
\`restricted_guilds\` text NULL,
|
||||
\`show_current_game\` tinyint NULL,
|
||||
\`status\` varchar(255) NULL,
|
||||
\`stream_notifications_enabled\` tinyint NULL,
|
||||
\`theme\` varchar(255) NULL,
|
||||
\`timezone_offset\` int NULL,
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE = InnoDB
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP COLUMN \`settings\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`type\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`hook\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`redirect_uris\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`rpc_application_state\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`store_application_state\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`verification_state\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`interactions_endpoint_url\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`integration_public\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`integration_require_code_grant\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`discoverability_state\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`discovery_eligibility_flags\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`tags\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`install_params\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`bot_user_id\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`guilds\`
|
||||
ADD \`premium_progress_bar_enabled\` tinyint NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`rpc_origins\` text NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`primary_sku_id\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`slug\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`guild_id\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` CHANGE \`description\` \`description\` varchar(255) NOT NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`flags\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`flags\` varchar(255) NOT NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD CONSTRAINT \`FK_e5bf78cdbbe9ba91062d74c5aba\` FOREIGN KEY (\`guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP FOREIGN KEY \`FK_e5bf78cdbbe9ba91062d74c5aba\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`flags\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`flags\` int NOT NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` CHANGE \`description\` \`description\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`guild_id\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`slug\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`primary_sku_id\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\` DROP COLUMN \`rpc_origins\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`guilds\` DROP COLUMN \`premium_progress_bar_enabled\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`bot_user_id\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`install_params\` text NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`tags\` text NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`discovery_eligibility_flags\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`discoverability_state\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`integration_require_code_grant\` tinyint NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`integration_public\` tinyint NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`interactions_endpoint_url\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`verification_state\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`store_application_state\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`rpc_application_state\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`redirect_uris\` text NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`hook\` tinyint NOT NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD \`type\` text NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD \`settings\` text NOT NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP TABLE \`user_settings\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX \`REL_2ce5a55796fe4c2f77ece57a64\` ON \`applications\` (\`bot_user_id\`)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`applications\`
|
||||
ADD CONSTRAINT \`FK_2ce5a55796fe4c2f77ece57a647\` FOREIGN KEY (\`bot_user_id\`) REFERENCES \`users\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
`);
|
||||
}
|
||||
}
|
||||
38
src/util/migrations/mariadb/1660260587556-CodeCleanup4.ts
Normal file
38
src/util/migrations/mariadb/1660260587556-CodeCleanup4.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class CodeCleanup41660260587556 implements MigrationInterface {
|
||||
name = "CodeCleanup41660260587556";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD \`settingsId\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` (\`settingsId\`)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD CONSTRAINT \`FK_76ba283779c8441fd5ff819c8cf\` FOREIGN KEY (\`settingsId\`) REFERENCES \`user_settings\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP FOREIGN KEY \`FK_76ba283779c8441fd5ff819c8cf\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP COLUMN \`settingsId\`
|
||||
`);
|
||||
}
|
||||
}
|
||||
52
src/util/migrations/mariadb/1660265930624-CodeCleanup5.ts
Normal file
52
src/util/migrations/mariadb/1660265930624-CodeCleanup5.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class CodeCleanup51660265930624 implements MigrationInterface {
|
||||
name = "CodeCleanup51660265930624";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD \`settingsId\` varchar(255) NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD UNIQUE INDEX \`IDX_76ba283779c8441fd5ff819c8c\` (\`settingsId\`)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`channels\`
|
||||
ADD \`flags\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`channels\`
|
||||
ADD \`default_thread_rate_limit_per_user\` int NULL
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\` (\`settingsId\`)
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\`
|
||||
ADD CONSTRAINT \`FK_76ba283779c8441fd5ff819c8cf\` FOREIGN KEY (\`settingsId\`) REFERENCES \`user_settings\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP FOREIGN KEY \`FK_76ba283779c8441fd5ff819c8cf\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
DROP INDEX \`REL_76ba283779c8441fd5ff819c8c\` ON \`users\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`channels\` DROP COLUMN \`default_thread_rate_limit_per_user\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`channels\` DROP COLUMN \`flags\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP INDEX \`IDX_76ba283779c8441fd5ff819c8c\`
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE \`users\` DROP COLUMN \`settingsId\`
|
||||
`);
|
||||
}
|
||||
}
|
||||
13
src/util/plugin/Plugin.ts
Normal file
13
src/util/plugin/Plugin.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { TypedEventEmitter } from "@fosscord/util";
|
||||
import EventEmitter from "events";
|
||||
|
||||
type PluginEvents = {
|
||||
error: (error: Error | unknown) => void;
|
||||
loaded: () => void;
|
||||
};
|
||||
|
||||
export class Plugin extends (EventEmitter as new () => TypedEventEmitter<PluginEvents>) {
|
||||
async init() {
|
||||
// insert default config into database?
|
||||
}
|
||||
}
|
||||
39
src/util/plugin/PluginLoader.ts
Normal file
39
src/util/plugin/PluginLoader.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { Plugin, PluginManifest } from "./";
|
||||
|
||||
const root = process.env.PLUGIN_LOCATION || "../plugins";
|
||||
|
||||
let pluginsLoaded = false;
|
||||
export class PluginLoader {
|
||||
public static loadPlugins() {
|
||||
console.log(`Plugin root directory: ${path.resolve(root)}`);
|
||||
const dirs = fs.readdirSync(root).filter((x) => {
|
||||
try {
|
||||
fs.readdirSync(path.join(root, x));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
console.log(dirs);
|
||||
dirs.forEach(async (x) => {
|
||||
let modPath = path.resolve(path.join(root, x));
|
||||
console.log(`Trying to load plugin: ${modPath}`);
|
||||
const manifest = require(path.join(modPath, "plugin.json")) as PluginManifest;
|
||||
console.log(
|
||||
`Plugin info: ${manifest.name} (${manifest.id}), written by ${manifest.authors}, available at ${manifest.repository}`
|
||||
);
|
||||
const module_ = require(path.join(modPath, "dist", "index.js")) as Plugin;
|
||||
try {
|
||||
await module_.init();
|
||||
module_.emit("loaded");
|
||||
} catch (error) {
|
||||
module_.emit("error", error);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
//module_.pluginPath =
|
||||
}
|
||||
}
|
||||
9
src/util/plugin/PluginManifest.ts
Normal file
9
src/util/plugin/PluginManifest.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class PluginManifest {
|
||||
id: string;
|
||||
name: string;
|
||||
authors: string[];
|
||||
repository: string;
|
||||
license: string;
|
||||
version: string; // semver
|
||||
versionCode: number; // integer
|
||||
}
|
||||
3
src/util/plugin/index.ts
Normal file
3
src/util/plugin/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./Plugin";
|
||||
export * from "./PluginLoader";
|
||||
export * from "./PluginManifest";
|
||||
19
src/util/schemas/SelectProtocolSchema.ts
Normal file
19
src/util/schemas/SelectProtocolSchema.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface SelectProtocolSchema {
|
||||
protocol: "webrtc" | "udp";
|
||||
data:
|
||||
| string
|
||||
| {
|
||||
address: string;
|
||||
port: number;
|
||||
mode: string;
|
||||
};
|
||||
sdp?: string;
|
||||
codecs?: {
|
||||
name: "opus" | "VP8" | "VP9" | "H264";
|
||||
type: "audio" | "video";
|
||||
priority: number;
|
||||
payload_type: number;
|
||||
rtx_payload_type?: number | null;
|
||||
}[];
|
||||
rtc_connection_id?: string; // uuid
|
||||
}
|
||||
54
src/util/schemas/Validator.ts
Normal file
54
src/util/schemas/Validator.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import Ajv from "ajv";
|
||||
import addFormats from "ajv-formats";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const SchemaPath = path.join(__dirname, "..", "..", "..", "assets", "schemas.json");
|
||||
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
|
||||
|
||||
export const ajv = new Ajv({
|
||||
allErrors: true,
|
||||
parseDate: true,
|
||||
allowDate: true,
|
||||
schemas,
|
||||
coerceTypes: true,
|
||||
messages: true,
|
||||
strict: true,
|
||||
strictRequired: true
|
||||
});
|
||||
|
||||
addFormats(ajv);
|
||||
|
||||
export function validateSchema<G>(schema: string, data: G): G {
|
||||
const valid = ajv.validate(schema, normalizeBody(data));
|
||||
if (!valid) throw ajv.errors;
|
||||
return data;
|
||||
}
|
||||
|
||||
// Normalizer is introduced to workaround https://github.com/ajv-validator/ajv/issues/1287
|
||||
// this removes null values as ajv doesn't treat them as undefined
|
||||
// normalizeBody allows to handle circular structures without issues
|
||||
// taken from https://github.com/serverless/serverless/blob/master/lib/classes/ConfigSchemaHandler/index.js#L30 (MIT license)
|
||||
export const normalizeBody = (body: any = {}) => {
|
||||
const normalizedObjectsSet = new WeakSet();
|
||||
const normalizeObject = (object: any) => {
|
||||
if (normalizedObjectsSet.has(object)) return;
|
||||
normalizedObjectsSet.add(object);
|
||||
if (Array.isArray(object)) {
|
||||
for (const [index, value] of object.entries()) {
|
||||
if (typeof value === "object") normalizeObject(value);
|
||||
}
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
if (value == null) {
|
||||
if (key === "icon" || key === "avatar" || key === "banner" || key === "splash" || key === "discovery_splash") continue;
|
||||
delete object[key];
|
||||
} else if (typeof value === "object") {
|
||||
normalizeObject(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
normalizeObject(body);
|
||||
return body;
|
||||
};
|
||||
12
src/util/schemas/VoiceIdentifySchema.ts
Normal file
12
src/util/schemas/VoiceIdentifySchema.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface VoiceIdentifySchema {
|
||||
server_id: string;
|
||||
user_id: string;
|
||||
session_id: string;
|
||||
token: string;
|
||||
video?: boolean;
|
||||
streams?: {
|
||||
type: string;
|
||||
rid: string;
|
||||
quality: number;
|
||||
}[];
|
||||
}
|
||||
17
src/util/schemas/VoiceVideoSchema.ts
Normal file
17
src/util/schemas/VoiceVideoSchema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export interface VoiceVideoSchema {
|
||||
audio_ssrc: number;
|
||||
video_ssrc: number;
|
||||
rtx_ssrc?: number;
|
||||
user_id?: string;
|
||||
streams?: {
|
||||
type: "video" | "audio";
|
||||
rid: string;
|
||||
ssrc: number;
|
||||
active: boolean;
|
||||
quality: number;
|
||||
rtx_ssrc: number;
|
||||
max_bitrate: number;
|
||||
max_framerate: number;
|
||||
max_resolution: { type: string; width: number; height: number };
|
||||
}[];
|
||||
}
|
||||
@@ -30,6 +30,7 @@ export * from "./RelationshipPostSchema";
|
||||
export * from "./RelationshipPutSchema";
|
||||
export * from "./RoleModifySchema";
|
||||
export * from "./RolePositionUpdateSchema";
|
||||
export * from "./SelectProtocolSchema";
|
||||
export * from "./TemplateCreateSchema";
|
||||
export * from "./TemplateModifySchema";
|
||||
export * from "./TotpDisableSchema";
|
||||
@@ -37,7 +38,10 @@ export * from "./TotpEnableSchema";
|
||||
export * from "./TotpSchema";
|
||||
export * from "./UserModifySchema";
|
||||
export * from "./UserSettingsSchema";
|
||||
export * from "./Validator";
|
||||
export * from "./VanityUrlSchema";
|
||||
export * from "./VoiceIdentifySchema";
|
||||
export * from "./VoiceStateUpdateSchema";
|
||||
export * from "./VoiceVideoSchema";
|
||||
export * from "./WebhookCreateSchema";
|
||||
export * from "./WidgetModifySchema";
|
||||
|
||||
@@ -91,6 +91,7 @@ function getDataSourceOptions(): DataSourceOptions {
|
||||
cache: {
|
||||
duration: 1000 * 3 // cache all find queries for 3 seconds
|
||||
},
|
||||
// relationLoadStrategy: "query",
|
||||
bigNumberStrings: false,
|
||||
supportBigNumbers: true,
|
||||
name: "default",
|
||||
|
||||
12
src/util/util/NamingStrategy.ts
Normal file
12
src/util/util/NamingStrategy.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { DefaultNamingStrategy } from "typeorm";
|
||||
|
||||
export class NamingStrategy extends DefaultNamingStrategy {
|
||||
eagerJoinRelationAlias(alias: string, propertyPath: string) {
|
||||
const result = super.eagerJoinRelationAlias(alias, propertyPath);
|
||||
|
||||
console.log({ alias, propertyPath, result });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export const namingStrategy = new NamingStrategy();
|
||||
41
src/util/util/imports/TypedEmitter.ts
Normal file
41
src/util/util/imports/TypedEmitter.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
export type EventMap = {
|
||||
[key: string]: (...args: any[]) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type-safe event emitter.
|
||||
*
|
||||
* Use it like this:
|
||||
*
|
||||
* ```typescript
|
||||
* type MyEvents = {
|
||||
* error: (error: Error) => void;
|
||||
* message: (from: string, content: string) => void;
|
||||
* }
|
||||
*
|
||||
* const myEmitter = new EventEmitter() as TypedEmitter<MyEvents>;
|
||||
*
|
||||
* myEmitter.emit("error", "x") // <- Will catch this type error;
|
||||
* ```
|
||||
*/
|
||||
export interface TypedEventEmitter<Events extends EventMap> {
|
||||
addListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
on<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
once<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
prependListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
prependOnceListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
|
||||
off<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
removeAllListeners<E extends keyof Events>(event?: E): this;
|
||||
removeListener<E extends keyof Events>(event: E, listener: Events[E]): this;
|
||||
|
||||
emit<E extends keyof Events>(event: E, ...args: Parameters<Events[E]>): boolean;
|
||||
// The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5
|
||||
eventNames(): (keyof Events | string | symbol)[];
|
||||
rawListeners<E extends keyof Events>(event: E): Events[E][];
|
||||
listeners<E extends keyof Events>(event: E): Events[E][];
|
||||
listenerCount<E extends keyof Events>(event: E): number;
|
||||
|
||||
getMaxListeners(): number;
|
||||
setMaxListeners(maxListeners: number): this;
|
||||
}
|
||||
56
src/webrtc/Server.ts
Normal file
56
src/webrtc/Server.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { closeDatabase, Config, getOrInitialiseDatabase, initEvent } from "@fosscord/util";
|
||||
import dotenv from "dotenv";
|
||||
import http from "http";
|
||||
import ws from "ws";
|
||||
import { Connection } from "./events/Connection";
|
||||
dotenv.config();
|
||||
|
||||
export class Server {
|
||||
public ws: ws.Server;
|
||||
public port: number;
|
||||
public server: http.Server;
|
||||
public production: boolean;
|
||||
|
||||
constructor({ port, server, production }: { port: number; server?: http.Server; production?: boolean }) {
|
||||
this.port = port;
|
||||
this.production = production || false;
|
||||
|
||||
if (server) this.server = server;
|
||||
else {
|
||||
this.server = http.createServer(function (req, res) {
|
||||
res.writeHead(200).end("Online");
|
||||
});
|
||||
}
|
||||
|
||||
this.server.on("upgrade", (request, socket, head) => {
|
||||
if (!request.url?.includes("voice")) return;
|
||||
this.ws.handleUpgrade(request, socket, head, (socket) => {
|
||||
// @ts-ignore
|
||||
socket.server = this;
|
||||
this.ws.emit("connection", socket, request);
|
||||
});
|
||||
});
|
||||
|
||||
this.ws = new ws.Server({
|
||||
maxPayload: 1024 * 1024 * 100,
|
||||
noServer: true
|
||||
});
|
||||
this.ws.on("connection", Connection);
|
||||
this.ws.on("error", console.error);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
await getOrInitialiseDatabase();
|
||||
await Config.init();
|
||||
await initEvent();
|
||||
if (!this.server.listening) {
|
||||
this.server.listen(this.port);
|
||||
console.log(`[WebRTC] online on 0.0.0.0:${this.port}`);
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
closeDatabase();
|
||||
this.server.close();
|
||||
}
|
||||
}
|
||||
9
src/webrtc/events/Close.ts
Normal file
9
src/webrtc/events/Close.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { WebSocket } from "@fosscord/gateway";
|
||||
import { Session } from "@fosscord/util";
|
||||
|
||||
export async function onClose(this: WebSocket, code: number, reason: string) {
|
||||
console.log("[WebRTC] closed", code, reason.toString());
|
||||
|
||||
if (this.session_id) await Session.delete({ session_id: this.session_id });
|
||||
this.removeAllListeners();
|
||||
}
|
||||
60
src/webrtc/events/Connection.ts
Normal file
60
src/webrtc/events/Connection.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { CloseCodes, Send, setHeartbeat, WebSocket } from "@fosscord/gateway";
|
||||
import { IncomingMessage } from "http";
|
||||
import { URL } from "url";
|
||||
import WS from "ws";
|
||||
import { VoiceOPCodes } from "../util";
|
||||
import { onClose } from "./Close";
|
||||
import { onMessage } from "./Message";
|
||||
var erlpack: any;
|
||||
try {
|
||||
erlpack = require("@yukikaze-bot/erlpack");
|
||||
} catch (error) {}
|
||||
|
||||
// TODO: check rate limit
|
||||
// TODO: specify rate limit in config
|
||||
// TODO: check msg max size
|
||||
|
||||
export async function Connection(this: WS.Server, socket: WebSocket, request: IncomingMessage) {
|
||||
try {
|
||||
socket.on("close", onClose.bind(socket));
|
||||
socket.on("message", onMessage.bind(socket));
|
||||
console.log("[WebRTC] new connection", request.url);
|
||||
|
||||
if (process.env.WS_LOGEVENTS) {
|
||||
[
|
||||
"close",
|
||||
"error",
|
||||
"upgrade",
|
||||
//"message",
|
||||
"open",
|
||||
"ping",
|
||||
"pong",
|
||||
"unexpected-response"
|
||||
].forEach((x) => {
|
||||
socket.on(x, (y) => console.log("[WebRTC]", x, y));
|
||||
});
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(`http://localhost${request.url}`);
|
||||
|
||||
socket.encoding = "json";
|
||||
socket.version = Number(searchParams.get("v")) || 5;
|
||||
if (socket.version < 3) return socket.close(CloseCodes.Unknown_error, "invalid version");
|
||||
|
||||
setHeartbeat(socket);
|
||||
|
||||
socket.readyTimeout = setTimeout(() => {
|
||||
return socket.close(CloseCodes.Session_timed_out);
|
||||
}, 1000 * 30);
|
||||
|
||||
await Send(socket, {
|
||||
op: VoiceOPCodes.HELLO,
|
||||
d: {
|
||||
heartbeat_interval: 1000 * 30
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[WebRTC]", error);
|
||||
return socket.close(CloseCodes.Unknown_error);
|
||||
}
|
||||
}
|
||||
38
src/webrtc/events/Message.ts
Normal file
38
src/webrtc/events/Message.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { CloseCodes, Payload, WebSocket } from "@fosscord/gateway";
|
||||
import { Tuple } from "lambert-server";
|
||||
import OPCodeHandlers from "../opcodes";
|
||||
import { VoiceOPCodes } from "../util";
|
||||
|
||||
const PayloadSchema = {
|
||||
op: Number,
|
||||
$d: new Tuple(Object, Number), // or number for heartbeat sequence
|
||||
$s: Number,
|
||||
$t: String
|
||||
};
|
||||
|
||||
export async function onMessage(this: WebSocket, buffer: Buffer) {
|
||||
try {
|
||||
var data: Payload = JSON.parse(buffer.toString());
|
||||
if (data.op !== VoiceOPCodes.IDENTIFY && !this.user_id) return this.close(CloseCodes.Not_authenticated);
|
||||
|
||||
// @ts-ignore
|
||||
const OPCodeHandler = OPCodeHandlers[data.op];
|
||||
if (!OPCodeHandler) {
|
||||
// @ts-ignore
|
||||
console.error("[WebRTC] Unkown opcode " + VoiceOPCodes[data.op]);
|
||||
// TODO: if all opcodes are implemented comment this out:
|
||||
// this.close(CloseCodes.Unknown_opcode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![VoiceOPCodes.HEARTBEAT, VoiceOPCodes.SPEAKING].includes(data.op as VoiceOPCodes)) {
|
||||
// @ts-ignore
|
||||
console.log("[WebRTC] Opcode " + VoiceOPCodes[data.op]);
|
||||
}
|
||||
|
||||
return await OPCodeHandler.call(this, data);
|
||||
} catch (error) {
|
||||
console.error("[WebRTC] error", error);
|
||||
// if (!this.CLOSED && this.CLOSING) return this.close(CloseCodes.Unknown_error);
|
||||
}
|
||||
}
|
||||
2
src/webrtc/index.ts
Normal file
2
src/webrtc/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./Server";
|
||||
export * from "./util/index";
|
||||
6
src/webrtc/opcodes/BackendVersion.ts
Normal file
6
src/webrtc/opcodes/BackendVersion.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Payload, Send, WebSocket } from "@fosscord/gateway";
|
||||
import { VoiceOPCodes } from "../util";
|
||||
|
||||
export async function onBackendVersion(this: WebSocket, data: Payload) {
|
||||
await Send(this, { op: VoiceOPCodes.VOICE_BACKEND_VERSION, d: { voice: "0.8.43", rtc_worker: "0.3.26" } });
|
||||
}
|
||||
9
src/webrtc/opcodes/Heartbeat.ts
Normal file
9
src/webrtc/opcodes/Heartbeat.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { CloseCodes, Payload, Send, setHeartbeat, WebSocket } from "@fosscord/gateway";
|
||||
import { VoiceOPCodes } from "../util";
|
||||
|
||||
export async function onHeartbeat(this: WebSocket, data: Payload) {
|
||||
setHeartbeat(this);
|
||||
if (isNaN(data.d)) return this.close(CloseCodes.Decode_error);
|
||||
|
||||
await Send(this, { op: VoiceOPCodes.HEARTBEAT_ACK, d: data.d });
|
||||
}
|
||||
60
src/webrtc/opcodes/Identify.ts
Normal file
60
src/webrtc/opcodes/Identify.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { CloseCodes, Payload, Send, WebSocket } from "@fosscord/gateway";
|
||||
import { validateSchema, VoiceIdentifySchema, VoiceState } from "@fosscord/util";
|
||||
import { endpoint, getClients, VoiceOPCodes } from "@fosscord/webrtc";
|
||||
import SemanticSDP from "semantic-sdp";
|
||||
const defaultSDP = require("../../../assets/sdp.json");
|
||||
|
||||
export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
clearTimeout(this.readyTimeout);
|
||||
const { server_id, user_id, session_id, token, streams, video } = validateSchema("VoiceIdentifySchema", data.d) as VoiceIdentifySchema;
|
||||
|
||||
const voiceState = await VoiceState.findOneBy({ guild_id: server_id, user_id, token, session_id });
|
||||
if (!voiceState) return this.close(CloseCodes.Authentication_failed);
|
||||
|
||||
this.user_id = user_id;
|
||||
this.session_id = session_id;
|
||||
const sdp = SemanticSDP.SDPInfo.expand(defaultSDP);
|
||||
sdp.setDTLS(SemanticSDP.DTLSInfo.expand({ setup: "actpass", hash: "sha-256", fingerprint: endpoint.getDTLSFingerprint() }));
|
||||
|
||||
this.client = {
|
||||
websocket: this,
|
||||
out: {
|
||||
tracks: new Map()
|
||||
},
|
||||
in: {
|
||||
audio_ssrc: 0,
|
||||
video_ssrc: 0,
|
||||
rtx_ssrc: 0
|
||||
},
|
||||
sdp,
|
||||
channel_id: voiceState.channel_id
|
||||
};
|
||||
|
||||
const clients = getClients(voiceState.channel_id)!;
|
||||
clients.add(this.client);
|
||||
|
||||
this.on("close", () => {
|
||||
clients.delete(this.client);
|
||||
});
|
||||
|
||||
await Send(this, {
|
||||
op: VoiceOPCodes.READY,
|
||||
d: {
|
||||
streams: [
|
||||
// { type: "video", ssrc: this.ssrc + 1, rtx_ssrc: this.ssrc + 2, rid: "100", quality: 100, active: false }
|
||||
],
|
||||
ssrc: -1,
|
||||
port: endpoint.getLocalPort(),
|
||||
modes: [
|
||||
"aead_aes256_gcm_rtpsize",
|
||||
"aead_aes256_gcm",
|
||||
"xsalsa20_poly1305_lite_rtpsize",
|
||||
"xsalsa20_poly1305_lite",
|
||||
"xsalsa20_poly1305_suffix",
|
||||
"xsalsa20_poly1305"
|
||||
],
|
||||
ip: "127.0.0.1",
|
||||
experiments: []
|
||||
}
|
||||
});
|
||||
}
|
||||
44
src/webrtc/opcodes/SelectProtocol.ts
Normal file
44
src/webrtc/opcodes/SelectProtocol.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Payload, Send, WebSocket } from "@fosscord/gateway";
|
||||
import { SelectProtocolSchema, validateSchema } from "@fosscord/util";
|
||||
import { endpoint, PublicIP, VoiceOPCodes } from "@fosscord/webrtc";
|
||||
import SemanticSDP from "semantic-sdp";
|
||||
|
||||
export async function onSelectProtocol(this: WebSocket, payload: Payload) {
|
||||
const data = validateSchema("SelectProtocolSchema", payload.d) as SelectProtocolSchema;
|
||||
|
||||
const offer = SemanticSDP.SDPInfo.parse("m=audio\n" + data.sdp!);
|
||||
this.client.sdp!.setICE(offer.getICE());
|
||||
this.client.sdp!.setDTLS(offer.getDTLS());
|
||||
|
||||
const transport = endpoint.createTransport(this.client.sdp!);
|
||||
this.client.transport = transport;
|
||||
transport.setRemoteProperties(this.client.sdp!);
|
||||
transport.setLocalProperties(this.client.sdp!);
|
||||
|
||||
const dtls = transport.getLocalDTLSInfo();
|
||||
const ice = transport.getLocalICEInfo();
|
||||
const port = endpoint.getLocalPort();
|
||||
const fingerprint = dtls.getHash() + " " + dtls.getFingerprint();
|
||||
const candidates = transport.getLocalCandidates();
|
||||
const candidate = candidates[0];
|
||||
|
||||
const answer = `m=audio ${port} ICE/SDP
|
||||
a=fingerprint:${fingerprint}
|
||||
c=IN IP4 ${PublicIP}
|
||||
a=rtcp:${port}
|
||||
a=ice-ufrag:${ice.getUfrag()}
|
||||
a=ice-pwd:${ice.getPwd()}
|
||||
a=fingerprint:${fingerprint}
|
||||
a=candidate:1 1 ${candidate.getTransport()} ${candidate.getFoundation()} ${candidate.getAddress()} ${candidate.getPort()} typ host
|
||||
`;
|
||||
|
||||
await Send(this, {
|
||||
op: VoiceOPCodes.SELECT_PROTOCOL_ACK,
|
||||
d: {
|
||||
video_codec: "H264",
|
||||
sdp: answer,
|
||||
media_session_id: this.session_id,
|
||||
audio_codec: "opus"
|
||||
}
|
||||
});
|
||||
}
|
||||
22
src/webrtc/opcodes/Speaking.ts
Normal file
22
src/webrtc/opcodes/Speaking.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Payload, Send, WebSocket } from "@fosscord/gateway";
|
||||
import { getClients, VoiceOPCodes } from "../util";
|
||||
|
||||
// {"speaking":1,"delay":5,"ssrc":2805246727}
|
||||
|
||||
export async function onSpeaking(this: WebSocket, data: Payload) {
|
||||
if (!this.client) return;
|
||||
|
||||
getClients(this.client.channel_id).forEach((client) => {
|
||||
if (client === this.client) return;
|
||||
const ssrc = this.client.out.tracks.get(client.websocket.user_id);
|
||||
|
||||
Send(client.websocket, {
|
||||
op: VoiceOPCodes.SPEAKING,
|
||||
d: {
|
||||
user_id: client.websocket.user_id,
|
||||
speaking: data.d.speaking,
|
||||
ssrc: ssrc?.audio_ssrc || 0
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
117
src/webrtc/opcodes/Video.ts
Normal file
117
src/webrtc/opcodes/Video.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { Payload, Send, WebSocket } from "@fosscord/gateway";
|
||||
import { validateSchema, VoiceVideoSchema } from "@fosscord/util";
|
||||
import { channels, getClients, VoiceOPCodes } from "@fosscord/webrtc";
|
||||
import { IncomingStreamTrack, SSRCs } from "medooze-media-server";
|
||||
import SemanticSDP from "semantic-sdp";
|
||||
|
||||
export async function onVideo(this: WebSocket, payload: Payload) {
|
||||
if (!this.client) return;
|
||||
const { transport, channel_id } = this.client;
|
||||
if (!transport) return;
|
||||
const d = validateSchema("VoiceVideoSchema", payload.d) as VoiceVideoSchema;
|
||||
|
||||
await Send(this, { op: VoiceOPCodes.MEDIA_SINK_WANTS, d: { any: 100 } });
|
||||
|
||||
const id = "stream" + this.user_id;
|
||||
|
||||
var stream = this.client.in.stream!;
|
||||
if (!stream) {
|
||||
stream = this.client.transport!.createIncomingStream(
|
||||
// @ts-ignore
|
||||
SemanticSDP.StreamInfo.expand({
|
||||
id,
|
||||
// @ts-ignore
|
||||
tracks: []
|
||||
})
|
||||
);
|
||||
this.client.in.stream = stream;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
for (const track of stream.getTracks()) {
|
||||
for (const layer of Object.values(track.getStats())) {
|
||||
console.log(track.getId(), layer.total);
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
stream.on("stopped", () => {
|
||||
console.log("stream stopped");
|
||||
clearInterval(interval);
|
||||
});
|
||||
this.on("close", () => {
|
||||
transport!.stop();
|
||||
});
|
||||
const out = transport.createOutgoingStream(
|
||||
// @ts-ignore
|
||||
SemanticSDP.StreamInfo.expand({
|
||||
id: "out" + this.user_id,
|
||||
// @ts-ignore
|
||||
tracks: []
|
||||
})
|
||||
);
|
||||
this.client.out.stream = out;
|
||||
|
||||
const clients = channels.get(channel_id)!;
|
||||
|
||||
clients.forEach((client) => {
|
||||
if (client.websocket.user_id === this.user_id) return;
|
||||
if (!client.in.stream) return;
|
||||
|
||||
client.in.stream?.getTracks().forEach((track) => {
|
||||
attachTrack.call(this, track, client.websocket.user_id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (d.audio_ssrc) {
|
||||
handleSSRC.call(this, "audio", { media: d.audio_ssrc, rtx: d.audio_ssrc + 1 });
|
||||
}
|
||||
if (d.video_ssrc && d.rtx_ssrc) {
|
||||
handleSSRC.call(this, "video", { media: d.video_ssrc, rtx: d.rtx_ssrc });
|
||||
}
|
||||
}
|
||||
|
||||
function attachTrack(this: WebSocket, track: IncomingStreamTrack, user_id: string) {
|
||||
if (!this.client) return;
|
||||
const outTrack = this.client.transport!.createOutgoingStreamTrack(track.getMedia());
|
||||
outTrack.attachTo(track);
|
||||
this.client.out.stream!.addTrack(outTrack);
|
||||
var ssrcs = this.client.out.tracks.get(user_id)!;
|
||||
if (!ssrcs) ssrcs = this.client.out.tracks.set(user_id, { audio_ssrc: 0, rtx_ssrc: 0, video_ssrc: 0 }).get(user_id)!;
|
||||
|
||||
if (track.getMedia() === "audio") {
|
||||
ssrcs.audio_ssrc = outTrack.getSSRCs().media!;
|
||||
} else if (track.getMedia() === "video") {
|
||||
ssrcs.video_ssrc = outTrack.getSSRCs().media!;
|
||||
ssrcs.rtx_ssrc = outTrack.getSSRCs().rtx!;
|
||||
}
|
||||
|
||||
Send(this, {
|
||||
op: VoiceOPCodes.VIDEO,
|
||||
d: {
|
||||
user_id: user_id,
|
||||
...ssrcs
|
||||
} as VoiceVideoSchema
|
||||
});
|
||||
}
|
||||
|
||||
function handleSSRC(this: WebSocket, type: "audio" | "video", ssrcs: SSRCs) {
|
||||
const stream = this.client.in.stream!;
|
||||
const transport = this.client.transport!;
|
||||
|
||||
const id = type + ssrcs.media;
|
||||
var track = stream.getTrack(id);
|
||||
if (!track) {
|
||||
console.log("createIncomingStreamTrack", id);
|
||||
track = transport.createIncomingStreamTrack(type, { id, ssrcs });
|
||||
stream.addTrack(track);
|
||||
|
||||
const clients = getClients(this.client.channel_id)!;
|
||||
clients.forEach((client) => {
|
||||
if (client.websocket.user_id === this.user_id) return;
|
||||
if (!client.out.stream) return;
|
||||
|
||||
attachTrack.call(this, track, client.websocket.user_id);
|
||||
});
|
||||
}
|
||||
}
|
||||
19
src/webrtc/opcodes/index.ts
Normal file
19
src/webrtc/opcodes/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Payload, WebSocket } from "@fosscord/gateway";
|
||||
import { VoiceOPCodes } from "../util";
|
||||
import { onBackendVersion } from "./BackendVersion";
|
||||
import { onHeartbeat } from "./Heartbeat";
|
||||
import { onIdentify } from "./Identify";
|
||||
import { onSelectProtocol } from "./SelectProtocol";
|
||||
import { onSpeaking } from "./Speaking";
|
||||
import { onVideo } from "./Video";
|
||||
|
||||
export type OPCodeHandler = (this: WebSocket, data: Payload) => any;
|
||||
|
||||
export default {
|
||||
[VoiceOPCodes.HEARTBEAT]: onHeartbeat,
|
||||
[VoiceOPCodes.IDENTIFY]: onIdentify,
|
||||
[VoiceOPCodes.VOICE_BACKEND_VERSION]: onBackendVersion,
|
||||
[VoiceOPCodes.VIDEO]: onVideo,
|
||||
[VoiceOPCodes.SPEAKING]: onSpeaking,
|
||||
[VoiceOPCodes.SELECT_PROTOCOL]: onSelectProtocol
|
||||
};
|
||||
13
src/webrtc/start.ts
Normal file
13
src/webrtc/start.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
process.on("uncaughtException", console.error);
|
||||
process.on("unhandledRejection", console.error);
|
||||
|
||||
import { config } from "dotenv";
|
||||
import { Server } from "./Server";
|
||||
config();
|
||||
|
||||
const port = Number(process.env.PORT) || 3004;
|
||||
|
||||
const server = new Server({
|
||||
port
|
||||
});
|
||||
server.start();
|
||||
26
src/webrtc/util/Constants.ts
Normal file
26
src/webrtc/util/Constants.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export enum VoiceStatus {
|
||||
CONNECTED = 0,
|
||||
CONNECTING = 1,
|
||||
AUTHENTICATING = 2,
|
||||
RECONNECTING = 3,
|
||||
DISCONNECTED = 4
|
||||
}
|
||||
|
||||
export enum VoiceOPCodes {
|
||||
IDENTIFY = 0,
|
||||
SELECT_PROTOCOL = 1,
|
||||
READY = 2,
|
||||
HEARTBEAT = 3,
|
||||
SELECT_PROTOCOL_ACK = 4,
|
||||
SPEAKING = 5,
|
||||
HEARTBEAT_ACK = 6,
|
||||
RESUME = 7,
|
||||
HELLO = 8,
|
||||
RESUMED = 9,
|
||||
VIDEO = 12,
|
||||
CLIENT_DISCONNECT = 13,
|
||||
SESSION_UPDATE = 14,
|
||||
MEDIA_SINK_WANTS = 15,
|
||||
VOICE_BACKEND_VERSION = 16,
|
||||
CHANNEL_OPTIONS_UPDATE = 17
|
||||
}
|
||||
51
src/webrtc/util/MediaServer.ts
Normal file
51
src/webrtc/util/MediaServer.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { WebSocket } from "@fosscord/gateway";
|
||||
import MediaServer, { IncomingStream, OutgoingStream, Transport } from "medooze-media-server";
|
||||
import SemanticSDP from "semantic-sdp";
|
||||
MediaServer.enableLog(true);
|
||||
|
||||
export const PublicIP = process.env.PUBLIC_IP || "127.0.0.1";
|
||||
|
||||
try {
|
||||
const range = process.env.WEBRTC_PORT_RANGE || "4000";
|
||||
var ports = range.split("-");
|
||||
const min = Number(ports[0]);
|
||||
const max = Number(ports[1]);
|
||||
|
||||
MediaServer.setPortRange(min, max);
|
||||
} catch (error) {
|
||||
console.error("Invalid env var: WEBRTC_PORT_RANGE", process.env.WEBRTC_PORT_RANGE, error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
export const endpoint = MediaServer.createEndpoint(PublicIP);
|
||||
|
||||
export const channels = new Map<string, Set<Client>>();
|
||||
|
||||
export interface Client {
|
||||
transport?: Transport;
|
||||
websocket: WebSocket;
|
||||
out: {
|
||||
stream?: OutgoingStream;
|
||||
tracks: Map<
|
||||
string,
|
||||
{
|
||||
audio_ssrc: number;
|
||||
video_ssrc: number;
|
||||
rtx_ssrc: number;
|
||||
}
|
||||
>;
|
||||
};
|
||||
in: {
|
||||
stream?: IncomingStream;
|
||||
audio_ssrc: number;
|
||||
video_ssrc: number;
|
||||
rtx_ssrc: number;
|
||||
};
|
||||
sdp: SemanticSDP.SDPInfo;
|
||||
channel_id: string;
|
||||
}
|
||||
|
||||
export function getClients(channel_id: string) {
|
||||
if (!channels.has(channel_id)) channels.set(channel_id, new Set());
|
||||
return channels.get(channel_id)!;
|
||||
}
|
||||
2
src/webrtc/util/index.ts
Normal file
2
src/webrtc/util/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./Constants";
|
||||
export * from "./MediaServer";
|
||||
@@ -72,6 +72,7 @@
|
||||
"@fosscord/gateway": ["./gateway/index"],
|
||||
"@fosscord/cdn": ["./cdn/index"],
|
||||
"@fosscord/util": ["./util/index"]
|
||||
"@fosscord/webrtc": ["./webrtc/index"]
|
||||
},
|
||||
"noEmitHelpers": true,
|
||||
"importHelpers": true
|
||||
|
||||
Reference in New Issue
Block a user