user

Instances of the user package represent IRC users, both local and remote. The package is provided by the ircd::user submodule of ircd.

Low-level methods

These methods are most often used internally. Many of them exist merely to standardize the way certain user fields are stored and retrieved.

Unlike the many high-level methods, these low-level ones do not notify local clients or uplinks about any changes. Most modules will find the high-level methods more useful because they deal with the logistics associated with the changing of user data.

user->new(%opts)

Creates a new user object.

This low-level initializer should almost never be used directly; look at pool->new_user() instead.

my $user = $pool->new_user(
    nick  => 'Steve',
    ident => '~steve',
    host  => 'server.example.com',
    cloak => 'server.example.com',
    ip    => '93.184.216.119'
);
  • %opts - hash of constructor options.

$user->is_mode($mode_name)

Returns true if user has the supplied mode set.

if ($user->is_mode('ircop')) {
    $user->numeric('RPL_YOUREOPER');
}
  • $mode_name - name of the mode being tested.

$user->set_mode($mode_name)

The lowest level of user mode setting.

Local clients and uplinks are NOT notified; see ->handle_mode_string(), ->do_mode_string(), and ->do_mode_string_local() for ways to achieve that.

if (password_correct($pw)) {
    $user->set_mode('ircop');
}
  • $mode_name - name of the mode being set.

$user->unset_mode($mode_name)

The lowest level of user mode unsetting.

Local clients and uplinks are NOT notified; see ->handle_mode_string(), ->do_mode_string(), and ->do_mode_string_local() for ways to achieve that.

$user->unset_mode('invisible');
  • $mode_name - name of the mode being unset.

$user->handle_mode_string($mode_string, $force)

The lowest level of mode string handling.

Local clients and uplinks are NOT notified. The method only calls ->set_mode() and ->unset_mode() after firing any possible user mode blocks. See ->do_mode_string() and ->do_mode_string_local() for the different ways to handle mode strings at a higher level.

Returns a mode string of changes that occurred such as +ix.

$user->handle_mode_string('+o', 1);
  • $mode_string - mode string to be handled; e.g. +iox. this is in the perspective of user's server.

  • $force - optional, if true, failure of user mode blocks will be ignored, forcing the changes.

$user->mode_string

Returns string of all modes set on user; e.g. +iox.

# unset all modes
my $all_modes = $user->mode_string;
substr($all_modes, 0, 1) = '-';
$user->handle_mode_string($all_modes, 1);

$user->flags

Returns the list of oper flags belonging to user.

$user->has_flag($flag)

Returns true if user has the specified oper flag enabled.

if (!$user->has_flag('gkill')) {
    $user->numeric(ERR_NOPRIVILEGES => 'gkill');
    return;
}
  • $flag - name of the flag being tested; e.g. kill.

$user->add_flags(@flags)

The lowest level of oper flag handling.

Adds any of the supplied oper flags that user does not have already. Local clients and uplinks are NOT notified.

$user->add_flags('kill', 'gkill');
  • @flags - list of oper flags to add.

$user->remove_flags(@flags)

The lowest level of oper flag handling.

Removes any of the supplied oper flags that user has enabled. Local clients and uplinks are NOT notified.

$user->remove_flags('kill', 'gkill');
  • @flags - list of oper flags to remove.

$user->clear_flags

Removes all of user's oper flags at a low level.

$user->update_flags

After committing oper flag changes, sets or unsets user's IRCop mode as necessary. Also notifies user and other opers of flags granted.

Used for both local and remote users.

$user->has_notice($flag)

Returns true if user has the supplied oper notice flag enabled.

if ($user->has_notice('user_nick_change')) {
    $user->server_notice(nick => $user_other->full.' -> '.$new_nick);
}
  • $flag - oper notice flag being tested.

$user->add_notices(@flags)

Adds any of the supplied oper notice flags that user does not already have.

This has no effect on remote users.

$user->add_notices('user_nick_change', 'user_killed');
  • @flags - list of oper notice flags to enable.

$user->change_nick($new_nick, $new_nick_ts)

The lowest level of user nick changing.

Local clients and uplinks are NOT notified. You probably want ->do_nick() or ->do_nick_local() instead.

Returns the new nick TS if the nick was changed successfully. This can fail if it is invalid or already taken. Note that true is returned if the new nick is the same as it was already.

$user->change_nick('newbie');
  • $new_nick - nick to replace the current one.

  • $new_nick_ts - optional, new nick TS. defaults to current time.

$user->set_away($reason)

The lowest level of marking a user as away.

Local clients and uplinks are NOT notified. You likely want ->do_away() instead.

$user->set_away('Be back later.');
  • $reason - comment for why user is away.

$user->unset_away

The lowest level of marking a user as here.

Local clients and uplinks are NOT notified. You likely want ->do_away() instead.

$user->unset_away();

$user->quit($reason)

The lowest level of user quitting for both local and remote users.

To terminate the connection of a local user, you should instead use $user->conn->done($reason), which will in turn call this method after dropping the connection.

All event callbacks will be deleted as this method prepares the user object for disposal.

$user->quit('~ <insert meaningless quote here>');
  • $reason - reason for quitting.

$user->channels

Returns list of channel objects of which user is a member.

my $n;
for my $channel ($user->channels) {
    next if !$channel->user_has_basic_status($user);
    $n++;
}
say "I have ops in $n channels across 1 networks. But no one cares.";

$user->is_local

Returns true if user belongs to the local server.

if ($user->is_local) {
    $user->sendfrom($other->full, 'AWAY :Be back later.');
}

$user->full

Returns string nick!ident@host where host may be either an artificial host (cloak) or the actual hostname.

$user->sendfrom($other->full, 'AWAY :Be back later.');

$user->fullreal

Returns string nick!ident@host where host is always the actual hostname, ignoring any possible cloak or artificial host.

Generally ->full is favored over this if the return value may be exposed to other users.

# show opers the real host
notice(user_nick_change => $user->fullreal, $new_nick);

$user->fullip

Returns string nick!ident@host where host is the human-readable IP address of user, completely ignoring any host or cloak.

if (match($mask, $user->fullip)) {
    kill_user($user, "You're banned!");
}

$user->notice_info

Returns a list of user's nick, ident, and actual host in that order. Useful for oper notices where these three items are commonly displayed.

notice(user_nick_change => $user->notice_info, $new_nick);

$user->hops_to($target)

Returns the number of hops to a server or user.

$user->hops_to($user);          # 0
$user->hops_to($other_server);  # 1
  • $target - target user or server object. if it's a user, the result is the same as calling with $target->server.

$user->id

Returns the globally unique internal identifier associated with user.

# sometimes it is useful to use IDs rather than
# increasing the refcount on the user object
push @invited_users, $user->id;

$user->name

Returns user's nick.

my $target = $pool->lookup_channel($id) || $pool->lookup_user($id);
say 'Sending the message to '.$target->name;

$user->server

Returns the server to which user belongs, regardless of whether that server is directly linked to the local one.

if ($user->server != $fwd_serv) {
    $msg->forward_to($fwd_serv, privmsg => $target, $message);
}

High-level methods

High-level methods are typically for modifying data associated with a user and then notifying local clients and uplinks.

Most modules will find the high-level methods more useful than the low-level ones because they deal with the logistics associated with changing data associated with user.

$user->server_notice($info, $message)

Send a notice to user from the local server.

Works on both local and remote users.

$user->server_notice('A client is connecting.');
$user->server_notice(kill => $other_user->name.' was killed');
  • $info - optional, a brief description of the notice which will be formatted in an appealing way; e.g. 'kill' for a kill command result.

  • $message - notice message to send to user.

$user->numeric($const, @args)

Send a numeric reply to user from the local server.

Works on both local and remote users. The string is formatted locally so that remote servers do not have to understand the numeric.

$user->numeric(RPL_MAP => $spaces, $server->name, $users, $per);
  • $const - string name of the numeric reply.

  • @args - optional (depending on the numeric), a list of arguments for the user numeric handler.

$user->simulate_numeric($const, @args)

Like ->numeric, except instead of actually sending the numeric, returns the data which would have been sent.

Useful for checking the length of what the base of a numeric reply will be so that the message byte limit is not exceeded.

$user->handle_unsafe($data)

Emulates that user sent a piece of data to the local server.

It is "unsafe" in that it assumes that the command handlers are capable of dealing with emulated data from remote users.

Works exactly like ->handle() except it will not return fail if user is not connected directly to the local server.

$user->handle_unsafe("TOPIC $ch_name :$new_topic");
  • $data - one or more complete lines of data, including any possible trailing newlines or carriage returns.

$user->handle_with_opts_unsafe($data, %opts)

Same as ->handle_unsafe(), except that the provided options will be passed to the underlying call.

$user->handle_with_opts_unsafe("TOPIC $ch_name :$new_topic", fantasy => 1);
  • $data - one or more complete lines of data, including any possible trailing newlines or carriage returns.

  • %opts - optional, a hash of options to pass to the underlying function call.

$user->get_killed_by($murderer, $reason)

Handles a kill on a local level.

To clarify, neither user nor murderer has to be local, but uplinks are NOT notified. Local kills MUST be associated with a broadcast() call, and remote kills MUST be broadcast to other uplinks with forward methods.

my $reason = "Your behavior is not conducive to the desired environment.";
$user->get_killed_by($other_user, $reason);
  • $murderer - user committing the action.

  • $reason - comment for why user was killed.

$user->get_mask_changed($new_ident, $new_host)

Handles an ident or cloak change on a local level.

To clarify, user does not have to be local, but uplinks are NOT notified.

Updates the user fields, notifying local clients as necessary. If neither the ident nor the cloak has changed, the call is quietly discarded.

Local clients with the chghost capability will receive a CHGHOST notification. Others will receive a quit and rejoin emulation, unless users:chghost_quit is explicitly disabled.

$user->get_mask_changed('someone', 'my.vhost');
$user->get_mask_changed($user->{ident}, 'my.vhost');
  • $new_ident - new ident. if unchanged, the current ident MUST be passed.

  • $new_host - new cloak. if unchanged, the current cloak MUST be passed.

$user->save_locally

Handles a nick collision on user at a local level, adopting his UID as a nick.

To clarify, user does not have to be local, but uplinks are NOT notified.

Remote servers should been notified (such as through a SAVE, NICK, or similar message) that user was saved and will adopt his UID as a nick.

if ($save_old) {
    $server->fire_command(save_user => $me, $used);
    $used->save_locally;
}

$user->do_away($reason)

Processes an away or return for both local and remote users.

Calls ->set_away() or ->unset_away(), depending on whether the reason is provided.

If user is local, he receives a numeric notification.

Local clients with the away-notify capability are also notified.

$user->do_away("Be back later");
$user->do_away(undef);
  • $reason - optional, the away comment. if undefined or empty string, user is considered to have returned from being away.

$user->do_away_local($reason)

Local version.

$user->do_part_all

Parts user from all channels, notifying local clients.

This is typically initiated by a JOIN 0 command.

if ($target eq '0') {
    $user->do_part_all();
}

$user->do_part_all_local

Local version.

$user->do_login($act_name, $no_num)

Logs user into the given account name.

If user is local, he receives a numeric notification.

Local clients with the account-notify capability are also notified.

$user->do_login($act_name);
  • $act_name - name of the account.

  • $no_num - optional, if provided, user will not receive a numeric reply. this is useful if the reply was already sent before calling this method, such as during SASL authentication.

$user->do_login_local($act_name, $no_num)

Local version.

$user->do_logout

Logs user out from their current account.

If user is not logged in, the call is quietly discarded.

If user is local, he receives a numeric notification.

Local clients with the account-notify capability are also notified.

$user->do_logout();

$user->do_logout_local

Local version.

$user->do_nick($new_nick, $new_nick_ts)

Changes user's nick, notifying local clients and uplinks.

$user->do_nick($new_nick, $new_nick_ts);
$user->do_nick($new_nick); # use current time
  • $new_nick - nick to replace the current one.

  • $new_nick_ts - optional, new nick TS. defaults to current time.

Returns the new nick TS if the nick was changed successfully. This can fail if it is invalid or already taken. Note that true is returned if the new nick is the same as it was already.

$user->do_nick($new_nick, $new_nick_ts)

Local version.

$user->do_privmsgnotice($command, $source, $message, %opts)

Handles a PRIVMSG or NOTICE to user.

This is used for both local and remote users. Fires events like can_message, cant_message, can_receive_message, privmsg, notice, plus several others. These are used by modules to either block or modify messages.

This method also deals with routing of the message. If user is local, he will receive the message. Otherwise, the message will be forwarded to the appropriate uplink.

$target->do_privmsgnotice('notice', $someone, 'hi there');
  • $command - either 'privmsg' or 'notice'. case-insensitive.

  • $source - user or server object which is the source of the message.

  • $message - message text was it was received.

  • %opts - optional, a hash of options.

%opts

  • force - if specified, the can_privmsg, can_notice, and can_message events will not be fired. this means that any modules that prevent the message from being sent OR that modify the message will NOT have an effect on this message. used when receiving remote messages.

  • dont_forward - if specified, the message will NOT be forwarded to uplinks when user is not local.

$user->do_privmsgnotice_local($command, $source, $message, %opts)

Local version.

$user->do_mode_string($mode_string, $force)

Handles a mode string with ->handle_mode_string(), notifying user and uplinks.

If user is local, a MODE message will notify user with the result of any changes. The mode message will then be forwarded to uplinks.

$user->do_mode_string('+io', 1);
  • $mode_string - mode string to be handled; e.g. +iox. this is in the perspective of user's server, $user->server.

  • $force - optional, if true, failure of user mode blocks will be ignored, forcing the changes.

$user->do_mode_string_local($mode_string, $force)

Handles a mode string with ->handle_mode_string(), notifying user.

If user is local, a MODE message will notify user with the result of any changes.

Unlike ->do_mode_string(), the mode message will only be handled locally and will NOT be forwarded uplinks.

$user->do_mode_string_local('+i');
  • $mode_string - mode string to be handled; e.g. +iox. this is in the perspective of user's server, $user->server.

  • $force - optional, if true, failure of user mode blocks will be ignored, forcing the changes.

$user->send_to_channels($line, %opts)

Sends data with user as the source to all local users with at least one channel in common with user.

User himself will also receive the message, regardless of whether he has joined any channels.

$user->send_to_channels('NICK steve');
# sends :nick!user@host NICK steve to all users in a common channel with him
  • $line - line of data WITHOUT a suffixing newline and carriage return.

  • %opts - optional, a hash of options to pass to the underlying sendfrom_to_many_with_opts() function.

Local-only methods

These methods may ONLY be called on local users; however, some have similar versions or wrappers which support remote users.

Many of these methods require that the user is local because they involve using methods on their connection object, e.g. sending data directly to the associated socket.

$user->handle($data)

Handle one or more lines of incoming data from user.

The appropriate user command handlers will be called, or if a command does not exist, ERR_UNKNOWNCOMMAND will be sent to user.

Returns undef and produces a warning if user does not belong to the local server or does not have an associated connection object. Handling data from a remote user may be dangerous but can be achieved instead with ->handle_unsafe().

$user->handle("MOTD $$serv{name}");
  • $data - one or more complete lines of data, including any possible trailing newlines or carriage returns.

$user->handle_with_opts($data, %opts)

Same as ->handle(), except that the provided options will be passed to the underlying call.

$user->handle_with_opts("MODE $$channel{name}", fantasy => 1);
  • $data - one or more complete lines of data, including any possible trailing newlines or carriage returns.

  • %opts - optional, a hash of options to pass to the underlying function call.

$user->send($line)

Sends a line of data to user.

Returns undef and produces a warning if user does not belong to the local server or does not have an associated connection object. However, some other sending methods work on remote users such as ->numeric() and ->server_notice().

$user->send(':'.$me->name.' NOTICE '.$user->name.' :welcome to our server');
  • $line - line of data WITHOUT a suffixing newline and carriage return.

$user->sendfrom($from, $line)

Sends a line of data from a source.

The supplied source should not be an object but instead a string. Typically the ->full method of either a user or server will be used. For users, this is nick!ident@host where host is either a cloak or the real host. For servers, this is the server name.

# where $ouser is some other user.
$user->sendfrom($ouser->full, 'AWAY :gtg bye');
# the above is equivalent to
$user->send(':'.$ouser->full.' AWAY :gtg bye');
  • $from - source string of the message.

  • $line - line of data WITHOUT a suffixing newline and carriage return.

$user->sendme($line)

Sends a line of data from the local server.

$user->sendme("NOTICE $$user{nick} :Hi!");
# the above is equivalent to both of the following:
$user->sendfrom($me->name, "NOTICE $$user{nick} :Hi!");
$user->send(':'.$me->name." NOTICE $$user{nick} :Hi!");
# but just for the record, this is only an example.
# the proper way to achieve the above is:
$user->server_notice('Hi!');
  • $line - line of data WITHOUT a suffixing newline and carriage return.

$user->has_cap($flag)

Returns true if user has the specified client capability enabled.

if ($user->has_cap('away-notify')) {
    $user->sendfrom($other_user->full, 'AWAY');
}
  • $flag - name of the capability.

$user->add_cap($flag)

Enables a client capability.

$user->add_cap('userhost-in-names');
  • $flag - name of the capability.

$user->remove_cap($flag)

Disables a client capability.

$user->remove_cap('multi-prefix');
  • $flag - name of the capability.

$user->forward($event_name, @args)

Convenience method for $user->location->forward(...).

$user->conn

Returns the connection object associated with user.

This is never present for remote users, and may even be absent for some local users. Thus, you must always test for it.

$user->conn->done('Goodbye');

Procedural functions

These functions typically involve operations on multiple users. Rather than being called in the $user->method() form, they should be used directly from the user package, as in user::some_function().

sendfrom_to_many($from, $line, @users)

Sends a piece of data to several users at once from the specified source.

Even if any user occurs multiple times in the list, the message will only be sent once to each user.

user::sendfrom_to_many($user->full, 'NICK steve', @users, $user);
# the above sends a NICK message as follows: :user!ident@host NICK steve
# to all of the users within the list @users as well as himself.
  • $from - source string of the message.

  • $line - line of data WITHOUT a suffixing newline and carriage return.

  • @users - list of users to send the data to.

sendfrom_to_many_with_opts($from, $line, %opts, @users)

Same as sendfrom_to_many(), except that additional features may be used through the added options argument.

my $opts = { no_self => 1 };
user::sendfrom_to_many($user->full, 'NICK steve', $opts, @users);
# the above sends a NICK message as follows: :user!ident@host NICK steve
# to all of the users in @users other than $user
  • $from - source string of the message.

  • $line - line of data WITHOUT a suffixing newline and carriage return.

  • %opts - optional, a hash reference of options.

  • @users - list of users to send the data to.

Supported options

  • ignore - optional, user object to ignore. if ignored user is found in the provided list, they are skipped and will not receive the message.

  • no_self - optional, if true, the user identified by the mask $from will be skipped and will not receive the message.

  • cap - optional, client capability. if provided, the message will only be sent to users with this capability enabled.

  • alt - optional, alternate message to send to users without the capability specified by the cap option.

  • batch - optional, batch that this message belongs to, as returned by message->new_batch().

sendfrom_to_all($from, $line)

Like sendfrom_to_many(), except that it sends the message to all local users.

sendfrom_to_all_with_opts($from, $line, %opts, @users)

Like sendfrom_to_many_with_opts(), except that it sends the message to all local users.

Events

User objects are listened upon by the pool. Most events involving user interaction with a channel are fired on the channel object rather than the user object.

user.can_join($channel)

Fired before a local user joins a channel.

Callbacks of this event typically run checks to see if user can join, stopping the event fire if not. For instance, one such callback checks if user is banned.

Never fired for remote users, as it is the responsibility of each server to determine whether its own users should be able to join a channel.

Fired by Core::UserCommands.

$pool->on('user.can_join' => sub {
    my ($event, $channel) = @_;
    my $user = $event->object;
    # channel is not invite-only.
    return $JOIN_OK
        unless $channel->is_mode('invite_only');
    # user has been invited.
    return $JOIN_OK
        if $user->{invite_pending}{ lc $channel->name };
    # user matches the exception list.
    return $JOIN_OK
        if $channel->list_matches('invite_except', $user);
    # sorry, not invited, no exception.
    $user->numeric(ERR_INVITEONLYCHAN => $channel->name)
        unless $event->{silent_errors};
    $event->stop('not_invited');
}, name => 'has.invite', priority => 20);
  • $channel - channel user is attempting to join.

user.can_invite($t_user, $ch_name, $channel, $quiet)

Fired before a local user invites someone to a channel.

Callbacks of this event typically run checks to see if user can invite, stopping the event fire if not. For instance, one such callback checks if the target user is already in the channel.

Never fired on remote users, as it is the responsibility of each server to determine whether its own users should be able to invite.

Fired by Invite.

$pool->on('user.can_invite' => sub {
    my ($event, $t_user, $ch_name, $channel) = @_;
    my $user = $event->object;
    return unless $channel;
    # target is not in there.
    return $INVITE_OK
        unless $channel->has_user($t_user);
    # target is in there already.
    $event->stop;
    $user->numeric(ERR_USERONCHANNEL => $t_user->{nick}, $ch_name);
}, name => 'target.in.channel', priority => 20);
  • $t_user - local or remote user that we are sending an invitation to.

  • $ch_name - name of the channel $t_user is being invited to. this is necessary because we allow invitations to nonexistent channels (unless channels:invite_must_exist is enabled).

  • $channel - channel object or undef if it does not yet exist.

  • $quiet - if true, the handler MUST NOT send error replies to user.

user.can_message($target, $message, $lc_cmd)

Fired on a local user who is attempting to send a message.

The target may be a user or channel. This event allows modules to prevent a user from sending a message by stopping the event. Modules can also modify the message text before it is delivered to its target.

If your callback adds a message restriction, call $event->stop($reason) with a human-readable reason as to why the message was blocked. This may be used for debugging purposes. If you need to send an error numeric to the source user, do not call ->numeric() directly, as some errors are suppressed by other modules. Instead, use the $event->{error_reply} key, like so:

$event->{error_reply} = [ ERR_INVITEONLYCHAN => $channel->name ];

You can hook onto this event using any of these:

Event Command Target
can_message Any Any
can_message_user Any User
can_message_channel Any Channel
can_privmsg PRIVMSG Any
can_privmsg_user PRIVMSG User
can_privmsg_channel PRIVMSG Channel
can_notice NOTICE Any
can_notice_user NOTICE User
can_notice_channel NOTICE Channel
# not in channel and no external messages?
$pool->on('user.can_message_channel' => sub {
    my ($user, $event, $channel, $message_ref, $type) = @_;
    # not internal only, or user is in channel.
    return unless $channel->is_mode('no_ext');
    return if $channel->has_user($user);
    # no external messages.
    $event->{error_reply} =
        [ ERR_CANNOTSENDTOCHAN => $channel->name, 'No external messages' ];
    $event->stop('no_ext');
}, name => 'no.external.messages', with_eo => 1, priority => 30);
  • $target - message target. a user or channel object.

  • $message - scalar reference to the message text. callbacks may overwrite this to modify the message.

  • $lc_cmd - privmsg or notice. only useful for can_message_* events.

user.cant_message($target, $message, $can_fire, $lc_cmd)

You can hook onto this event using any of these:

Event Command Target
cant_message Any Any
cant_message_user Any User
cant_message_channel Any Channel
cant_privmsg PRIVMSG Any
cant_privmsg_user PRIVMSG User
cant_privmsg_channel PRIVMSG Channel
cant_notice NOTICE Any
cant_notice_user NOTICE User
cant_notice_channel NOTICE Channel
  • $target - message target. a user or channel object.

  • $message - message text.

  • $can_fire - event fire object from the can_message and related events. this is useful for extracting information about why the message was blocked.

  • $lc_cmd - privmsg or notice. only useful for cant_message_* events.

user.can_receive_message($target, $message, $lc_cmd)

Fired on a local user who is about to receive a message.

The message is either addressed directly to user or a channel of which user is a member. By the time this event is fired, ->do_privmsgnotice() has already determined that the source is permitted to send the message. Modifications may have been made to the primary message text as well.

Modules may hook onto this to prevent a specific user from seeing the message by stopping the event, or they can modify the text that the specific user will see. Modifying the message will not affect what other channel members might see, for example.

You can hook onto this event using any of these:

Event Command Target
can_receive_message Any Any
can_receive_message_user Any User
can_receive_message_channel Any Channel
can_receive_privmsg PRIVMSG Any
can_receive_privmsg_user PRIVMSG User
can_receive_privmsg_channel PRIVMSG Channel
can_receive_notice NOTICE Any
can_receive_notice_user NOTICE User
can_receive_notice_channel NOTICE Channel
  • $target - message target. a user or channel object.

  • $message - scalar reference to the message text. callbacks may overwrite this to modify the message. unlike can_message, changes to it will only affect what the user the event is fired on sees.

  • $lc_cmd - privmsg or notice. only useful for can_receive_message_* events.