Convos

 view release on metacpan or  search on metacpan

lib/Convos.pm  view on Meta::CPAN

    # Using push() since I don't think it's a good idea for allowing the user
    # to customize every template, at least not when the application is still
    # unstable.
    push @{$self->renderer->paths}, $ENV{CONVOS_TEMPLATES};
  }

  $self->defaults(full_page => 1, organization_name => $self->config('name'));
  $self->hook(before_dispatch => \&_before_dispatch);
  $self->_embed_backend if $ENV{CONVOS_BACKEND_EMBEDDED};

  Scalar::Util::weaken($self);
  Mojo::IOLoop->timer(0 => sub { $self->_set_secrets });
}

sub _assets {
  my $self = shift;

  $self->plugin('AssetPack');
  $self->plugin('FontAwesome4', css => []);
  $self->asset('c.css' => qw( /scss/font-awesome.scss /sass/convos.scss ));
  $self->asset(

lib/Convos/Controller/Chat.pm  view on Meta::CPAN


=cut

sub socket {
  my $self  = shift;
  my $login = $self->session('login');
  my $key   = "convos:user:$login:out";

  $self->_schedule_day_changed_event;
  $self->inactivity_timeout(60);
  Scalar::Util::weaken($self);

  # from browser to backend
  $self->on(
    message => sub {
      my ($self, $octets) = @_;
      my $dom;

      $self->logf(debug => '[ws] < %s', $octets);

      if ($octets eq 'PING') {

lib/Convos/Controller/Chat.pm  view on Meta::CPAN

      $self->redis->rpush("user:$login:cmd_history", $dom->text(0));
      $self->redis->ltrim("user:$login:cmd_history", -30, -1);
    }
  }
}

sub _schedule_day_changed_event {
  my $self = shift;
  my $t    = 86400 - time % 86400;

  Scalar::Util::weaken($self);
  $self->stash(
    day_changed_event_tid => Mojo::IOLoop->timer(
      $t => sub {
        $self or return;
        $self->send_partial('event/day_changed', timestamp => time + 1,);
        $self->_schedule_day_changed_event;
      }
    )
  );
}

lib/Convos/Core.pm  view on Meta::CPAN


  die "Convos::Core is already started" if $self->{start}++;

  # TODO: Remove in future versions and/or move to Convos::Upgrader
  $self->redis->del($_)
    for qw( convos:backend:lock convos:backend:pid convos:backend:started convos:host2convos convos:loopback:names );

  $self->redis->del('core:control');    # need to clear instructions queued while backend was stopped
  $self->_start_control_channel;

  Scalar::Util::weaken($self);
  Mojo::IOLoop->delay(
    sub {
      my ($delay) = @_;
      $self->redis->smembers('connections', $delay->begin);
    },
    sub {
      my ($delay, $connections) = @_;
      for my $id (@$connections) {
        $self->_connection($id)->_state('disconnected')->{core_connect_timer} = 1;
      }

lib/Convos/Core.pm  view on Meta::CPAN

    }
  );

  return $self;
}

sub _start_control_channel {
  my $self = shift;
  my $cb;

  Scalar::Util::weaken($self);

  $cb = sub {
    my ($redis, $instruction) = @_;
    $redis->brpop($instruction->[0], 0, $cb);
    $instruction->[1] or return;
    my ($command, $login, $name) = split /:/, $instruction->[1];
    my $action = "ctrl_$command";
    $self->$action($login, $name);
  };

lib/Convos/Core.pm  view on Meta::CPAN

  my $validation = $self->_validation($input, qw( login name nick password server username ));

  if ($validation->has_error) {
    $self->$cb($validation, undef);
    return $self;
  }

  my ($login, $name) = $validation->param([qw( login name )]);

  warn "[core:$login] add ", _dumper($validation->output), "\n" if DEBUG;
  Scalar::Util::weaken($self);
  Mojo::IOLoop->delay(
    sub {
      my ($delay) = @_;
      $self->redis->exists("user:$login:connection:$name", $delay->begin);
    },
    sub {
      my ($delay, $exists) = @_;

      if ($exists) {
        $validation->error(name => ['exists']);

lib/Convos/Core.pm  view on Meta::CPAN


Stop a connection by connection id.

=cut

sub ctrl_stop {
  my ($self, $login, $server) = @_;
  my $id = join ':', $login, $server;
  my $conn = $self->{connections}{$id} or return;

  Scalar::Util::weaken($self);
  $conn->disconnect(sub { delete $self->{connections}{$id} });
}

=head2 ctrl_restart

  $self->ctrl_restart($login, $server);

Restart a connection by connection id.

=cut

sub ctrl_restart {
  my ($self, $login, $server) = @_;
  my $id = join ':', $login, $server;

  if (my $conn = $self->{connections}{$id}) {
    Scalar::Util::weaken($self);
    $conn->disconnect(
      sub {
        delete $self->{connections}{$id};
        $self->ctrl_start($login => $server);
      }
    );
  }
  else {
    $self->ctrl_start($login => $server);
  }

lib/Convos/Core.pm  view on Meta::CPAN

    }
  );
}

sub _connection {
  my ($self, $id) = @_;
  my $conn = $self->{connections}{$id};

  unless ($conn) {
    my ($login, $name) = split /:/, $id;
    Scalar::Util::weaken($self);
    $conn = Convos::Core::Connection->new(redis => $self->redis, log => $self->log, login => $login, name => $name);
    $conn->on(save => sub { $_[1]->{message} and $_[1]->{timestamp} and $self->archive->save(@_); });
    $self->{connections}{$id} = $conn;
  }

  $conn;
}

sub _dumper {    # function
  Data::Dumper->new([@_])->Indent(0)->Sortkeys(1)->Terse(1)->Dump;

lib/Convos/Core/Connection.pm  view on Meta::CPAN

  irc_rpl_listend irc_mode irc_quit irc_kick irc_error
  irc_rpl_namreply irc_rpl_endofnames err_nicknameinuse
);

has _irc => sub {
  my $self = shift;
  my $irc = Mojo::IRC->new(debug_key => join ':', $self->login, $self->name);

  $irc->parser(Parse::IRC->new(ctcp => 1));

  Scalar::Util::weaken($self);
  $irc->register_default_event_handlers;
  $irc->on(close => sub { $self->_irc_close });
  $irc->on(error => sub { $self->_irc_error($_[1]) });

  for my $event (@ADD_MESSAGE_EVENTS) {
    $irc->on($event => sub { $self->add_message($_[1]) });
  }
  for my $event (@ADD_SERVER_MESSAGE_EVENTS) {
    $irc->on($event => sub { $self->add_server_message($_[1]) });
  }

lib/Convos/Core/Connection.pm  view on Meta::CPAN


Attributes fetched from backend: nick, user, host and channels. The latter
is set in L</channels> and used by L</irc_rpl_welcome>.

=cut

sub connect {
  my ($self) = @_;
  my $irc = $self->_irc;

  Scalar::Util::weaken($self);
  $self->{core_connect_timer} = 0;
  $self->{keepnick_tid} ||= $irc->ioloop->recurring(60 => sub { $self->_steal_nick });
  $self->_subscribe;

  $self->redis->execute(
    [hgetall => $self->{path}],
    [get     => 'convos:frontend:url'],
    sub {
      my ($redis, $args, $url) = @_;
      $self->redis->hset($self->{path} => tls => $self->{disable_tls} ? 0 : 1);

lib/Convos/Core/Connection.pm  view on Meta::CPAN

      my ($delay, $nick) = @_;
      $self->_irc->write(NICK => $nick) if $nick and $self->_irc->nick ne $nick;
    }
  );
}

sub _subscribe {
  my $self = shift;
  my $irc  = $self->_irc;

  Scalar::Util::weaken($self);
  $self->{messages} = $self->redis->subscribe("convos:user:@{[$self->login]}:@{[$self->name]}");
  $self->{messages}->on(
    error => sub {
      my ($sub, $error) = @_;
      $self->log->warn("[$self->{path}] Re-subcribing to messages to @{[$irc->name]}. ($error)");
      $self->_subscribe;
    },
  );
  $self->{messages}->on(
    message => sub {

lib/Convos/Core/Connection.pm  view on Meta::CPAN


:Zurich.CH.EU.Undernet.Org 001 somenick :Welcome to the UnderNet IRC Network, somenick

=cut

sub irc_rpl_welcome {
  my ($self, $message) = @_;

  $self->{attempts} = 0;

  Scalar::Util::weaken($self);
  $self->redis->zrange(
    $self->{conversation_path},
    0, -1,
    sub {
      for my $channel ($self->channels_from_conversations($_[1])) {
        $self->redis->hget(
          "$self->{path}:$channel",
          key => sub {
            $_[1] ? $self->_irc->write(JOIN => $channel, $_[1]) : $self->_irc->write(JOIN => $channel);
          }

lib/Convos/Core/Connection.pm  view on Meta::CPAN

    command => 'QUIT',
    prefix => 'nick!~user@localhost'
  };

=cut

sub irc_quit {
  my ($self, $message) = @_;
  my ($nick) = IRC::Utils::parse_user($message->{prefix});

  Scalar::Util::weaken($self);
  $self->_publish(nick_quit => {nick => $nick, message => $message->{params}[0]});
}

=head2 irc_kick

  'raw_line' => ':testing!~marcus@home.means.no KICK #testmore :marcus_',
  'params' => [ '#testmore', 'marcus_' ],
  'command' => 'KICK',
  'handled' => 1,
  'prefix' => 'testing!~marcus@40.101.45.31.customer.cdi.no'

lib/Convos/Core/Connection.pm  view on Meta::CPAN


=head2 irc_part

=cut

sub irc_part {
  my ($self, $message) = @_;
  my ($nick) = IRC::Utils::parse_user($message->{prefix});
  my $channel = lc $message->{params}[0];

  Scalar::Util::weaken($self);
  if ($nick eq $self->_irc->nick) {
    my $name = as_id $self->name, $channel;

    $self->redis->zrem(
      $self->{conversation_path},
      $name,
      sub {
        $self->_publish(remove_conversation => {target => $channel});
      }
    );

lib/Convos/Core/Connection.pm  view on Meta::CPAN


=cut

sub err_bannedfromchan {
  my ($self, $message) = @_;
  my $channel = lc $message->{params}[1];
  my $name = as_id $self->name, $channel;

  $self->_publish_and_save(server_message => {status => 401, message => $message->{params}[2]});

  Scalar::Util::weaken($self);
  $self->redis->zrem(
    $self->{conversation_path},
    $name,
    sub {
      $self->_publish(remove_conversation => {target => $channel});
    }
  );
}

=head2 err_nicknameinuse

lib/Convos/Core/Connection.pm  view on Meta::CPAN

=cut

sub err_nosuchchannel {
  my ($self, $message) = @_;
  my $channel = lc $message->{params}[1];
  my $name = as_id $self->name, $channel;

  $self->_publish(server_message => {status => 400, message => qq(No such channel "$channel")});

  if ($channel =~ /^[#&]/) {
    Scalar::Util::weaken($self);
    $self->redis->zrem(
      $self->{conversation_path},
      $name,
      sub {
        $self->_publish(remove_conversation => {target => $channel});
      }
    );
  }
}



( run in 0.795 second using v1.01-cache-2.11-cpan-65fba6d93b7 )