6 releases
0.2.4 | Aug 19, 2023 |
---|---|
0.2.3 | Jul 23, 2023 |
0.1.0 | Jul 1, 2023 |
#1483 in Command line utilities
49KB
880 lines
nofi
A Rofi-driven notification manager
https://github.com/ellsclytn/nofi/assets/8725013/b3c5e53b-7ba9-44bd-a920-81a408b84cb9
nofi
is a distraction-free notification center. While most notification daemons make immediate popups a key function, nofi
is designed with such functionality as an anti-feature: notifications are intended to be viewed, but not to annoy. Notifications can be viewed at the user's discretion by launching nofi
's Rofi-driven notification manager.
nofi
is a server implementation of freedesktop.org - Desktop Notifications Specification and it can be used to receive notifications from applications via D-Bus.
The name?
A portmanteau of "notification" and Rofi.
Features
- Template-powered (Jinja2/Django) notification text.
- Run custom OS commands based on the matched notifications.
Installation
From crates.io
nofi
can be installed from crates.io:
$ cargo install nofi
The minimum supported Rust version is 1.64.0
.
Arch Linux
nofi
can be installed from the AUR using an AUR helper. For example:
aura -A nofi-bin
Binary releases
See the available binaries for different operating systems/architectures from the releases page.
Build from source
Prerequisites
Instructions
- Clone the repository.
$ git clone https://github.com/ellsclytn/nofi && cd nofi/
- Build.
$ CARGO_TARGET_DIR=target cargo build --release
Binary will be located at target/release/nofi
.
Usage
On Xorg startup
You can use xinitrc or xprofile for autostarting nofi
.
xinitrc
If you are starting Xorg manually with xinit, you can nofi
on X server startup via xinitrc:
$HOME/.xinitrc
:
nofi &
Long-running programs such as notification daemons should be started before the window manager, so they should either fork themself or be run in the background via appending &
sign. Otherwise, the script would halt and wait for each program to exit before executing the window manager or desktop environment.
In the case of nofi
not being available since it's started at a faster manner than the window manager, you can add a delay as shown in the example below:
{ sleep 2; nofi; } &
xprofile
If you are using a display manager, you can utilize an xprofile file which allows you to execute commands at the beginning of the X user session.
The xprofile file, which is ~/.xprofile
or /etc/xprofile
, can be styled similarly to xinitrc.
As a D-Bus service
You can create a D-Bus service to launch nofi
automatically on the first notification action. For example, you can create the following service configuration:
/usr/share/dbus-1/services/org.ellsclytn.nofi.service
:
[D-BUS Service]
Name=org.freedesktop.Notifications
Exec=/usr/bin/nofi
Whenever an application sends a notification by sending a signal to org.freedesktop.Notifications
, D-Bus activates nofi
.
As a systemd service
~/.config/systemd/user/nofi.service
:
[Unit]
Description=Nofi notification daemon
Documentation=man:nofi(1)
PartOf=graphical-session.target
[Service]
Type=dbus
BusName=org.freedesktop.Notifications
ExecStart=/usr/bin/nofi
You may then reload systemd and start/enable the service:
systemctl --user daemon-reload
systemctl --user start nofi.service
Usage
nofi
uses dbus-send(1)
to receive control instructions. There is currently only one instruction: viewing notification history.
# show the last notification
dbus-send --print-reply \
--dest=org.freedesktop.Notifications \
/org/freedesktop/Notifications/ctl \
org.freedesktop.Notifications.History
An example use-case of this is to bind this to a key in your window manager, such as i3:
bindsym $mod+grave exec dbus-send --print-reply \
--dest=org.freedesktop.Notifications /org/freedesktop/Notifications/ctl org.freedesktop.Notifications.History
Status Bar Integration
nofi
broadcasts notification counts over a UNIX socket in the same format as Rofication. This means it can be integrated into status bars like i3status-rust via the Rofication block. The socket path follows the XDG Base Directory specification which usually exposes the socket at /run/user/<UID>/nofi/socket
. This may vary between systems, so the socket path is output to stdout
when nofi
starts.
# Example i3status-rust integration
[[block]]
block = "rofication"
interval = 1
socket_path = "/run/user/1000/nofi/socket"
Configuration
nofi
configuration file supports TOML format and the default configuration values can be found here.
Configuration overrides can be placed in $HOME/.config/nofi/nofi.toml
, or at a path of your choosing by specifying a NOFI_CONFIG
environment variable.
Global configuration
log_verbosity
Sets the logging verbosity. Possible values are error
, warn
, info
, debug
and trace
.
template
Sets the template for the notification message. The syntax is based on Jinja2 and Django templates.
Simply, there are 3 kinds of delimiters:
{{
and}}
for expressions{%
or{%-
and%}
or-%}
for statements{#
and#}
for comments
See Tera documentation for more information about control structures, built-in filters, etc.
Context
Context is the model that holds the required data for template rendering. The JSON format is used in the following example for the representation of a context.
{
"app_name": "nofi",
"summary": "example",
"body": "this is a notification 🦡",
"urgency": "normal",
"unread_count": 1,
"timestamp": 1672426610
}
Urgency configuration
There are 3 levels of urgency defined in the Freedesktop specification and they define the importance of the notification.
low
: e.g. "joe signed on"normal
: e.g. "you got mail"critical
: e.g. "your computer is on fire!"
You can configure nofi
to act differently based on these urgency levels. For this, there need to be 3 different sections defined in the configuration file. Each of these sections has the following fields:
[urgency_{level}] # urgency_low, urgency_normal or urgency_critical
custom_commands = []
custom_commands
With using this option, you can run custom OS commands based on urgency levels and the notification contents. The basic usage is the following:
custom_commands = [
{ command = 'echo "{{app_name}} {{summary}} {{body}}"' } # echoes the notification to stdout
]
As shown in the example above, you can specify an arbitrary command via command
which is also processed through the template engine. This means that you can use the same template context.
The filtering is done by matching the fields in JSON via using filter
along with the command
. For example, if you want to play a custom notification sound for a certain application:
custom_commands = [
{ filter = '{ "app_name":"notify-send" }', command = 'aplay notification.wav' },
{ filter = '{ "app_name":"weechat" }', command = 'aplay irc.wav' }
]
The JSON filter can have the following fields:
app_name
: Name of the application that sends the notification.summary
: Summary of the notification.body
: Body of the notification.
Each of these fields is matched using regex and you can combine them as follows:
custom_commands = [
{ filter = '{ "app_name":"telegram|discord|.*chat$","body":"^hello.*" }', command = 'gotify push -t "{{app_name}}" "someone said hi!"' }
]
In this hypothetical example, we are sending a Gotify notification when someone says hi to us in any chatting application matched by the regex.
Related Projects
- Rofication
- runst, which is what this project is a fork of.
License
Licensed under either of Apache License Version 2.0 or The MIT License at your option.
Copyright
Copyright © 2023, Ellis Clayton
Dependencies
~20–29MB
~375K SLoC