ruf

command module
v0.0.0-...-838a368 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 7, 2026 License: MIT Imports: 1 Imported by: 0

README

rufd

A (vibe coded) application to make calls.

What it does

This application is a CLI tool to send calls to different platforms. Currently, it supports Slack.

Usage

To see a list of all available commands and flags, run:

rufd --help

Persistent Scheduling

The application uses a persistent scheduling model. Instead of calculating the schedule on-the-fly, the scheduler pre-calculates all call instances within a defined time window and stores them in its datastore. The worker then reads from this persistent schedule to determine which calls to send.

The schedule is automatically recalculated whenever a change is detected in the source files. You can also manually trigger a refresh of the schedule by running the following command:

rufd scheduled refresh

This command will refetch all source files, recalculate the entire schedule, and update the datastore with the new information.

Configuration

The application is configured using a YAML file located at $XDG_CONFIG_HOME/rufd/config.yaml.

An example, well-documented configuration file can be found at /etc/rufd/config.yaml after installing the Debian package.

Time Slot Scheduling

This application supports a time slot scheduling feature that allows you to define specific time slots for your calls. To use this feature, you must use the explicit slot trigger in your call definition.

Slots can be configured on a per-destination basis, with a fallback to a global default. The configuration is hierarchical, with the following order of precedence:

  1. Destination-specific: slots.<type>.<destination> (e.g., slots.slack."#general")
  2. Type-specific: slots.<type>.default (e.g., slots.slack.default)
  3. Global default: slots.default

To configure the time slots, add a slots section to your config.yaml file. The following options are available:

  • timezone: The timezone to use for the time slots. It should be a valid IANA Time Zone database name (e.g. "Europe/Berlin").
Example
slots:
  timezone: "Europe/Berlin"
  default:
    monday:
      - "09:00"
      - "14:00"
  slack:
    default:
      monday:
        - "10:00"
    "#general":
      monday:
        - "11:00"

In this example:

  • All non-Slack destinations will use the global default slots (9am and 2pm on Mondays).
  • Slack destinations will use the Slack-specific default slots (10am on Mondays), unless a more specific configuration is provided.
  • The #general Slack channel will use its own specific slot (11am on Mondays).

If you do not configure any time slots, the application will default to "09:00" and "14:00" for every day of the week, in UTC.

Git Sources

The application supports fetching calls from Git repositories. The URL format is:

git://<repository>/tree/<refspec>/<file-path>

For example:

git://github.com/andrewhowdencom/ruf-example-announcements/tree/main/example.yaml

Repositories are cached locally in your XDG data directory (e.g., ~/.local/share/rufd/git-cache) to improve performance and support offline access to previously fetched files. The cache is automatically updated periodically.

Slack Configuration

To use the Slack integration, you'll need to create a Slack app and install it in your workspace. The app will need the following permissions:

  • channels:read: To list public channels.
  • groups:read: To list private channels.
  • chat:write: To send messages.
  • chat:write.customize: To send messages with a custom username and icon (for campaigns and author attribution).
  • chat:write.public: To send messages to public channels without explicitly joining them.
  • im:write: To send direct messages.
  • reactions:write: To add emoji reactions to messages.
  • users:read.email: To look up users by email.
  • users:read: To look up users by their Slack handle (e.g., @user).
  • channels:history: To list messages in public channels.
  • groups:history: To list messages in private channels.
  • im:history: To list messages in direct messages.
  • mpim:history: To list messages in multi-person direct messages.

Call Format

The application expects the source YAML files to contain a top-level calls list. Optionally, a campaign can be specified. If a campaign is not specified, it will be derived from the filename.

Each call must have a list of triggers that determine when the call should be sent. The following trigger types are available:

  • scheduled_at: A specific time to send the call.
  • cron: A cron expression for recurring calls.
  • rrule: An iCal rrule string for more complex recurring calls.
  • hijri: A date in the Islamic (Hijri) calendar.
  • slot: Explicitly find a time slot matching constraints (on, after, before).
  • sequence and delta: For event-driven call sequences.
Content Formatting

The content of a call can be written in Markdown. This will be automatically converted to the appropriate format for the destination. For example, it will be converted to HTML for email and Slack's mrkdwn for Slack.

Example

For a detailed example of a calls file, see examples/calls.yaml.

For an example of scheduling calls based on the Islamic (Hijri) calendar, see examples/hijri_schedule.yaml.

Event-Driven Call Sequences

In addition to scheduled and recurring calls, the application also supports event-driven call sequences. This feature allows you to define a sequence of calls that are triggered by a specific event.

To use this feature, you'll need to define a call with a trigger that has a sequence and a delta, and then create an event with a matching sequence and a start_time.

  • sequence: A unique identifier for the sequence.
  • delta: A duration string (e.g., "5m", "1h30m") that specifies when the call should be sent relative to the event's start_time.
  • events: A new top-level list in your source YAML file that contains a list of events.
Author Impersonation

When a Call includes an author email address, rufd will attempt to send the message on behalf of that user.

  • Slack: The message will appear to come from the author, using their Slack profile name and picture. If the user is not found in Slack, the message will be sent by the default bot, with the author's email appended to the message body for attribution.
  • Email: The application will first attempt to send the email with the From address set to the author's email. If the configured SMTP server rejects this (due to security policies like SPF/DKIM), it will fall back to sending from the default configured sender address, but will set the Reply-To header to the author's email.

Migrating from the Old Format

The application provides a migrate command to help you update your old YAML files to the new triggers format. To migrate from the v0 format to the v1 format, simply run:

rufd migrate v1 /path/to/your/file.yaml

The command will print the migrated YAML to the console.

Listing Sent Calls

When you list the sent calls, you will see the following statuses:

Status Description
sent The call has been successfully sent.
deleted The call has been sent and then subsequently deleted.

Getting it

You can download the latest version of the application from the GitHub Releases page.

Development

This application has been almost entirely "vibe coded" with Google Jules & Gemini.

Task Runner

This project uses Taskfile as a task runner for common development tasks. To use it, you'll first need to install it.

Installation

You can install Taskfile with the following command:

go install github.com/go-task/task/v3/cmd/task@latest
Usage

To see a list of all available tasks, run:

task --list

You can then run any task with task <task-name>.

Running as a Service

This project includes an example systemd unit file that can be used to run the application as a user-level service.

To install it, copy the file to ~/.config/systemd/user/:

mkdir -p ~/.config/systemd/user
cp examples/rufd.service ~/.config/systemd/user/

Documentation

Overview

Copyright © 2025 NAME HERE <EMAIL ADDRESS>

Directories

Path Synopsis
api
proto/v1
Package v1 is a reverse proxy.
Package v1 is a reverse proxy.
internal
kv

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL