add resources flow and secure config

Document resources, extend list actions, and wire Nix settings/token handling to support new resource saves and UI tweaks.
This commit is contained in:
TheGeneralist 2026-02-03 09:05:29 +01:00
parent 47a712b4ae
commit 6f3c5a6894
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
4 changed files with 916 additions and 85 deletions

6
AGENTS.md Normal file
View file

@ -0,0 +1,6 @@
# Repository Guidelines
## NixOS Module Notes
- `services.readlater-bot.settings` is rendered to TOML without the token; the token must come from `services.readlater-bot.tokenFile` and is combined at runtime in `/run/readlater-bot/config.toml` to keep secrets out of the Nix store.
- If you override `services.readlater-bot.user`/`group`, ensure the group exists; otherwise systemd fails at step GROUP. Defaults only auto-create the `readlater-bot` user/group when you keep the defaults.

11
SPEC.md
View file

@ -17,6 +17,16 @@ Read Later behavior
- New items are prepended (inserted immediately after any preamble).
- Deduping: exact full-block match (entire entry text). If identical block exists, skip add and inform user.
- Add acknowledgment: send Saved. and auto-delete after 5s.
- After single-item or multi-item saves, delete the user's original message.
Resources behavior
- /add <text> prompts for Reading list vs Resource.
- Add Resource is available in the selected item view; it does not change the current view.
- Resource adds prompt for a target .md file in resources_path (or a new filename).
- New resource entry is prepended to the chosen file as: `- (Auto-Resource): <message contents>`.
- Preserve additional lines after the first.
- Deduping: exact full-block match (entire entry text). If identical block exists, skip add and inform user.
- Resource acknowledgment: send Added to resources. and auto-delete after 5s.
Finished Reading behavior
- Mark Finished moves an entry: remove from Read Later, prepend to Finished (no separators).
@ -63,6 +73,7 @@ Config
- user_id (Telegram user ID)
- read_later_path (absolute path)
- finished_path (absolute path)
- resources_path (absolute path to resources directory)
- data_dir (absolute path)
- retry_interval_seconds (default 30, configurable)

View file

@ -38,6 +38,16 @@
}:
let
cfg = config.services.readlater-bot;
tomlFormat = pkgs.formats.toml { };
defaultSettings = {
data_dir = "/var/lib/readlater-bot";
retry_interval_seconds = 30;
};
mergedSettings = defaultSettings // cfg.settings;
settingsFile = tomlFormat.generate "readlater-bot.toml" mergedSettings;
runtimeConfig = "/run/readlater-bot/config.toml";
useRuntimeConfig = cfg.configFile == null;
configPath = if useRuntimeConfig then runtimeConfig else cfg.configFile;
in
{
options.services.readlater-bot = {
@ -47,22 +57,81 @@
default = self.packages.${pkgs.system}.default;
description = "Package providing the bot binary.";
};
settings = lib.mkOption {
type = tomlFormat.type;
default = { };
description = "TOML settings without the token.";
};
tokenFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Path to a file containing the Telegram bot token.";
};
configFile = lib.mkOption {
type = lib.types.path;
description = "Path to TOML config file.";
type = lib.types.nullOr lib.types.str;
default = null;
description = "Path to a TOML config file (bypasses settings/tokenFile).";
};
user = lib.mkOption {
type = lib.types.str;
default = "readlater-bot";
description = "User account for the bot service.";
};
group = lib.mkOption {
type = lib.types.str;
default = "readlater-bot";
description = "Group for the bot service.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.configFile != null || cfg.tokenFile != null;
message = "services.readlater-bot: set tokenFile with settings, or provide configFile.";
}
{
assertion = !(cfg.settings ? token);
message = "services.readlater-bot: do not set settings.token; use tokenFile.";
}
{
assertion = cfg.configFile == null || (cfg.settings == { } && cfg.tokenFile == null);
message = "services.readlater-bot: when configFile is set, do not set settings or tokenFile.";
}
];
users.users = lib.mkIf (cfg.user == "readlater-bot") {
readlater-bot = {
isSystemUser = true;
group = cfg.group;
};
};
users.groups = lib.mkIf (cfg.group == "readlater-bot") {
readlater-bot = { };
};
systemd.services.readlater-bot = {
description = "Read Later Telegram bot";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
preStart = lib.optionalString useRuntimeConfig ''
umask 0077
{
printf 'token = "%s"\n' "$(cat ${cfg.tokenFile})"
cat ${settingsFile}
} > ${runtimeConfig}
'';
serviceConfig = {
ExecStart = "${cfg.package}/bin/readlater-bot --config ${cfg.configFile}";
ExecStart = "${cfg.package}/bin/readlater-bot --config ${configPath}";
Restart = "on-failure";
RestartSec = 5;
User = cfg.user;
Group = cfg.group;
RuntimeDirectory = "readlater-bot";
RuntimeDirectoryMode = "0700";
StateDirectory = "readlater-bot";
StateDirectoryMode = "0700";
};
};
};

File diff suppressed because it is too large Load diff