package CF::Controller::Websockets; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Pg::PubSub; use Digest::SHA qw(hmac_sha1_hex); use Encode qw(encode decode); use feature 'signatures'; no warnings qw{ experimental::signatures }; use constant SOCKET_INACTIVITY_TIMEOUT => 180; use constant ALIVE_TIME => 60; sub main { my $c = shift; $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT); $c->tx->with_compression; my $key = $c->req->headers->header('Sec-WebSocket-Key'); my $pubsub = $c->redis->pubsub; my $user; my $event_id = 0; #TODO - parametr! my $listener = $pubsub->listen( "notify:$event_id" => sub($pubsub, $payload, $channel) { $c->send(decode("UTF-8", $payload)); } ); my $listener_online = $pubsub->listen( "online:$event_id" => sub($pubsub, $payload, $channel) { my @counts = split ' ', $payload; $c->send({json => { event => 'online_users_updated', payload => { all => $counts[0]+0, members => $counts[1]+0, group_size_full => $counts[2]+0, group_size_half => $counts[3]+0, } }}); } ); $c->on(json => sub( $c, $message ) { if ( $message->{event} eq 'KEEPALIVE' ) { my $is_member = 0; if ($message->{payload} =~ /^\d+$/) { $user ||= $c->schema->resultset('User')->find({ id => $message->{payload}, }); } if ( $user ) { my $sig = hmac_sha1_hex($message->{payload}, $user->secret ); if ( $sig ne $message->{sig} ) { $c->app->log->warn( "Invalid signature for " . $user->username ); } else { $user->add_to_alive({ event_id => $event_id }); $is_member = 1 if $user && $user->roles =~ /member/; my $jitsi = $user->jitsi_allowed || $user->roles =~ /chairman|jitsi/; $c->send({json => { event => 'user_status', payload => { jitsi_allowed => $jitsi ? \1:\0, is_banned => $user->banned_until ? \1:\0, }}}); } } $c->redis->db->set( join (':', ('live', $event_id, $is_member, $is_member ? $user->id : $key)), 'live', 'EX', ALIVE_TIME ); } }); $c->on(finish => sub ($c, $code, $reason = undef) { $pubsub->unlisten("notify:$event_id", $listener); $pubsub->unlisten("online:$event_id", $listener_online); $c->app->log->debug("WebSocket closed with status $code"); }); } 1;