10.0.0.17
4444
secretobspassword
####
/home/monastery/temp/clacksproxy.sock
monasterygates
opensesame
...
####
#!/usr/bin/env perl
#---AUTOPRAGMASTART---
use 5.020;
use strict;
use warnings;
use diagnostics;
use mro 'c3';
use English qw(-no_match_vars);
use Carp;
our $VERSION = 2.4;
use Fatal qw( close );
use Array::Contains;
#---AUTOPRAGMAEND---
use IO::Socket::IP;
use Data::Dumper;
use Protocol::WebSocket::Frame;
use Time::HiRes qw[sleep];
use JSON::XS;
use XML::Simple;
use Net::Clacks::Client;
use Crypt::Digest::SHA256 qw[sha256_b64];
use Encode qw[encode_utf8 decode_utf8 is_utf8];
####
my $clacksconfig = XMLin('dskyconfig.xml');
my $obsconfig = XMLin('obsconfig.xml');
my $sock;
while(1) {
$sock = IO::Socket::IP->new(
PeerHost => $obsconfig->{ip},
PeerPort => $obsconfig->{port},
Type => SOCK_STREAM,
Blocking => 1,
);
last if(defined($sock));
print "OBS not available...\n";
sleep(10);
}
binmode($sock);
$sock->blocking(0);
####
my $clacks;
if(defined($clacksconfig->{clackssocket}) && $clacksconfig->{clackssocket} ne '') {
$clacks = Net::Clacks::Client->newSocket($clacksconfig->{clackssocket}, $clacksconfig->{clacksuser}, $clacksconfig->{clackspassword}, 'DSKY');
} else {
$clacks = Net::Clacks::Client->new($clacksconfig->{clackshost}, $clacksconfig->{clacksport}, $clacksconfig->{clacksuser}, $clacksconfig->{clackspassword}, 'DSKY');
}
$clacks->set('OBS::Alive', 1);
$clacks->listen('OBS::SelectScene');
$clacks->listen('OBS::Streaming');
$clacks->listen('OBS::Recording');
$clacks->listen('OBS::MuteVoice');
$clacks->doNetwork();
my $nextping = 0;
my $authmsgid = '';
my $lastobspacket = time;
my $sendobsversionrequest = 0;
my %scenes = (
1 => '1 ... Starting soon...',
2 => '2 ... shortbreak',
3 => '3 ... Streamending',
4 => '4 ... Desktop',
5 => '5 ... Technical Difficulties',
6 => '6 ... Camera',
);
####
binmode $sock;
my $header =
"GET / HTTP/1.1\r\n" .
"Host: localhost\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" .
"Sec-WebSocket-Protocol: chat, superchat\r\n" .
"Sec-WebSocket-Version: 13\r\n" .
"Origin: https://cavac.at\r\n" .
"\r\n";
syswrite($sock, $header);
my $line = "";
while(1) {
my $char;
sysread($sock, $char, 1);
if(defined($char) && length($char)) {
if($char eq "\r") {
next;
} elsif($char eq "\n") {
if($line eq "") {
# end of header
last;
} else {
$line = "";
}
} else {
$line .= $char;
}
}
}
my $frame = Protocol::WebSocket::Frame->new(max_payload_size => 500 * 1024 * 1024, masked => 1);
####
# Request authentication
if(1){
my %request = (
'request-type' => 'GetAuthRequired',
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
}
####
my $mutemessageid = '';
while(1) {
# Read from socket
while(1) {
my $char;
sysread($sock, $char, 1);
last if(!defined($char) || !length($char));
$frame->append($char);
}
while(my $data = $frame->next_bytes) {
my $msg = decode_json($data);
# Got any kind of OBS packet, so it's still alive. Reset our timeout and notify display that we have OBS connection
$lastobspacket = time;
$sendobsversionrequest = 0;
$clacks->set('OBS::Alive', 1);
if(defined($msg->{'authRequired'})) {
# Start authentication
my $secret = sha256_b64($obsconfig->{password} . $msg->{salt});
$secret .= $msg->{challenge};
my $auth = sha256_b64($secret);
$authmsgid = 'auth_' . int(rand(1_000_000));
my %request = (
'request-type' => 'Authenticate',
'auth' => $auth,
'message-id' => $authmsgid,
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} =~ /^auth\_/ && $msg->{'message-id'} eq $authmsgid) {
# Auth reply
if(defined($msg->{status}) && $msg->{status} eq 'ok') {
# auth ok
print "Authenticated to OBS\n";
# Enable real heartbeat
my %request = (
'request-type' => 'SetHeartbeat',
'enable' => \1, # Boolean TRUE
'message-id' => 'heartbeat_' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} else {
# Auth failed!
print "AUTHENTICATION FAILED!!!!!\n";
sleep(30);
exit(1);
}
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} =~ /^heartbeat\_/) {
if(defined($msg->{status}) && $msg->{status} eq 'ok') {
print "Heartbeat enabled\n";
} else {
print "Failed to enable heartbeat!\n";
}
####
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'Heartbeat') {
print "OBS Heartbeat\n";
} elsif(defined($msg->{'obs-studio-version'})) {
print "OBS Studio version: ", $msg->{'obs-studio-version'}, "\n";
print "OBS Websocket version: ", $msg->{'obs-websocket-version'}, "\n";
####
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'SwitchScenes') {
my $scene = substr $msg->{'scene-name'}, 0, 1;
print "Selected scene: ", $scene, "\n";
$clacks->set('OBS::SceneSelected', $scene);
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'StreamStatus') {
if($msg->{'streaming'}) {
$clacks->set('OBS::StreamStatus', 1);
} else {
$clacks->set('OBS::StreamStatus', 0);
}
} elsif(defined($msg->{'update-type'}) && ($msg->{'update-type'} eq 'RecordingStarted' ||
$msg->{'update-type'} eq 'RecordingResumed')) {
$clacks->set('OBS::RecordingStatus', 1);
} elsif(defined($msg->{'update-type'}) && ($msg->{'update-type'} eq 'RecordingStopped' ||
$msg->{'update-type'} eq 'RecordingPaused')) {
$clacks->set('OBS::RecordingStatus', 0);
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'TransitionBegin') {
print "Transitioning to new scene\n";
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'SourceMuteStateChanged') {
# # We get the info that mute has changed, but not it's new status. Let's request it
my %request = (
'request-type' => 'GetMute',
'message-id' => 'rand' . int(rand(1_000_000)),
'source' => 'Mike',
);
$mutemessageid = $request{'message-id'};
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} eq $mutemessageid) {
# Check if we got muted or not
my $ismuted = $msg->{muted};
$mutemessageid = '';
$clacks->set('OBS::MutedVoice', $ismuted);
print "Mike muted: ", $ismuted, "\n";
} else {
print "Opcode: ", $frame->opcode, "\n";
print "iscont ", $frame->is_continuation, "\n";
print Dumper($msg);
}
}
####
$clacks->doNetwork();
# No data packet for more than 10 seconds, send a "OBS Version" request *once*
if(!$sendobsversionrequest && (time - $lastobspacket) > 10) {
print "No heartbeat from OBS, requesting livetick as a backup measure\n";
my %request = (
'request-type' => 'GetVersion',
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
$sendobsversionrequest = 1;
}
# No data packet for more than 20 seconds and we already send a version request!
# We are pretty sure that OBS is not available. So notify display, and shut down program cleanly
# (will be externally restarted by bash script)
if($sendobsversionrequest && (time - $lastobspacket) > 20) {
print "Can't get info from OBS\n";
$clacks->set('OBS::Alive', 0);
for(1..10) {
$clacks->doNetwork();
sleep(0.1);
}
$clacks = undef;
exit(0);
}
####
$clacks->doNetwork();
while(my $msg = $clacks->getNext()) {
if($msg->{type} eq 'serverinfo') {
print "Connected to clacks server with version ", $msg->{data}, "\n";
next;
}
if($msg->{type} eq 'disconnect') {
# Disconnected from clacks, wait 10 seconds and then restart
print "Clacks disconnect\n";
sleep(10);
exit(0);
}
next unless($msg->{type} eq 'set');
if($msg->{name} eq 'OBS::SelectScene') {
if(!defined($scenes{$msg->{data}})) {
print "Invalid scene selected\n";
}
my %request = (
'request-type' => 'SetCurrentScene',
'scene-name' => $scenes{$msg->{data}},
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::MuteVoice') {
my $ismute = $msg->{data};
my %request = (
'request-type' => 'SetMute',
'message-id' => 'rand' . int(rand(1_000_000)),
'source' => 'Mike',
'mute' => \1,
);
if(!$ismute) {
$request{mute} = \0;
}
my $buffer = encode_json(\%request);
print "--\n", $buffer, "\n--\n";
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::Streaming') {
my $type = 'StopStreaming';
if($msg->{data} == 1) {
$type = 'StartStreaming';
}
my %request = (
'request-type' => $type,
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::Recording') {
my $type = 'StopRecording';
if($msg->{data} == 1) {
$type = 'StartRecording';
}
my %request = (
'request-type' => $type,
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
}
}
if($nextping < time) {
$clacks->ping();
$nextping = time + 30;
}
$clacks->doNetwork();
}
####
#!/usr/bin/env perl
#---AUTOPRAGMASTART---
use 5.020;
use strict;
use warnings;
use diagnostics;
use mro 'c3';
use English qw(-no_match_vars);
use Carp;
our $VERSION = 2.4;
use Fatal qw( close );
use Array::Contains;
#---AUTOPRAGMAEND---
use IO::Socket::IP;
use Data::Dumper;
use Protocol::WebSocket::Frame;
use Time::HiRes qw[sleep];
use JSON::XS;
use XML::Simple;
use Net::Clacks::Client;
use Crypt::Digest::SHA256 qw[sha256_b64];
use Encode qw[encode_utf8 decode_utf8 is_utf8];
my $clacksconfig = XMLin('dskyconfig.xml');
my $obsconfig = XMLin('obsconfig.xml');
my $sock;
while(1) {
$sock = IO::Socket::IP->new(
PeerHost => $obsconfig->{ip},
PeerPort => $obsconfig->{port},
Type => SOCK_STREAM,
Blocking => 1,
);
last if(defined($sock));
print "OBS not available...\n";
sleep(10);
}
binmode($sock);
$sock->blocking(0);
my $clacks;
if(defined($clacksconfig->{clackssocket}) && $clacksconfig->{clackssocket} ne '') {
$clacks = Net::Clacks::Client->newSocket($clacksconfig->{clackssocket}, $clacksconfig->{clacksuser}, $clacksconfig->{clackspassword}, 'DSKY');
} else {
$clacks = Net::Clacks::Client->new($clacksconfig->{clackshost}, $clacksconfig->{clacksport}, $clacksconfig->{clacksuser}, $clacksconfig->{clackspassword}, 'DSKY');
}
$clacks->set('OBS::Alive', 1);
$clacks->listen('OBS::SelectScene');
$clacks->listen('OBS::Streaming');
$clacks->listen('OBS::Recording');
$clacks->listen('OBS::MuteVoice');
$clacks->doNetwork();
my $nextping = 0;
my $authmsgid = '';
my $lastobspacket = time;
my $sendobsversionrequest = 0;
my %scenes = (
1 => '1 ... Starting soon...',
2 => '2 ... shortbreak',
3 => '3 ... Streamending',
4 => '4 ... Desktop',
5 => '5 ... Technical Difficulties',
6 => '6 ... Camera',
);
binmode $sock;
my $header =
"GET / HTTP/1.1\r\n" .
"Host: localhost\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" .
"Sec-WebSocket-Protocol: chat, superchat\r\n" .
"Sec-WebSocket-Version: 13\r\n" .
"Origin: https://cavac.at\r\n" .
"\r\n";
syswrite($sock, $header);
my $line = "";
while(1) {
my $char;
sysread($sock, $char, 1);
if(defined($char) && length($char)) {
if($char eq "\r") {
next;
} elsif($char eq "\n") {
if($line eq "") {
# end of header
last;
} else {
$line = "";
}
} else {
$line .= $char;
}
}
}
my $frame = Protocol::WebSocket::Frame->new(max_payload_size => 500 * 1024 * 1024, masked => 1);
# Request authentication
if(1){
my %request = (
'request-type' => 'GetAuthRequired',
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
}
if(0){
}
my $mutemessageid = '';
while(1) {
# Read from socket
while(1) {
my $char;
sysread($sock, $char, 1);
last if(!defined($char) || !length($char));
$frame->append($char);
}
while(my $data = $frame->next_bytes) {
my $msg = decode_json($data);
# Got any kind of OBS packet, so it's still alive. Reset our timeout and notify display that we have OBS connection
$lastobspacket = time;
$sendobsversionrequest = 0;
$clacks->set('OBS::Alive', 1);
if(defined($msg->{'authRequired'})) {
# Start authentication
my $secret = sha256_b64($obsconfig->{password} . $msg->{salt});
$secret .= $msg->{challenge};
my $auth = sha256_b64($secret);
$authmsgid = 'auth_' . int(rand(1_000_000));
my %request = (
'request-type' => 'Authenticate',
'auth' => $auth,
'message-id' => $authmsgid,
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} =~ /^auth\_/ && $msg->{'message-id'} eq $authmsgid) {
# Auth reply
if(defined($msg->{status}) && $msg->{status} eq 'ok') {
# auth ok
print "Authenticated to OBS\n";
# Enable real heartbeat
my %request = (
'request-type' => 'SetHeartbeat',
'enable' => \1, # Boolean TRUE
'message-id' => 'heartbeat_' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} else {
# Auth failed!
print "AUTHENTICATION FAILED!!!!!\n";
sleep(30);
exit(1);
}
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} =~ /^heartbeat\_/) {
if(defined($msg->{status}) && $msg->{status} eq 'ok') {
print "Heartbeat enabled\n";
} else {
print "Failed to enable heartbeat!\n";
}
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'SwitchScenes') {
my $scene = substr $msg->{'scene-name'}, 0, 1;
print "Selected scene: ", $scene, "\n";
$clacks->set('OBS::SceneSelected', $scene);
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'StreamStatus') {
if($msg->{'streaming'}) {
$clacks->set('OBS::StreamStatus', 1);
} else {
$clacks->set('OBS::StreamStatus', 0);
}
} elsif(defined($msg->{'update-type'}) && ($msg->{'update-type'} eq 'RecordingStarted' ||
$msg->{'update-type'} eq 'RecordingResumed')) {
$clacks->set('OBS::RecordingStatus', 1);
} elsif(defined($msg->{'update-type'}) && ($msg->{'update-type'} eq 'RecordingStopped' ||
$msg->{'update-type'} eq 'RecordingPaused')) {
$clacks->set('OBS::RecordingStatus', 0);
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'TransitionBegin') {
print "Transitioning to new scene\n";
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'Heartbeat') {
print "OBS Heartbeat\n";
} elsif(defined($msg->{'obs-studio-version'})) {
print "OBS Studio version: ", $msg->{'obs-studio-version'}, "\n";
print "OBS Websocket version: ", $msg->{'obs-websocket-version'}, "\n";
} elsif(defined($msg->{'update-type'}) && $msg->{'update-type'} eq 'SourceMuteStateChanged') {
# # We get the info that mute has changed, but not it's new status. Let's request it
my %request = (
'request-type' => 'GetMute',
'message-id' => 'rand' . int(rand(1_000_000)),
'source' => 'Mike',
);
$mutemessageid = $request{'message-id'};
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif(defined($msg->{'message-id'}) && $msg->{'message-id'} eq $mutemessageid) {
# Check if we got muted or not
my $ismuted = $msg->{muted};
$mutemessageid = '';
$clacks->set('OBS::MutedVoice', $ismuted);
print "Mike muted: ", $ismuted, "\n";
} else {
print "Opcode: ", $frame->opcode, "\n";
print "iscont ", $frame->is_continuation, "\n";
print Dumper($msg);
}
}
$clacks->doNetwork();
# No data packet for more than 10 seconds, send a "OBS Version" request *once*
if(!$sendobsversionrequest && (time - $lastobspacket) > 10) {
print "No heartbeat from OBS, requesting livetick as a backup measure\n";
my %request = (
'request-type' => 'GetVersion',
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
$sendobsversionrequest = 1;
}
# No data packet for more than 20 seconds and we already send a version request!
# We are pretty sure that OBS is not available. So notify display, and shut down program cleanly
# (will be externally restarted by bash script)
if($sendobsversionrequest && (time - $lastobspacket) > 20) {
print "Can't get info from OBS\n";
$clacks->set('OBS::Alive', 0);
for(1..10) {
$clacks->doNetwork();
sleep(0.1);
}
$clacks = undef;
exit(0);
}
$clacks->doNetwork();
while(my $msg = $clacks->getNext()) {
if($msg->{type} eq 'serverinfo') {
print "Connected to clacks server with version ", $msg->{data}, "\n";
next;
}
if($msg->{type} eq 'disconnect') {
# Disconnected from clacks, wait 10 seconds and then restart
print "Clacks disconnect\n";
sleep(10);
exit(0);
}
next unless($msg->{type} eq 'set');
if($msg->{name} eq 'OBS::SelectScene') {
if(!defined($scenes{$msg->{data}})) {
print "Invalid scene selected\n";
}
my %request = (
'request-type' => 'SetCurrentScene',
'scene-name' => $scenes{$msg->{data}},
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::MuteVoice') {
my $ismute = $msg->{data};
my %request = (
'request-type' => 'SetMute',
'message-id' => 'rand' . int(rand(1_000_000)),
'source' => 'Mike',
'mute' => \1,
);
if(!$ismute) {
$request{mute} = \0;
}
my $buffer = encode_json(\%request);
print "--\n", $buffer, "\n--\n";
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::Streaming') {
my $type = 'StopStreaming';
if($msg->{data} == 1) {
$type = 'StartStreaming';
}
my %request = (
'request-type' => $type,
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
} elsif($msg->{name} eq 'OBS::Recording') {
my $type = 'StopRecording';
if($msg->{data} == 1) {
$type = 'StartRecording';
}
my %request = (
'request-type' => $type,
'message-id' => 'rand' . int(rand(1_000_000)),
);
my $outframe = $frame->new(buffer => encode_json(\%request), type => 'text', masked => 1)->to_bytes;
syswrite($sock, $outframe);
}
}
if($nextping < time) {
$clacks->ping();
$nextping = time + 30;
}
$clacks->doNetwork();
}