Get TURN config from shittyserver instead of hard-coding

This commit is contained in:
Marks Polakovs 2020-04-24 11:27:08 +02:00
parent 90c082ab2c
commit 327f25f901
4 changed files with 100 additions and 61 deletions

View file

@ -29,14 +29,18 @@ parso==0.6.2
pluggy==0.13.1 pluggy==0.13.1
pycparser==2.20 pycparser==2.20
pyee==7.0.1 pyee==7.0.1
PyJWT==1.7.1
pylibsrtp==0.6.6 pylibsrtp==0.6.6
pyls==0.1.6 pyls==0.1.6
pyls-mypy==0.1.8 pyls-mypy==0.1.8
python-dateutil==2.8.1
python-jsonrpc-server==0.3.4 python-jsonrpc-server==0.3.4
python-language-server==0.31.9 python-language-server==0.31.9
pytz==2019.3
raygun4py==4.3.0 raygun4py==4.3.0
requests==2.23.0 requests==2.23.0
six==1.14.0 six==1.14.0
twilio==6.38.1
typed-ast==1.4.1 typed-ast==1.4.1
typing-extensions==3.7.4.2 typing-extensions==3.7.4.2
ujson==1.35 ujson==1.35

View file

@ -2,11 +2,19 @@
key = CHANGEME key = CHANGEME
enable = False enable = False
[twilio]
account_sid = CHANGEME
auth_token = CHANGEME
[time]
local_timezone = Europe/London
[shittyserver] [shittyserver]
notify_url = https://example.com notify_url = https://example.com
websocket_port = 8079 websocket_port = 8079
telnet_port = 8078 telnet_port = 8078
turn_provider = twilio
[stateserver] [stateserver]
myradio_key = CHANGEME myradio_key = CHANGEME
sustainer_autonews = True sustainer_autonews = True

View file

@ -7,7 +7,7 @@ import sys
import uuid import uuid
from datetime import datetime, timezone from datetime import datetime, timezone
from types import TracebackType from types import TracebackType
from typing import Optional, Any, Type, Dict from typing import Optional, Any, Type, Dict, List
import aiohttp import aiohttp
import av # type: ignore import av # type: ignore
@ -21,18 +21,17 @@ config = configparser.RawConfigParser()
config.read("serverconfig.ini") config.read("serverconfig.ini")
if config.get("raygun", "enable") == "True": if config.get("raygun", "enable") == "True":
def handle_exception( def handle_exception(
exc_type: Type[BaseException], exc_type: Type[BaseException],
exc_value: BaseException, exc_value: BaseException,
exc_traceback: TracebackType, exc_traceback: TracebackType,
) -> None: ) -> None:
sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.__excepthook__(exc_type, exc_value, exc_traceback)
cl = raygunprovider.RaygunSender(config.get("raygun", "key")) cl = raygunprovider.RaygunSender(config.get("raygun", "key"))
cl.send_exception(exc_info=(exc_type, exc_value, exc_traceback)) cl.send_exception(exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_exception
sys.excepthook = handle_exception
file_contents_ex = re.compile(r"^ws=\d$") file_contents_ex = re.compile(r"^ws=\d$")
@ -54,6 +53,42 @@ def write_ob_status(status: bool) -> None:
fd.truncate() fd.truncate()
TurnCredentials = List[Dict[str, Any]]
def get_turn_credentials() -> TurnCredentials:
provider = config.get("shittyserver", "turn_provider")
if provider == "twilio":
from twilio.rest import Client # type: ignore
client = Client(config.get("twilio", "account_sid"), config.get("twilio", "auth_token"))
token = client.tokens.create()
# Twilio's typedef is wrong, reee
# noinspection PyTypeChecker
return token.ice_servers # type: ignore
elif provider == "hardcoded":
return [
{
"urls": ["stun:eu-turn4.xirsys.com"],
},
{
"username":
"h42bRBHL2GtRTiQRoXN8GCG-PFYMl4Acel6EQ9xINBWdTpoZyBEGyCcJBCtT3iINAAAAAF5_NJptYXJrc3BvbGFrb3Zz",
"credential": "17e834fa-70e7-11ea-a66c-faa4ea02ad5c",
"urls": [
"turn:eu-turn4.xirsys.com:80?transport=udp",
"turn:eu-turn4.xirsys.com:3478?transport=udp",
"turn:eu-turn4.xirsys.com:80?transport=tcp",
"turn:eu-turn4.xirsys.com:3478?transport=tcp",
"turns:eu-turn4.xirsys.com:443?transport=tcp",
"turns:eu-turn4.xirsys.com:5349?transport=tcp",
],
},
]
else:
raise Exception("unknown provider " + provider)
@Jack.set_error_function # type: ignore @Jack.set_error_function # type: ignore
def error(msg: str) -> None: def error(msg: str) -> None:
print("Error:", msg) print("Error:", msg)
@ -177,8 +212,8 @@ class Session(object):
await self.pc.close() await self.pc.close()
if ( if (
self.websocket is not None self.websocket is not None
and self.websocket.state == websockets.protocol.State.OPEN and self.websocket.state == websockets.protocol.State.OPEN
): ):
try: try:
await self.websocket.send(json.dumps({"kind": "DIED"})) await self.websocket.send(json.dumps({"kind": "DIED"}))
@ -311,9 +346,11 @@ class Session(object):
self.websocket = websocket self.websocket = websocket
self.connection_state = "HELLO" self.connection_state = "HELLO"
print(self.connection_id, "Connected") print(self.connection_id, "Connected")
ice_config = get_turn_credentials()
print(self.connection_id, "Obtained ICE")
# TODO Raygun user ID # TODO Raygun user ID
await websocket.send( await websocket.send(
json.dumps({"kind": "HELLO", "connectionId": self.connection_id}) json.dumps({"kind": "HELLO", "connectionId": self.connection_id, "iceServers": ice_config})
) )
try: try:
@ -353,7 +390,7 @@ print("Shittyserver WS starting on port {}.".format(config.get("shittyserver", "
async def telnet_server( async def telnet_server(
reader: asyncio.StreamReader, writer: asyncio.StreamWriter reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None: ) -> None:
global active_sessions, live_session global active_sessions, live_session
while True: while True:
@ -374,15 +411,15 @@ async def telnet_server(
result[sid] = sess.to_dict() result[sid] = sess.to_dict()
writer.write( writer.write(
( (
json.dumps( json.dumps(
{ {
"live": live_session.to_dict() "live": live_session.to_dict()
if live_session is not None if live_session is not None
else None, else None,
"active": result, "active": result,
} }
) )
+ "\r\n" + "\r\n"
).encode("utf-8") ).encode("utf-8")
) )

View file

@ -29,46 +29,6 @@ export class WebRTCStreamer extends Streamer {
async start(): Promise<void> { async start(): Promise<void> {
console.log("RTCStreamer start"); console.log("RTCStreamer start");
this.pc = new RTCPeerConnection({
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
],
},
{
urls: ["stun:eu-turn4.xirsys.com"],
},
{
username:
"h42bRBHL2GtRTiQRoXN8GCG-PFYMl4Acel6EQ9xINBWdTpoZyBEGyCcJBCtT3iINAAAAAF5_NJptYXJrc3BvbGFrb3Zz",
credential: "17e834fa-70e7-11ea-a66c-faa4ea02ad5c",
urls: [
"turn:eu-turn4.xirsys.com:80?transport=udp",
"turn:eu-turn4.xirsys.com:3478?transport=udp",
"turn:eu-turn4.xirsys.com:80?transport=tcp",
"turn:eu-turn4.xirsys.com:3478?transport=tcp",
"turns:eu-turn4.xirsys.com:443?transport=tcp",
"turns:eu-turn4.xirsys.com:5349?transport=tcp",
],
},
],
});
this.pc.oniceconnectionstatechange = (e) => {
if (!this.pc) {
throw new Error(
"Received ICEConnectionStateChange but PC was null?????"
);
}
console.log("ICE Connection state change: " + this.pc.iceConnectionState);
this.onStateChange(this.mapStateToConnectionState());
};
this.stream.getAudioTracks().forEach((track) => this.pc!.addTrack(track));
this.addConnectionStateListener((state) => { this.addConnectionStateListener((state) => {
if (state === "CONNECTED") { if (state === "CONNECTED") {
this.newsInterval = later.setInterval( this.newsInterval = later.setInterval(
@ -83,7 +43,6 @@ export class WebRTCStreamer extends Streamer {
} }
}); });
console.log("PC created");
this.ws = new WebSocket(process.env.REACT_APP_WS_URL!); this.ws = new WebSocket(process.env.REACT_APP_WS_URL!);
this.ws.onopen = (e) => { this.ws.onopen = (e) => {
console.log("WS open"); console.log("WS open");
@ -156,6 +115,9 @@ export class WebRTCStreamer extends Streamer {
if (this.state !== "HELLO") { if (this.state !== "HELLO") {
this.ws!.close(); this.ws!.close();
} }
this.createPeerConnection(data.iceServers);
if (!this.pc) { if (!this.pc) {
throw new Error( throw new Error(
"Tried to do websocket fuckery with a null PeerConnection!" "Tried to do websocket fuckery with a null PeerConnection!"
@ -225,6 +187,34 @@ export class WebRTCStreamer extends Streamer {
} }
} }
createPeerConnection(iceServers: RTCIceServer[]) {
this.pc = new RTCPeerConnection({
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:19302",
"stun:stun2.l.google.com:19302",
"stun:stun3.l.google.com:19302",
"stun:stun4.l.google.com:19302",
],
},
...iceServers,
],
});
this.pc.oniceconnectionstatechange = (e) => {
if (!this.pc) {
throw new Error(
"Received ICEConnectionStateChange but PC was null?????"
);
}
console.log("ICE Connection state change: " + this.pc.iceConnectionState);
this.onStateChange(this.mapStateToConnectionState());
};
this.stream.getAudioTracks().forEach((track) => this.pc!.addTrack(track));
console.log("PC created");
}
async getStatistics() { async getStatistics() {
if (this.pc) { if (this.pc) {
return await this.pc.getStats(); return await this.pc.getStats();