This commit is contained in:
yash 2026-02-26 16:30:56 +03:00
parent 8a76cca5fb
commit 7eb4977b99
6 changed files with 537 additions and 5 deletions

187
flake.nix Normal file
View file

@ -0,0 +1,187 @@
{
description = "Crypto alert Telegram bot that monitors Bybit prices";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages.default = pkgs.buildGoModule {
pname = "crypto-alert-bot";
version = "0.1.0";
src = ./.;
vendorHash = "sha256-toXJCeMHa61YCFIYtsTl4dime015AF5LlB62QmYVaA8=";
subPackages = [ "cmd/app" ];
meta = with pkgs.lib; {
description = "Telegram bot for Bybit cryptocurrency price alerts";
mainProgram = "app";
};
};
devShells.default = pkgs.mkShell {
packages = with pkgs; [
go
gopls
gotools
golangci-lint
go-migrate # provides the `migrate` CLI
];
shellHook = ''
echo "crypto-alert-bot dev shell"
echo " Run: CONFIG_PATH=./internal/config/local.yml go run ./cmd/app/main.go"
'';
};
}
) // {
# NixOS module — import this in your NixOS config to run the bot as a service.
#
# The module manages PostgreSQL automatically (creates the DB and user,
# connects via Unix socket with peer auth). The only settings you must
# provide are the Telegram token file and optionally log/provider tuning.
#
# Minimal example:
# services.crypto-alert-bot = {
# enable = true;
# telegramTokenFile = "/run/secrets/telegram-token";
# };
nixosModules.default = { config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.crypto-alert-bot;
svcUser = "crypto-alert-bot";
# The config YAML is fully static (no secrets); the Telegram token is
# read from a file at ExecStartPre time and written into a per-run copy.
staticConfig = pkgs.writeText "crypto-alert-bot-base.yml" ''
logger:
service_name: "${cfg.logServiceName}"
encoding: "${cfg.logEncoding}"
level: "${cfg.logLevel}"
postgresql:
address: "/run/postgresql"
user: "${svcUser}"
db_name: "${cfg.dbName}"
telegram:
token: "__TELEGRAM_TOKEN__"
providers:
bybit:
base_url: "${cfg.bybitBaseUrl}"
'';
# Runs as root (+ prefix) before the main process. Copies the static
# config into the runtime directory and splices in the Telegram token.
configSetupScript = pkgs.writeShellScript "crypto-alert-bot-setup-config" ''
set -euo pipefail
token=$(< ${lib.escapeShellArg cfg.telegramTokenFile})
install -m 600 -o ${svcUser} ${staticConfig} /run/crypto-alert-bot/config.yml
sed -i "s|__TELEGRAM_TOKEN__|$token|" /run/crypto-alert-bot/config.yml
'';
in
{
options.services.crypto-alert-bot = {
enable = mkEnableOption "crypto alert bot";
package = mkOption {
type = types.package;
default = self.packages.${pkgs.system}.default;
defaultText = literalExpression "self.packages.\${system}.default";
description = "The crypto-alert-bot package to use.";
};
dbName = mkOption {
type = types.str;
default = svcUser;
description = "PostgreSQL database name (created automatically).";
};
telegramTokenFile = mkOption {
type = types.path;
description = "Path to a file containing the Telegram bot token (e.g. managed by agenix or sops-nix).";
};
logServiceName = mkOption {
type = types.str;
default = "alert-bot";
description = "Service name printed in log lines.";
};
logEncoding = mkOption {
type = types.enum [ "console" "json" ];
default = "json";
description = "Log output encoding.";
};
logLevel = mkOption {
type = types.enum [ "debug" "info" "warn" "error" ];
default = "info";
description = "Minimum log level.";
};
bybitBaseUrl = mkOption {
type = types.str;
default = "https://api.bybit.com";
description = "Bybit REST API base URL.";
};
};
config = mkIf cfg.enable {
# Create a stable system user whose name matches the PostgreSQL role
# so that peer (Unix socket) authentication works without a password.
users.users.${svcUser} = {
isSystemUser = true;
group = svcUser;
description = "Crypto alert bot service user";
};
users.groups.${svcUser} = {};
# Provision the database and role automatically.
services.postgresql = {
enable = true;
ensureDatabases = [ cfg.dbName ];
ensureUsers = [{
name = svcUser;
ensureDBOwnership = true;
}];
};
systemd.services.crypto-alert-bot = {
description = "Crypto Alert Telegram Bot";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "postgresql.service" ];
requires = [ "postgresql.service" ];
serviceConfig = {
# '+' prefix → root, so we can write to the RuntimeDirectory
# before switching to the unprivileged service user.
ExecStartPre = "+${configSetupScript}";
ExecStart = "${cfg.package}/bin/app";
Environment = "CONFIG_PATH=/run/crypto-alert-bot/config.yml";
User = svcUser;
Group = svcUser;
RuntimeDirectory = "crypto-alert-bot";
RuntimeDirectoryMode = "0700";
Restart = "on-failure";
RestartSec = "5s";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
};
};
};
};
};
}