Extension:TimedNotify

MediaWiki extensions manual
TimedNotify
Release status: stable
Implementation Hook
Description Provides a time-based notification system for Echo
Author(s) Xxmarijnw (Wikibase Solutions)
Latest version 1.1.0 (2023-01-23)
MediaWiki 1.35+
PHP 7.4+
Database changes Yes
License GNU General Public License 2.0 or later
Download
  • $wgTimedNotifyRunRate
  • $wgTimedNotifyPushedNotificationRetentionDays
  • $wgTimedNotifyRunDeferred
  • $wgTimedNotifyDisabledNotifiers

TimedNotify provides a time-based notification system for Echo. Time-based notifications are notifications that are not explicitly triggered by an action, but are instead sent out (periodically) when certain criteria are met. The canonical example of time-based notifications are reminders.

Configuration edit

The following configuration options are available:

  • $wgTimedNotifyRunRate (default: 0.05) - determines the run rate of the notifier
  • $wgTimedNotifyRunDeferred (default: true) - whether to run the notifications in a deferred update
  • $wgTimedNotifyPushedNotificationRetentionDays (default: 60) - the number of days to retain pushed notifications
  • $wgTimedNotifyDisabledNotifiers (default: []) - which notifiers to disable

$wgTimedNotifyRunRate edit

This configuration parameter determines how often to run the notifications. Calculating which notifications to send out and to whom is expensive. The notifications are therefore only calculated approximately once every 1/$wgTimedNotifyRunRate requests. Thus, if you set your run rate to 0.01, the probability of running the notifications is 1 in 100 for every request. Setting this to a lower number will increase performance, as the notifications have to be calculated less often, but it will decrease the timeliness of the notifications. That is, notifications may arrive later than expected. It is recommended to keep this value between 0.01 (1/100) and 0.5 (1/2), depending on the size of your wiki. For smaller wiki's, this value should be greater than for large wiki's.

If you don't want notifications to run on web requests at all, you can set the run rate to 0.0. In this case maintenance/runNotifications.php should be run periodically.

It should be a number between 0 inclusive and 1 inclusive. The default value is 0.05 (1/20).

$wgTimedNotifyRunDeferred edit

This configuration parameter determines whether to run the notifications in a deferred update. If this parameter is set to true, notifications are calculated after a response has already been sent to the browser. This calculation will therefore not impact the load time of the wiki. Unless you have a specific reason to set this to false, you generally should not have to change this.

It should be a boolean. The default value is true.

$wgTimedNotifyPushedNotificationRetentionDays edit

This configuration parameter specifies the number of days to remember pushed notifications. In order to prevent duplicate notifications for the same event, TimedNotify keeps track for which events it has already sent out notifications in a database table. To prevent this table from growing extremely large, old notifications are occasionally purged from the table. This configuration options specifies the minimum age in days before a notification is purged from this table.

It should be an integer. The default value is 60.

$wgTimedNotifyDisabledNotifiers edit

This configuration parameter specifies which notifiers to disable. It should be an array of booleans, where the key is the name of the notifier, and the value is true to disable the notifier. By default, the array is empty and all notifiers are enabled.

It should be an array. The default value is [].

Adding a new notification type edit

Adding a new time-based notification type is relatively easy and quite similar to adding a new notification type to Echo directly.

Introduction to how time-based notifications work edit

Time-based notifications are notifications that are not explicitly triggered by an action. This means that the calculation to determine which time-based notifications need to be sent out needs to happen periodically. How often this happens is based on $wgTimedNotifyRunRate, which is explained above.

Since the notifications are (re-)calculated periodically, we need to make sure we do not push the same notification twice. To solve this, a notification can be annotated with a unique ID. The extension guarantees that a notification is only pushed if no notification with the same ID has been pushed before.

For example, suppose we want to create a notification that reminds a user that their moderation status is to expire in less than 30 days. The condition for this "event" is true for 30 days, but the notification should only be sent out once. Therefore, we push a notification annotated with the ID 2022-08-12-30d, where 2022-08-12 is the expiration date of the user's moderation status. The expiration date will not change (and if it does, the notification should be sent out again when applicable), and therefore the notification will only be sent out once.

Notification definition edit

Each notifier must be defined in a class. This class must extend the base TimedNotify\Notifier class, and may implement the following methods:

Method Description
getName() The name of the notifier (should be unique).
getPresentationModel() The class name of the presentation model. This corresponds directly to presentation models in Echo.
getNotifications() An array of notifications that should be sent (see below for the format).
getIcons() Additional icons to define.
static getNotificationUsers(EchoEvent) The users that should be notified for the given EchoEvent.
static getFilteredUsers(EchoEvent) The list of users that should not be notified for the given EchoEvent.

Notification format edit

A notification should be an array with the following keys:

Key Type Description
id string A unique identifier for this notification (will automatically be scoped to the notifier). The notification will only be emitted if a notification with this key has not already been emitted. If this value is omitted, the notification will be emitted unconditionally. Optional
data array Additional data to add to the notification. Optional

Notification registration edit

Notification registration happens in the method that corresponds to the TimedNotifyGetNotifierClasses hook. This hook includes a single variable, &$notifierClasses and contains a list of classes that extend Notifier. You must add your new notifiers through this hook. For example:

public function onTimedNotifyGetNotifierClasses( array &$notifierClasses ): void {
    $notifierClasses[] = MyCoolNotifier::class;
}

Walkthrough: Creating a new notification type edit

Suppose we want to create an extension that notifies the user whenever their moderation status is about to expire. This section will demonstrate how to build such an extension using TimedNotify. This section will omit some details of creating an extension, and will only focus on the parts that interact with TimedNotify.

Creating the class edit

We start by creating a new class that extends TimedNotify\Notifier:

class ModerationStatusReminder extends Notifier {
    public function getName(): string {
        // This function can return basically anything, as long as it is unique
        return "ModerationStatusReminder";
    }

    public function getPresentationModel(): string {
        // This function should return the class name of the presentation model 
        // to use for this notification. This corresponds directly to the presentation 
        // model used by Echo. For details on how to create a presentation model, 
        // please refer to the Echo documentation:
        // https://www.mediawiki.org/wiki/Extension:Echo/Creating_a_new_notification_type
        return ModerationStatusReminderPresentationModel::class;
    }

    public function getNotifications(): array {
        $config = MediaWikiServices::getInstance()->getMainConfig();

        // Get a timestamp $wgModerationStatusReminderDays into the future
        $future = time() + 60 * 60 * 24 * $config->get( 'ModerationStatusReminderDays' );

        // Retrieve all the users that have an expiry time between now and $future 
        // (for this walkthrough, we will assume 'getSoonToExpire' builds an 
        // appropriate query and returns its result as an stdClass.
        $result = $this->getSoonToExpire( $future );

        // We now iterate over all users that have a moderation status that is 
        // soon to expire and create notifications for each user
        $notifications = [];

        foreach ( $result as $row ) {
            $expirationTime = $row->ug_expiry;
            $user = $row->ug_user;

            // We build a unique ID for the notification
            $notificationId = $user . '-' . $expirationTime;

            $notifications[] = [
                'id' => $notificationId,
                'data' => [
                    // Include the user so we know who to notify
                    'user' => $user,
                    // Include the expiration time for visual purposes
                    'expirationTime' => $expirationTime
                ]
            ];
        }

        // Finally, we return the list of notifications we have constructed
        return $notifications;
    }

    public static function getNotificationUsers( EchoEvent $event ): array {
        // Since we already included which user to notify in the event, we 
        // can just return that user. For many other notification types, 
        // it is more convenient to calculate who to notify in this function 
        // directly, since many users may be notified for the same event.
        return [User::newFromId( $event->getExtraParam( 'user' ) )];
    }
}

Next, we implement the TimedNotifyGetNotifierClasses to register our notifier:

public function onTimedNotifyGetNotifierClasses( array &$notifierClasses ): void {
    $notifierClasses[] = ModerationStatusReminder::class;
}

Installation edit

This extension requires the Echo extension.
  • Download and place the file(s) in a directory called TimedNotify in your extensions/ folder.
  • Add the following code at the bottom of your LocalSettings.php file:
    wfLoadExtension( 'TimedNotify' );
    
  • Run the update script which will automatically create the necessary database tables that this extension needs.
  •   Done – Navigate to Special:Version on your wiki to verify that the extension is successfully installed.