ユーザーコネクションに関するイベントが呼ばれます。コネクションへのJIDのバインドや、コネクションの切断、コネクションの状態であるプレゼンスに関するイベントはこのクラスに実装していきます。
プロトコルガイドのログインからログアウトまでのケーススタディにおけるリソースバインディング、イニシャルプレゼンス、ログアウト のそれぞれのセクションを参照するとよいでしょう。
リソースバインディング要求に対する処理を、このイベントハンドラで実装します。
リソースバインディングについて詳しくは、ログインからログアウトまでのケーススタディ [ リソースバインディング ]を参照するとよいでしょう。
sub on_bind_request {
my ($self, $ctx, $args) = @_;
my $stream_id = $args->stream_id;
my $user_id = $args->user_id;
$self->log_debug("on_bind_request");
my $user = $ctx->get('db')->find_user_by_id($user_id);
unless ($user) {
$self->log_debug("user not found");
return;
}
my $resource = $args->resource;
unless ($resource) {
Ocean::Error::ProtocolError->throw(
message => q{resource not found},
);
}
my $jid = Ocean::JID->build(
$user->username,
$self->domain,
$resource
);
$ctx->get('db')->insert_connection(
user_id => $user->user_id,
username => $user->username,
resource => $resource,
#presence => undef,
);
my $builder =
Ocean::Stanza::DeliveryRequestBuilder::BoundJID->new;
$builder->stream_id($stream_id);
$builder->jid($jid);
if ($args->want_extval) {
$builder->nickname($user->nickname);
$builder->photo_url($user->profile_img_url || '');
}
$ctx->deliver($builder->build());
}
引数としてOcean::HandlerArgs::BindRequestを受け取ります。このオブジェクトは次のアクセサを持ちます。
| アクセサ名 | 概要 |
|---|---|
| stream_id | 要求を送信してきたストリームのIDです |
| user_id | 認証成功時に返したユーザーIDです |
| resource | 認証成功時に発行したsession_idです |
| want_extval | 拡張仕様を要求するかどうか。要求された場合、リソースバインディング成功時に、JIDだけでなく、ニックネームと画像URLも含めて返します。 |
| セッター名 | 概要 |
|---|---|
| stream_id | 認証を要求してきたストリームのID |
| jid | 割り当てられたJID |
| nickname | |
| photo_url |
ログイン後のプレゼンス配信はこのハンドラで行われます。
SYNOPSISsub on_presence {
my ($self, $ctx, $args) = @_;
my $sender_jid = $args->from;
my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );
my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
$sender_conn->update({
presence_show => $args->show,
presence_status => $args->status,
});
my @followers = $ctx->get('db')->search_followers_of($sender);
for my $follower_id ( @followers ) {
my @follower_conns =
$ctx->get('db')->search_available_connection_by_user_id($follower_id);
for my $follower_conn ( @follower_conns ) {
my $receiver_jid = Ocean::JID->build(
$follower_conn->username,
$self->domain,
$follower_conn->resource,
);
my $builder =
Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
$builder->from($sender_jid);
$builder->to($receiver_jid);
$builder->show($args->show);
$builder->status($args->status);
$builder->image_hash($sender->profile_img_hash)
if $sender->profile_img_hash;
$ctx->deliver($builder->build());
}
}
}
引数としてOcean::HandlerArgs::Presenceを受け取ります。このオブジェクトは次のアクセサを持ちます。
| アクセサ名 | 概要 |
|---|---|
| from | 送信者のFull JIDです |
| show | 要求されたshowタイプです |
| status | 要求されたステータステキストです |
| セッター名 | 概要 |
|---|---|
| from | |
| to | |
| show | |
| status | |
| image_hash |
ログイン処理の最後、イニシャルプレゼンスに対する処理がこの中で実装されます。
イニシャルプレゼンスについて詳しくは、ログインからログアウトまでのケーススタディ [ イニシャルプレゼンス ]を参照するとよいでしょう。 SYNOPSISsub on_initial_presence {
my ($self, $ctx, $args) = @_;
my $sender_jid = $args->from;
my $no_probe = $args->no_probe;
$self->log_debug("on_initial_presence");
my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );
# UPDATE CONNECTION STATE
my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
unless ($sender_conn) {
$self->log_warn("connection for sender_jid %s not found", $sender_jid->as_string);
return;
}
$sender_conn->update({
presence_show => $args->show,
presence_status => $args->status,
});
$self->log_debug("broadcast presence");
# send presence to follower
my @followers = $ctx->get('db')->search_followers_of($sender);
for my $follower_id ( @followers ) {
my @follower_conns =
$ctx->get('db')->search_available_connection_by_user_id($follower_id);
for my $follower_conn ( @follower_conns ) {
my $receiver_jid = Ocean::JID->build(
$follower_conn->username,
$self->domain,
$follower_conn->resource,
);
$self->log_debug("deliver presence to %s",
$receiver_jid->as_string);
my $builder =
Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
$builder->from($sender_jid);
$builder->to($receiver_jid);
$builder->show($args->show);
$builder->status($args->status);
$builder->image_hash($sender->profile_img_hash)
if $sender->profile_img_hash;
$ctx->deliver($builder->build());
}
}
return if $no_probe;
$self->log_debug("probe presence");
my @followees = $ctx->get('db')->search_followees_of($sender);
for my $followee_id ( @followees ) {
my @followee_conns =
$ctx->get('db')->search_available_connection_by_user_id($followee_id);
for my $followee_conn ( @followee_conns ) {
my $followee_presence = $followee_conn->presence;
my $followee_jid = Ocean::JID->build(
$followee_conn->username,
$self->domain,
$followee_conn->resource,
);
my $followee = $ctx->get('db')->find_user_by_username( $followee_jid->node );
my $builder =
Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
$builder->from($followee_jid);
$builder->to($sender_jid);
$builder->show($followee_presence->show);
$builder->status($followee_presence->status);
$builder->image_hash($followee->profile_img_hash)
if $followee->profile_img_hash;
$ctx->deliver($builder->build());
}
}
}
引数としてOcean::HandlerArgs::Presenceを受け取ります。このオブジェクトは次のアクセサを持ちます。
| アクセサ名 | 概要 |
|---|---|
| from | 送信者のFull JIDです |
| show | 要求されたshowタイプです |
| status | 要求されたステータステキストです |
| no_probe | probeを実行するかどうかのフラグです。 |
ストリーム切断時に呼ばれます。すでに有効状態になっているストリームだった場合、このイベントハンドラが呼ばれます。そうでなかった場合は、下のon_silent_disconnectionが呼ばれます。このイベントハンドラでは、ストリームの切断に伴う後処理を適切に行うと同時に、このユーザーのストリームがオフラインになったことを、ユーザーのフォロワーのうちオンラインのストリーム全てに対して通知しなければなりません。
ログアウトについて詳しくは、ログインからログアウトまでのケーススタディ [ ログアウト ]を参照するとよいでしょう。
次の実装例を確認すると、まずコネクション情報の削除、それからフォロワーへの通知を行っていることがわかると思います。
SYNOPSISsub on_unavailable_presence {
my ($self, $ctx, $args) = @_;
my $sender_jid = $args->from;
my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );
my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
$sender_conn->delete() if $sender_conn;
# send presence to follower
my @followers = $ctx->get('db')->search_followers_of($sender);
for my $follower_id ( @followers ) {
my @follower_conns =
$ctx->get('db')->search_available_connection_by_user_id($follower_id);
for my $follower_conn ( @follower_conns ) {
my $receiver_jid = Ocean::JID->build(
$follower_conn->username,
$self->domain,
$follower_conn->resource,
);
my $builder =
Ocean::Stanza::DeliveryRequestBuilder::UnavailablePresence->new;
$builder->from($sender_jid);
$builder->to($receiver_jid);
$ctx->deliver($builder->build());
}
}
}
ここでは一般的な処理の例のみにとどまっていますが、たとえばグループチャットをサポートしていた場合は、部屋からの退出処理などをここに追加しなければなりません。
引数としてOcean::HandlerArgs::UnavailablePresenceを受け取ります。このオブジェクトは次のアクセサを持ちます。
| アクセサ名 | 概要 |
|---|---|
| from | 送信者のFull JID |
| セッター名 | 概要 |
|---|---|
| from | 送信者のFull JID |
| to | 宛先のFull JID |
ストリーム切断時に呼ばれます。まだ有効状態になっていないストリームが切断された場合に、このイベントハンドラが呼ばれます。ストリームが有効状態になっていたら、このイベントハンドラでなく、上のon_unavailable_presenceが呼ばれます。このイベントハンドラでは、ストリームの切断に伴う後処理のみを行い、フォロワーに対する通知などは行いません。
次の実装例を見ると、on_unavailable_presenceのコードから、フォロワーへの通知をしているパートを抜いただけになっているのが分かると思います。
SYNOPSISsub on_silent_disconnection {
my ($self, $ctx, $args) = @_;
my $sender_jid = $args->from;
my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );
my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
$sender_conn->delete() if $sender_conn;
}
引数としてOcean::HandlerArgs::SilentDisconnectionを受け取ります。このオブジェクトは次のアクセサを持ちます。
| アクセサ名 | 概要 |
|---|---|
| from | 送信者のFull JIDです |
配送処理は必要ないので、対応する配送リクエストビルダーもありません。