Compare commits

..

2 Commits

17 changed files with 706 additions and 216 deletions

View File

@ -9,7 +9,7 @@
]; ];
programs.git.config.merge.tool = "meld"; programs.git.config.merge.tool = "meld";
programs.git.config.core.editor = "nvim"; programs.git.config.core.editor = "kak";
programs.neovim = { programs.neovim = {
enable = true; enable = true;
@ -26,12 +26,12 @@
''; '';
packages.myVimPackage = with pkgs.vimPlugins; { packages.myVimPackage = with pkgs.vimPlugins; {
start = [ start = [
rust-vim #rust-vim
vim-tmux-navigator vim-tmux-navigator
popup-nvim popup-nvim
vim-sleuth vim-sleuth
nvim-lspconfig nvim-lspconfig
rust-tools-nvim #rust-tools-nvim
tabby-nvim tabby-nvim
nvim-lint nvim-lint
nvim-cmp nvim-cmp
@ -44,7 +44,8 @@
telescope-file-browser-nvim telescope-file-browser-nvim
telescope-fzy-native-nvim telescope-fzy-native-nvim
nvim-treesitter.withAllGrammars nvim-treesitter.withAllGrammars
sweetie-nvim #sweetie-nvim
tokyonight-nvim
vim-nix vim-nix
csharpls-extended-lsp-nvim csharpls-extended-lsp-nvim
]; ];

View File

@ -1,4 +1,7 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let
swaylockCmd = "swaylock -c 1a1b26";
in
{ {
networking.networkmanager.enable = true; networking.networkmanager.enable = true;
@ -16,14 +19,182 @@
libinput.enable = true; libinput.enable = true;
displayManager.sddm = {
enable = true;
wayland.enable = true;
};
desktopManager.plasma6 = { desktopManager.plasma6 = {
enable = true; enable = true;
}; };
blueman.enable = true;
};
# sway stuff
programs.ydotool.enable = true;
users.users.audrey.extraGroups = [ "ydotool" ];
programs.regreet.enable = true;
services.greetd.settings = {
default_session.command = "${pkgs.dbus}/bin/dbus-run-session ${lib.getExe config.programs.sway.package} -c /etc/sway/greeter-config";
};
programs.regreet.settings = {
background.path = "/home/audrey/Pictures/smotsgamed.jpg";
background.fit = "Fill";
GTK.application_prefer_dark_theme = true;
};
environment.etc."sway/greeter-config".source = lib.mkForce (pkgs.writeText "sway-greeter-config" ''
exec "${lib.getExe config.programs.regreet.package}; swaymsg exit"
output * scale 2
input type:keyboard {
xkb_options "caps:escape"
}
input type:touchpad {
dwt enabled
dwtp enabled
tap enabled
tap_button_map lrm
natural_scroll enabled
}
# Brightness
bindsym --locked XF86MonBrightnessDown exec light -U 10
bindsym --locked XF86MonBrightnessUp exec light -A 10
blur enable
corner_radius 8
shadows enable
shadow_blur_radius 8
'');
programs.uwsm = {
enable = true;
waylandCompositors.sway = {
prettyName = "Sway";
binPath = "/run/current-system/sw/bin/sway";
};
};
programs.sway = {
enable = true;
package = pkgs.swayfx;
wrapperFeatures.gtk = true;
xwayland.enable = true;
extraPackages = with pkgs; [
swaylock
swayr
pavucontrol
libnotify
wdisplays
playerctl
grim
slurp
wl-clipboard
wlogout
fuzzel
gsettings-desktop-schemas
adwaita-icon-theme
glib
kdePackages.kwallet
];
extraSessionCommands = ''
export ELECTRON_OZONE_PLATFORM_HINT=wayland
export SDL_VIDEODRIVER=wayland
export QT_QPA_PLATFORM=wayland-egl
export QT_WAYLAND_DISABLE_WINDOWDECORATION=1
'';
};
environment.sessionVariables.XDG_DATA_DIRS = [ "/run/current-system/sw/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}" ];
programs.light.enable = lib.mkIf config.programs.sway.enable true;
security.pam.services.swaylock = {};
security.pam.loginLimits = [
{ domain = "@users"; item = "rtprio"; type = "-"; value = 1; }
];
security.pam.services = {
greetd.kwallet = {
enable = true;
package = pkgs.kdePackages.kwallet-pam;
forceRun = true;
};
greetd.rules.session.kwallet.settings.auto_start = true;
};
programs.dconf.enable = true;
systemd.user.targets.graphical-environment = lib.mkIf config.programs.sway.enable { };
systemd.user.services.kanshi = lib.mkIf config.programs.sway.enable {
description = "Monitor hotswap daemon";
serviceConfig = {
Type = "simple";
ExecStart = lib.getExe pkgs.kanshi;
};
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.swayidle = lib.mkIf config.programs.sway.enable {
description = "Idle lock + sleep manager";
serviceConfig = {
Type = "simple";
ExecStart = ''${lib.getExe pkgs.swayidle} -w \
timeout 300 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \
timeout 360 '${swaylockCmd} -f' \
timeout 600 'systemctl suspend' before-sleep '${swaylockCmd} -f'
'';
};
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.waybar = lib.mkIf config.programs.sway.enable {
description = "Desktop status bar";
serviceConfig = {
Type = "simple";
ExecStart = lib.getExe pkgs.waybar;
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.networkmanagerapplet = lib.mkIf config.programs.sway.enable {
description = "Networkmanager applet";
serviceConfig = {
Type = "simple";
ExecStart = lib.getExe pkgs.networkmanagerapplet;
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.swaynotificationcenter = lib.mkIf config.programs.sway.enable {
description = "Sway Notification Center";
serviceConfig = {
Type = "simple";
ExecStart = lib.getExe pkgs.swaynotificationcenter;
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.pasystray = lib.mkIf config.programs.sway.enable {
description = "Pulseaudio system tray icon";
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getExe pkgs.pasystray} --notify source --notify sink -m 100";
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.kdeconnect-indicator = lib.mkIf config.programs.sway.enable {
description = "KDE connect indicator";
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getBin pkgs.kdePackages.kdeconnect-kde}/bin/kdeconnect-indicator";
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
};
systemd.user.services.swayr = lib.mkIf config.programs.sway.enable {
description = "Sway MRU window switcher";
serviceConfig = {
Type = "simple";
ExecStart = "${lib.getBin pkgs.swayr}/bin/swayrd";
};
path = [ "/run/current-system/sw" ];
partOf = [ "graphical-environment.target" ];
wantedBy = [ "graphical-environment.target" ];
}; };
virtualisation.docker = { virtualisation.docker = {
@ -36,12 +207,21 @@
}; };
programs = { programs = {
chromium.enable = true;
firefox.enable = true; firefox.enable = true;
kdeconnect.enable = true; kdeconnect.enable = true;
partition-manager.enable = true;
wireshark.enable = true; wireshark.enable = true;
wireshark.package = pkgs.wireshark; wireshark.package = pkgs.wireshark;
foot.enable = true; foot.enable = true;
obs-studio = {
enable = true;
plugins = with pkgs.obs-studio-plugins; [
obs-livesplit-one
];
};
}; };
environment.sessionVariables.TERMINAL = "foot";
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
dino dino
@ -49,7 +229,8 @@
element-desktop element-desktop
signal-desktop signal-desktop
slack slack
obs-studio zotero
via
dwarfdump dwarfdump
@ -71,4 +252,7 @@
''; '';
before = [ "boot-complete.target" ]; before = [ "boot-complete.target" ];
}; };
hardware.keyboard.qmk.enable = true;
services.udev.packages = [ pkgs.via ];
} }

View File

@ -35,7 +35,7 @@ in {
# language servers # language servers
nil nil
rust-analyzer #rust-analyzer # misbehaves unless it's in a dev shell with other environment variables... see shelld
lua-language-server lua-language-server
clang-tools clang-tools
bash-language-server bash-language-server
@ -49,6 +49,7 @@ in {
]; ];
programs = { programs = {
firejail.enable = true;
virt-manager.enable = true; virt-manager.enable = true;
nix-ld = { nix-ld = {
enable = true; enable = true;
@ -66,6 +67,11 @@ in {
xorg.xcbutilkeysyms xorg.xcbutilkeysyms
xorg.xcbutilrenderutil xorg.xcbutilrenderutil
xorg.xcbutilwm xorg.xcbutilwm
xorg.libXrandr
xorg.libXxf86vm
xorg.libXi
xorg.libXcursor
xorg.libXinerama
]; ];
}; };
}; };

View File

@ -1,4 +1,4 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, pkgs-unstable, ... }:
let rhelmot = config.rhelmot; let rhelmot = config.rhelmot;
in { in {
options.rhelmot = { options.rhelmot = {
@ -7,8 +7,13 @@ in {
default = []; default = [];
description = "python packages (p: with p; [ x ]) to include in the global python environment"; description = "python packages (p: with p; [ x ]) to include in the global python environment";
}; };
globalKakounePlugins = lib.mkOption {
type = with lib.types; listOf package;
default = [];
description = "kakoune packages to include in the global editor";
};
}; };
imports = [ ./overlays/packages.nix ./configuration-cross.nix ]; imports = [ ./overlays/packages.nix ./overlays/lix.nix ./configuration-cross.nix ];
config = { config = {
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
@ -32,12 +37,16 @@ in {
uid = 1000; uid = 1000;
description = "Audrey Dutcher"; description = "Audrey Dutcher";
isNormalUser = true; isNormalUser = true;
extraGroups = [ "wheel" "docker" ]; extraGroups = [ "wheel" "docker" "video" "networkmanager" ];
openssh.authorizedKeys.keyFiles = [ ./keys/ssh ]; openssh.authorizedKeys.keyFiles = [ ./keys/ssh ];
}; };
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
man-pages
man-pages-posix
gnumake
wget wget
moar
ripgrep ripgrep
fd fd
curl curl
@ -45,10 +54,14 @@ in {
file file
nettools nettools
psmisc psmisc
units
units-desktop
patchelf patchelf
gdb gdb
kubectl
p7zip p7zip
unzip unzip
zip
foremost foremost
binwalk binwalk
jq jq
@ -56,9 +69,18 @@ in {
openssl openssl
wireguard-tools wireguard-tools
cached-nix-shell cached-nix-shell
tcpdump
editorconfig-core-c
pkgs-unstable.kakoune-lsp
(pkgs-unstable.kakoune.override { plugins = rhelmot.globalKakounePlugins; })
(python3.withPackages (p: lib.concatMap (pl: pl p) rhelmot.globalPythonPackages)) (python3.withPackages (p: lib.concatMap (pl: pl p) rhelmot.globalPythonPackages))
]; ];
rhelmot.globalKakounePlugins = with pkgs-unstable.kakounePlugins; [
kak-fzf
smarttab-kak
];
rhelmot.globalPythonPackages = [ (p: with p; [ rhelmot.globalPythonPackages = [ (p: with p; [
virtualenvwrapper virtualenvwrapper
pylint pylint
@ -70,8 +92,11 @@ in {
snakeviz snakeviz
requests requests
pysocks pysocks
aiohttp
]) ]; ]) ];
documentation.dev.enable = true;
programs = { programs = {
zsh = { zsh = {
enable = true; enable = true;
@ -93,7 +118,8 @@ in {
gits = "git status"; gits = "git status";
pag = "ps aux | grep -v grep | grep -i"; pag = "ps aux | grep -v grep | grep -i";
hd = "hexdump -C"; hd = "hexdump -C";
man = "MAN_POSIXLY_CORRECT=1 man"; hdc = "hexdump -ve '\"\\\x\" 1/1 \"%02x\"'";
man = "batman";
nose = "pytest -v --capture=no --pdbcls=IPython.terminal.debugger:TerminalPdb"; nose = "pytest -v --capture=no --pdbcls=IPython.terminal.debugger:TerminalPdb";
mkvirtualenv = "mkvirtualenv -r /etc/venv-default.txt"; mkvirtualenv = "mkvirtualenv -r /etc/venv-default.txt";
}; };
@ -116,8 +142,22 @@ in {
url."ssh://git@".insteadOf = "git://"; url."ssh://git@".insteadOf = "git://";
}; };
}; };
bat = {
enable = true;
extraPackages = with pkgs.bat-extras; [
batdiff
batman
prettybat
];
settings = {
italic-text = "always";
wrap = "never";
style = "plain";
};
};
}; };
environment.variables.PAGER = "moar";
environment.etc.zinputrc.text = lib.mkForce (builtins.readFile ./dotfiles/zsh-input.sh); environment.etc.zinputrc.text = lib.mkForce (builtins.readFile ./dotfiles/zsh-input.sh);
environment.etc."gdb/gdbinit".source = ./dotfiles/gdb-init.gdb; environment.etc."gdb/gdbinit".source = ./dotfiles/gdb-init.gdb;

View File

@ -8,7 +8,7 @@ if ok then
end end
end end
local rt = require("rust-tools") --local rt = require("rust-tools")
local lint = require('lint') local lint = require('lint')
local lspconfig = require('lspconfig') local lspconfig = require('lspconfig')
tb = require("telescope.builtin") tb = require("telescope.builtin")
@ -345,7 +345,7 @@ end
local pipe = io.popen('rustup which rust-analyzer --toolchain nightly 2>/dev/null') local pipe = io.popen('rustup which rust-analyzer --toolchain nightly 2>/dev/null')
local rust_analyzer = pipe:read() local rust_analyzer = pipe:read()
if rust_analyzer == nil then if rust_analyzer == nil then
pipe = io.popen('which rust-analyzer') pipe = io.popen('which rust-analyzer 2>/dev/null')
rust_analyzer = pipe:read() rust_analyzer = pipe:read()
end end
pipe = io.popen('which clangd') pipe = io.popen('which clangd')
@ -360,14 +360,25 @@ pipe = io.popen('which pyright-langserver')
pyright = pipe:read() pyright = pipe:read()
pipe.close() pipe.close()
if rust_analyzer ~= nil then if rust_analyzer ~= nil then
rt.setup({ --rt.setup({
server = { -- server = {
on_attach = lsp_keybinds, -- on_attach = lsp_keybinds,
capabilities = capabilities, -- capabilities = capabilities,
root_dir = rust_root_dir, -- root_dir = rust_root_dir,
cmd = {rust_analyzer}, -- cmd = {rust_analyzer},
-- },
--})
lspconfig.rust_analyzer.setup{
on_attach = lsp_keybinds,
capabilities = capabilities,
settings = {
["rust-analyzer"] = {
diagnostics = {
enable = true,
},
},
}, },
}) }
end end
if pyright ~= nil then if pyright ~= nil then
lspconfig.pyright.setup{ lspconfig.pyright.setup{
@ -509,24 +520,29 @@ show = function(x)
vim.notify(vim.inspect(x)) vim.notify(vim.inspect(x))
end end
vim.g.sweetie = { --vim.g.sweetie = {
palette = { -- palette = {
dark = { -- dark = {
bg_alt = "#151523", -- bg = "NONE",
bg_hl = "#505040", -- bg_alt = "#151523",
} -- bg_hl = "#505040",
}, -- }
overrides = { -- },
StatusLineNC = { fg = "#eeffee", bg = "#151523" }, -- overrides = {
StatusLine = {fg = "#ffffff", bg = "#151523", bold = true }, -- StatusLineNC = { fg = "#eeffee", bg = "#151523" },
MatchParen = { fg = '#ae920a', reverse = false }, -- StatusLine = {fg = "#ffffff", bg = "#151523", bold = true },
Search = { fg = '#ae920a', bg = "#0b658e" }, -- MatchParen = { fg = '#ae920a', reverse = false },
} -- Search = { fg = '#ae920a', bg = "#0b658e" },
-- }
--}
require("tokyonight").setup {
style = "night",
transparent = true,
} }
if vim.env.TERM == "tmux" or vim.env.TERM == "xterm" then if vim.env.TERM == "tmux" or vim.env.TERM == "xterm" then
vim.cmd.colorscheme("default") vim.cmd.colorscheme("default")
else else
vim.cmd.colorscheme("sweetie") vim.cmd.colorscheme("tokyonight")
end end
local tabtheme = { local tabtheme = {

View File

@ -10,7 +10,7 @@ unsetopt beep nomatch
# standard functions # standard functions
function nixos-edit() { function nixos-edit() {
(cd ~/nixos-config && vim configuration.nix) (cd ~/nixos-config && kak configuration.nix)
} }
function nixos-apply() { function nixos-apply() {
@ -93,11 +93,19 @@ function preexec-osc133-marker() {
print -Pn "\e]133;B\e\\" print -Pn "\e]133;B\e\\"
print -n "\e]133;C\e\\" print -n "\e]133;C\e\\"
} }
function precmd-osc-title() {
print -Pn "\e]2;%n@%M | %~\a"
}
function preexec-osc-title() {
print -Pn "\e]2;%n@%M | ${~1:gs/%/%%}\a"
}
autoload -Uz add-zsh-hook autoload -Uz add-zsh-hook
add-zsh-hook -Uz chpwd chpwd-osc7-pwd add-zsh-hook -Uz chpwd chpwd-osc7-pwd
add-zsh-hook -Uz precmd precmd-osc133-marker add-zsh-hook -Uz precmd precmd-osc133-marker
add-zsh-hook -Uz precmd precmd-osc-title
add-zsh-hook -Uz preexec preexec-osc133-marker add-zsh-hook -Uz preexec preexec-osc133-marker
add-zsh-hook -Uz preexec preexec-osc-title
# virtualenv integration # virtualenv integration
@ -120,6 +128,8 @@ export PYTHONBREAKPOINT="ipdb.set_trace"
export COLORTERM=1 export COLORTERM=1
export SHELL=$(which zsh) export SHELL=$(which zsh)
export npm_config_prefix=~/.local export npm_config_prefix=~/.local
export HISTSIZE=100000
export SAVEHIST=100000
# site vars, functions, and aliases # site vars, functions, and aliases
if [ -e ~/.site_aliases.sh ]; then if [ -e ~/.site_aliases.sh ]; then

View File

@ -26,29 +26,34 @@ key[CtrlRight]=${terminfo[kRIT5]}
bindkey -v bindkey -v
export KEYTIMEOUT=1 export KEYTIMEOUT=1
[[ -n "${key[Home]}" ]] && bindkey "${key[Home]}" beginning-of-line bindkey-both() {
[[ -n "${key[End]}" ]] && bindkey "${key[End]}" end-of-line bindkey "$@"
[[ -n "${key[Insert]}" ]] && bindkey "${key[Insert]}" overwrite-mode bindkey -a "$@"
[[ -n "${key[Delete]}" ]] && bindkey "${key[Delete]}" backward-delete-char }
[[ -n "${key[Up]}" ]] && bindkey "${key[Up]}" up-line-or-history
[[ -n "${key[Down]}" ]] && bindkey "${key[Down]}" down-line-or-history
[[ -n "${key[Left]}" ]] && bindkey "${key[Left]}" backward-char
[[ -n "${key[Right]}" ]] && bindkey "${key[Right]}" forward-char
[[ -n "${key[Backspace]}" ]] && bindkey "${key[Backspace]}" backward-delete-char
[[ -n "${key[PageUp]}" ]] && bindkey "${key[PageUp]}" up-history
[[ -n "${key[PageDown]}" ]] && bindkey "${key[PageDown]}" down-history
[[ -n "${key[CtrlLeft]}" ]] && bindkey "${key[CtrlLeft]}" backward-word
[[ -n "${key[CtrlRight]}" ]] && bindkey "${key[CtrlRight]}" forward-word
bindkey '^P' up-history [[ -n "${key[Home]}" ]] && bindkey-both "${key[Home]}" beginning-of-line
bindkey '^N' down-history [[ -n "${key[End]}" ]] && bindkey-both "${key[End]}" end-of-line
[[ -n "${key[Insert]}" ]] && bindkey-both "${key[Insert]}" overwrite-mode
[[ -n "${key[Delete]}" ]] && bindkey-both "${key[Delete]}" backward-delete-char
[[ -n "${key[Up]}" ]] && bindkey-both "${key[Up]}" up-line-or-history
[[ -n "${key[Down]}" ]] && bindkey-both "${key[Down]}" down-line-or-history
[[ -n "${key[Left]}" ]] && bindkey-both "${key[Left]}" backward-char
[[ -n "${key[Right]}" ]] && bindkey-both "${key[Right]}" forward-char
[[ -n "${key[Backspace]}" ]] && bindkey "${key[Backspace]}" backward-delete-char
[[ -n "${key[PageUp]}" ]] && bindkey-both "${key[PageUp]}" up-history
[[ -n "${key[PageDown]}" ]] && bindkey-both "${key[PageDown]}" down-history
[[ -n "${key[CtrlLeft]}" ]] && bindkey-both "${key[CtrlLeft]}" backward-word
[[ -n "${key[CtrlRight]}" ]] && bindkey-both "${key[CtrlRight]}" forward-word
bindkey-both '^P' up-history
bindkey-both '^N' down-history
bindkey '^h' backward-delete-char bindkey '^h' backward-delete-char
bindkey '^w' backward-kill-word bindkey '^w' backward-kill-word
bindkey '^r' history-incremental-search-backward bindkey-both '^r' history-incremental-search-backward
bindkey -a '/' history-incremental-search-backward bindkey -a '/' history-incremental-search-backward
# https://github.com/romkatv/zsh4humans/issues/7 # https://github.com/romkatv/zsh4humans/issues/7
bindkey "^[[H" beginning-of-line bindkey-both "^[[H" beginning-of-line
bindkey "^[[F" end-of-line bindkey-both "^[[F" end-of-line
# Finally, make sure the terminal is in application mode, when zle is # Finally, make sure the terminal is in application mode, when zle is
# active. Only then are the values from $terminfo valid. # active. Only then are the values from $terminfo valid.

View File

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"bingosync": { "bingosync": {
"locked": { "locked": {
"lastModified": 1751571764, "lastModified": 1760739933,
"narHash": "sha256-KEII5eCIBsBJy3eowAmmtRbdmV2aREOjU8AHypPz5y8=", "narHash": "sha256-yhgeHqfn0ZlXYkhZMqccTzWDvzre+DpcYzpk/7a7xec=",
"owner": "rhelmot", "owner": "rhelmot",
"repo": "bingosync", "repo": "bingosync",
"rev": "08ab441fee60360435c263430610fc10406b0602", "rev": "59f0555be4e7679750bc15396dc1b3e559325951",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -23,11 +23,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1745193236, "lastModified": 1759863336,
"narHash": "sha256-1hG3it3DDdn+VXmKUgF/KCbO3VMteEZ8xuh20jXqFus=", "narHash": "sha256-H8NRd03xQVKVunTYsd95pMzZS5nfYTDUw6R78dJESrs=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "5cb010a404c5a6c9fe2257aee2afe4aa3b288974", "rev": "bc6337d8f649f5afdc281b64fad2891bb2067a51",
"revCount": 8, "revCount": 11,
"type": "git", "type": "git",
"url": "https://git.lain.faith/rhelmot/blog.rhelmot.io" "url": "https://git.lain.faith/rhelmot/blog.rhelmot.io"
}, },
@ -45,11 +45,11 @@
"utils": "utils" "utils": "utils"
}, },
"locked": { "locked": {
"lastModified": 1745191548, "lastModified": 1759863318,
"narHash": "sha256-z8BhZIXNNNfGop+k/6+psSKp9BLz5eiuE16fDICdnyE=", "narHash": "sha256-6yXyEllmvAFgSg4KzFqJ3bx6K1+ZBsqOOdX08F29k08=",
"owner": "rhelmot", "owner": "rhelmot",
"repo": "coricamu", "repo": "coricamu",
"rev": "225536eafef6d8de6bdad3bc756587ad3ebaecb7", "rev": "f109bad2add146f3001805a8600b198473b3c9c2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -88,77 +88,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flakey-profile": {
"locked": {
"lastModified": 1712898590,
"narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=",
"owner": "lf-",
"repo": "flakey-profile",
"rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d",
"type": "github"
},
"original": {
"owner": "lf-",
"repo": "flakey-profile",
"type": "github"
}
},
"lix": { "lix": {
"flake": false,
"locked": {
"lastModified": 1751825501,
"narHash": "sha256-6Jve3xCvRJGkz0A9D8dXH/DMWYE8MkFt+KJWQs9cLkw=",
"rev": "378b360bf8e0d04e8dd3733acd20d7a9b70360f1",
"type": "tarball",
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/378b360bf8e0d04e8dd3733acd20d7a9b70360f1.tar.gz?rev=378b360bf8e0d04e8dd3733acd20d7a9b70360f1"
},
"original": {
"type": "tarball",
"url": "https://git.lix.systems/lix-project/lix/archive/main.tar.gz"
}
},
"lix-module": {
"inputs": {
"flake-utils": "flake-utils",
"flakey-profile": "flakey-profile",
"lix": "lix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1751909859,
"narHash": "sha256-gbpuESxl/An4GTh7QEbQRYJozVIxWkwVGbWK0/0GoRc=",
"ref": "refs/heads/main",
"rev": "4d4c2b8f0a801c91ce5b717c77fe3a17efa1402f",
"revCount": 150,
"type": "git",
"url": "https://git.lix.systems/lix-project/nixos-module"
},
"original": {
"type": "git",
"url": "https://git.lix.systems/lix-project/nixos-module"
}
},
"lix_2": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_2", "flake-compat": "flake-compat_2",
"nix2container": "nix2container", "nix2container": "nix2container",
@ -227,7 +157,7 @@
"nixbsd": { "nixbsd": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"lix": "lix_2", "lix": "lix",
"mini-tmpfiles": "mini-tmpfiles", "mini-tmpfiles": "mini-tmpfiles",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
@ -262,13 +192,29 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1753489912, "lastModified": 1757545623,
"narHash": "sha256-uDCFHeXdRIgJpYmtcUxGEsZ+hYlLPBhR83fdU+vbC1s=", "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "13e8d35b7d6028b7198f8186bc0347c6abaa2701", "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1757545623,
"narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -298,9 +244,9 @@
"inputs": { "inputs": {
"bingosync": "bingosync", "bingosync": "bingosync",
"blog-rhelmot-io": "blog-rhelmot-io", "blog-rhelmot-io": "blog-rhelmot-io",
"lix-module": "lix-module",
"nixbsd": "nixbsd", "nixbsd": "nixbsd",
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable"
} }
}, },
"systems": { "systems": {
@ -318,21 +264,6 @@
"type": "github" "type": "github"
} }
}, },
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": { "utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"

View File

@ -1,11 +1,8 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-25.05";
nixbsd.url = "github:nixos-bsd/nixbsd/main"; nixbsd.url = "github:nixos-bsd/nixbsd/main";
lix-module = {
url = "git+https://git.lix.systems/lix-project/nixos-module";
inputs.nixpkgs.follows = "nixpkgs";
};
bingosync.url = "github:rhelmot/bingosync"; bingosync.url = "github:rhelmot/bingosync";
blog-rhelmot-io.url = "git+https://git.lain.faith/rhelmot/blog.rhelmot.io"; blog-rhelmot-io.url = "git+https://git.lain.faith/rhelmot/blog.rhelmot.io";
@ -14,7 +11,7 @@
#nixos-defcon.url = "path:/home/audrey/nixos-defcon"; #nixos-defcon.url = "path:/home/audrey/nixos-defcon";
#nixos-defcon.inputs.nixpkgs.follows = "nixpkgs"; #nixos-defcon.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = { self, nixpkgs, nixbsd, lix-module, bingosync, /*nixos-defcon, */... }@flakeInputs: let outputs = { self, nixpkgs, nixbsd, bingosync, nixpkgs-unstable, ... }@flakeInputs: let
sitesFiles = builtins.readDir ./sites; sitesFiles = builtins.readDir ./sites;
sitesNames = builtins.filter (name: builtins.pathExists ./sites/${name}/configuration.nix) (builtins.attrNames sitesFiles); sitesNames = builtins.filter (name: builtins.pathExists ./sites/${name}/configuration.nix) (builtins.attrNames sitesFiles);
systemTypes = { systemTypes = {
@ -30,12 +27,14 @@
./configuration-${systemName name}.nix ./configuration-${systemName name}.nix
./sites/${name}/configuration.nix ./sites/${name}/configuration.nix
{ nixpkgs.buildPlatform = platform; } { nixpkgs.buildPlatform = platform; }
lix-module.nixosModules.default
bingosync.nixosModules.default bingosync.nixosModules.default
#nixos-defcon.nixosModules.pkgsOverlay #nixos-defcon.nixosModules.pkgsOverlay
#nixos-defcon.nixosModules.tulip #nixos-defcon.nixosModules.tulip
#nixos-defcon.nixosModules.noscope #nixos-defcon.nixosModules.noscope
]; ];
specialArgs = {
pkgs-unstable = nixpkgs-unstable.legacyPackages.${platform};
};
}; in { }; in {
inherit (evaluated) config options; inherit (evaluated) config options;
system = evaluated.config.system.build.toplevel; system = evaluated.config.system.build.toplevel;

15
overlays/lix.nix Normal file
View File

@ -0,0 +1,15 @@
{
pkgs,
...
}:
{
nixpkgs.overlays = [ (final: prev: {
inherit (prev.lixPackageSets.latest)
nixpkgs-review
nix-eval-jobs
nix-fast-build
colmena;
}) ];
nix.package = pkgs.lixPackageSets.latest.lix;
}

View File

@ -12,6 +12,7 @@ let overlay = final: prev: {
}); });
idapro9 = pkgs.callPackage ../pkgs/idapro9.nix {}; idapro9 = pkgs.callPackage ../pkgs/idapro9.nix {};
condition-unmetered-network = pkgs.callPackage ../pkgs/condition-unmetered-network {}; condition-unmetered-network = pkgs.callPackage ../pkgs/condition-unmetered-network {};
units-desktop = pkgs.callPackage ../pkgs/units-desktop.nix {};
}; };
in { in {

View File

@ -33,7 +33,7 @@
pyhidra, pyhidra,
writableTmpDirAsHomeHook, writableTmpDirAsHomeHook,
}: let }: let
libbs_latest = buildPythonPackage rec { libbs_latest = buildPythonPackage {
pname = "libbs"; pname = "libbs";
version = "2.15.5+dev"; version = "2.15.5+dev";
pyproject = true; pyproject = true;
@ -83,7 +83,7 @@
}; };
binsync_latest = buildPythonPackage rec { binsync_latest = buildPythonPackage {
pname = "binsync"; pname = "binsync";
version = "5.5.1+dev"; version = "5.5.1+dev";
pyproject = true; pyproject = true;

View File

@ -6,7 +6,7 @@
autoPatchelfHook, autoPatchelfHook,
copyDesktopItems, copyDesktopItems,
python311, python311,
libsForQt5, qt6,
cairo, cairo,
dbus, dbus,
fontconfig, fontconfig,
@ -40,13 +40,13 @@ in
# https://github.com/msanft/ida-pro-overlay/blob/main/packages/ida-pro.nix # https://github.com/msanft/ida-pro-overlay/blob/main/packages/ida-pro.nix
stdenv.mkDerivation (self: { stdenv.mkDerivation (self: {
pname = "idapro"; pname = "idapro";
version = "9.0.240807"; version = "9.2.250908";
src = requireFile { src = requireFile {
name = "IDA_Pro_Linux_9.tar.gz"; name = "idapro-linux-9.2.250908.tar.xz";
hash = "sha256-PKbEHc8dPHOMwv76xjoMUoeSn1jF7VXF9QUmQ4YSmP0="; hash = "sha256-daQtHbJxCuKzfGiBzkmy7FOTCMEJX3WL7IwuuvwIi+Y=";
message = '' message = ''
Please run nix store add-file IDA_Pro_Linux_9.tar.gz Please run nix store add-file idapro-linux-9.2.250908.tar.xz
Its sha256sum should be 3ca6c41dcf1d3c738cc2fefac63a0c5287929f58c5ed55c5f5052643861298fd Its sha256sum should be 75a42d1db2710ae2b37c6881ce49b2ec539308c1095f758bec8c2ebafc088be6
''; '';
}; };
@ -87,6 +87,7 @@ stdenv.mkDerivation (self: {
zlib zlib
curl.out curl.out
pythonForIDA pythonForIDA
qt6.qtwayland
]; ];
buildInputs = self.runtimeDependencies; buildInputs = self.runtimeDependencies;
@ -97,7 +98,6 @@ stdenv.mkDerivation (self: {
runHook preInstall runHook preInstall
mkdir -p $out/opt $out/lib $out/bin mkdir -p $out/opt $out/lib $out/bin
cp ${libsForQt5.qtbase.out}/lib/libQt5EglFSDeviceIntegration.so.5 .
cp -a * $out/opt cp -a * $out/opt
# Link the exported libraries to the output. # Link the exported libraries to the output.
@ -105,8 +105,8 @@ stdenv.mkDerivation (self: {
ln -s $lib $out/lib/$(basename $lib) ln -s $lib $out/lib/$(basename $lib)
done done
ln -s $out/opt/ida64 $out/bin/ida64 ln -s $out/opt/ida $out/bin/ida64
ln -s $out/opt/ida64 $out/bin/ida ln -s $out/opt/ida $out/bin/ida
ln -s ${pythonForIDA}/bin/binsync $out/bin/binsync ln -s ${pythonForIDA}/bin/binsync $out/bin/binsync
@ -114,27 +114,26 @@ stdenv.mkDerivation (self: {
''; '';
preFixup = '' preFixup = ''
patchelf --shrink-rpath --allowed-rpath-prefixes $(patchelf --print-rpath $out/opt/libQt5EglFSDeviceIntegration.so.5 | sed 's/:/\n/g' | grep -v qtbase | paste -s -d: -) $out/opt/libQt5EglFSDeviceIntegration.so.5
# Some libraries come with the installer. # Some libraries come with the installer.
addAutoPatchelfSearchPath $out/opt addAutoPatchelfSearchPath $out/opt
# Manually patch libraries that dlopen stuff. # Manually patch libraries that dlopen stuff.
patchelf --add-needed libpython${pythonForIDA.pythonVersion}.so $out/lib/libida.so patchelf --add-needed libpython${pythonForIDA.pythonVersion}.so $out/lib/libida.so
patchelf --add-needed libpython${pythonForIDA.pythonVersion}.so $out/opt/plugins/idapython3_64.so patchelf --add-needed libpython${pythonForIDA.pythonVersion}.so $out/opt/plugins/idapython3.so
patchelf --add-needed libcrypto.so $out/lib/libida.so patchelf --add-needed libcrypto.so $out/lib/libida.so
patchelf --add-needed libcrypto.so $out/opt/plugins/idapython3_64.so patchelf --add-needed libcrypto.so $out/opt/plugins/idapython3.so
wrapProgram "$out/opt/ida64" \ wrapProgram "$out/opt/ida" \
--prefix PYTHONPATH : $out/opt/idalib/python \ --prefix PYTHONPATH : $out/opt/idalib/python \
--prefix PATH : ${pythonForIDA}/bin --prefix PATH : ${pythonForIDA}/bin
''; '';
#--prefix QT_PLUGIN_PATH : $out/opt/plugins/platforms \ dontWrapQtApps = true;
desktopItem = makeDesktopItem { desktopItem = makeDesktopItem {
name = "ida-pro"; name = "ida-pro";
exec = "ida"; exec = "ida";
icon = runCommand "appico.png" {nativeBuildInputs = [gnutar]; strictDeps = true;} '' icon = runCommand "appico.png" {nativeBuildInputs = [gnutar]; strictDeps = true;} ''
tar --to-command cat -xf ${self.src} './idapro-9.0/appico.png' > "$out" tar --to-command cat -xf ${self.src} 'idapro-linux-9.2.250908/appico.png' > "$out"
''; '';
comment = self.meta.description; comment = self.meta.description;
desktopName = "IDA Pro"; desktopName = "IDA Pro";
@ -148,7 +147,7 @@ stdenv.mkDerivation (self: {
inherit pythonForIDA; inherit pythonForIDA;
}; };
meta = with lib; { meta = with lib; {
description = "The world's smartest and most feature-full disassembler"; description = "The world's smartest and most feature-full disassembler";
homepage = "https://hex-rays.com/ida-pro/"; homepage = "https://hex-rays.com/ida-pro/";
mainProgram = "ida"; mainProgram = "ida";

18
pkgs/units-desktop.nix Normal file
View File

@ -0,0 +1,18 @@
{
lib,
writeTextFile,
units,
}:
writeTextFile {
name = "units-desktop";
destination = "/share/applications/units.desktop";
text = ''
[Desktop Entry]
Encoding=UTF-8
Version=${units.version}
Type=Application
Terminal=true
Exec=${lib.getExe units}
Name=Units
'';
}

View File

@ -32,22 +32,23 @@
}; };
# not sure when this commit will reach upstream # not sure when this commit will reach upstream
boot.kernelPackages = pkgs.linuxPackages_latest.extend ( self: super: { #boot.kernelPackages = pkgs.linuxPackages_6_16.extend ( self: super: {
ipu6-drivers = super.ipu6-drivers.overrideAttrs ( # ipu6-drivers = super.ipu6-drivers.overrideAttrs (
final: previous: rec { # final: previous: rec {
src = builtins.fetchGit { # src = builtins.fetchGit {
url = "https://github.com/intel/ipu6-drivers.git"; # url = "https://github.com/intel/ipu6-drivers.git";
ref = "master"; # ref = "master";
rev = "b4ba63df5922150ec14ef7f202b3589896e0301a"; # rev = "b4ba63df5922150ec14ef7f202b3589896e0301a";
}; # };
patches = [ # patches = [
"${src}/patches/0001-v6.10-IPU6-headers-used-by-PSYS.patch" # "${src}/patches/0001-v6.10-IPU6-headers-used-by-PSYS.patch"
] ; # ] ;
} # }
); # );
} ); #} );
environment.systemPackages = [ environment.systemPackages = [
pkgs.racket
pkgs.idapro9 pkgs.idapro9
pkgs.qemu_kvm pkgs.qemu_kvm
(pkgs.runCommand "OVMF-fd" {} '' (pkgs.runCommand "OVMF-fd" {} ''
@ -62,6 +63,15 @@
openFirewall = true; openFirewall = true;
}; };
#services.coolify = {
# enable = true;
# hostname = "coolify";
#};
#networking.extraHosts = ''
# 127.0.0.1 coolify
#'';
#services.influxdb2 = { #services.influxdb2 = {
# enable = true; # enable = true;
# provision = { # provision = {
@ -174,4 +184,19 @@
# enable = true; # enable = true;
# nginxHost = "noscope"; # nginxHost = "noscope";
#}; #};
boot.binfmt.emulatedSystems = [
"aarch64-linux"
"mips-linux"
"mipsel-linux"
"armv7l-linux"
];
boot.binfmt.preferStaticEmulators = true;
programs.steam.enable = true;
programs.steam.gamescopeSession.enable = true;
programs.gamescope.enable = true;
programs.gamescope.capSysNice = true;
services.pulseaudio.support32Bit = true;
hardware.graphics.enable32Bit = true;
} }

View File

@ -43,15 +43,69 @@
extraPythonPackages = p: [ p.psycopg2 ]; extraPythonPackages = p: [ p.psycopg2 ];
}; };
users.users.wiki-js = {
isSystemUser = true;
group = "wiki-js";
};
users.groups.wiki-js = {};
users.groups.${config.services.forgejo.group}.members = [config.services.nginx.user];
services.wiki-js = {
enable = true;
settings = {
db.type = "postgres";
db.db = "wiki-js";
db.user = "wiki-js";
db.host = "/run/postgresql";
bindIP = "127.0.0.1";
port = 5517;
};
};
services.forgejo = {
enable = true;
lfs.enable = true;
database = {
createDatabase = true;
type = "postgres";
socket = "/run/postgresql";
};
settings = {
DEFAULT = {
APP_NAME = "Shellphish Git";
};
server = {
DOMAIN = "git.rhelmot.io";
PROTOCOL = "http+unix";
ROOT_URL = "https://git.rhelmot.io/";
UNIX_SOCKET_PERMISSION = "770";
LANDING_PAGE = "explore";
};
"ssh.minimum_key_sizes".RSA = "2047";
repository = {
ENABLE_PUSH_CREATE_USER = "true";
ENABLE_PUSH_CREATE_ORG = "true";
};
};
};
services.postgresql = { services.postgresql = {
enable = true; enable = true;
ensureDatabases = [ ensureDatabases = [
"bingosync" "bingosync"
"mspa" "mspa"
"wiki-js"
"forgejo"
]; ];
ensureUsers = [ ensureUsers = [
{ name = "bingosync"; ensureDBOwnership = true; } { name = "bingosync"; ensureDBOwnership = true; }
{ name = "mspa"; ensureDBOwnership = true; } { name = "mspa"; ensureDBOwnership = true; }
{ name = "wiki-js"; ensureDBOwnership = true; }
{ name = "forgejo"; ensureDBOwnership = true; }
]; ];
authentication = pkgs.lib.mkOverride 10 '' authentication = pkgs.lib.mkOverride 10 ''
#type database DBuser auth-method optional_ident_map #type database DBuser auth-method optional_ident_map
@ -61,8 +115,10 @@
# ArbitraryMapName systemUser DBUser # ArbitraryMapName systemUser DBUser
defaultmap root postgres defaultmap root postgres
defaultmap postgres postgres defaultmap postgres postgres
defaultmap php-nginx mspa defaultmap php-nginx mspa
defaultmap bingosync bingosync defaultmap bingosync bingosync
defaultmap wiki-js wiki-js
defaultmap forgejo forgejo
''; '';
}; };
@ -162,33 +218,44 @@
globalRedirect = "mimispastrypost.com"; globalRedirect = "mimispastrypost.com";
enableACME = true; enableACME = true;
}; };
"wiki.rhelmot.io" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:5517/";
proxyWebsockets = true;
};
};
"git.rhelmot.io" = {
forceSSL = true;
enableACME = true;
extraConfig = ''
client_max_body_size 4G;
'';
locations."/" = {
proxyPass = "http://unix:/run/forgejo/forgejo.sock";
proxyWebsockets = true;
};
};
}; };
}; };
systemd.services.spamkick = let users.users.reminder-bot = {
src = pkgs.fetchFromGitHub { isSystemUser = true;
owner = "maddie480"; group = "nogroup";
repo = "SpamKick"; };
rev = "9dd5b5e3cc78e2520b13a0875ae7ef264a5a52c5"; systemd.services.reminder-bot = {
hash = "sha256-ZjxnqIiXBaxrZwrCfDPVTpGmRxtrL5kc5ZcDUaQtbZo="; path = [ (pkgs.python3.withPackages (p: with p; [ discordpy aiocron aiosqlite cronsim ])) ];
};
env = pkgs.python3.withPackages (ps: with ps; [ discordpy ]);
in {
path = [ env ];
script = '' script = ''
export TOKEN="$(cat /var/lib/spamkick/token.txt)" exec python ${./reminder_bot.py}
exec python ${src}/main.py
''; '';
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
Restart = "always"; Restart = "always";
User = "reminder-bot";
}; };
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
environment = {
LOG_CHANNEL_ID = "532689319350108160";
CHANNEL_COUNT = "4";
DELAY_SECONDS = "5";
DEBUG = "0";
};
}; };
} }

View File

@ -0,0 +1,173 @@
from cronsim import CronSimError
import discord
from discord.ext import commands
import aiosqlite
import aiocron
from datetime import datetime, timedelta, timezone
import logging
log = logging.getLogger("reminder_bot")
DATABASE = '/var/lib/reminder-bot/db'
with open('/var/lib/reminder-bot/token') as fp:
TOKEN = fp.read().strip()
BOTSPAM_CHANNEL_ID = 1428820508178124820
intents = discord.Intents.default()
intents.message_content = True
client = commands.Bot('/', intents=intents)
tree = client.tree
# ------------------------------------------------------------
# DATABASE SETUP
# ------------------------------------------------------------
async def init_db():
async with aiosqlite.connect(DATABASE) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS reminders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
message TEXT NOT NULL,
cron TEXT NOT NULL
)
""")
await db.commit()
# ------------------------------------------------------------
# DISMISS BUTTON
# ------------------------------------------------------------
class DismissButton(discord.ui.View):
def __init__(self, reminder_id: int):
super().__init__(timeout=None)
self.reminder_id = reminder_id
@discord.ui.button(label="Dismiss", style=discord.ButtonStyle.red)
async def dismiss(self, interaction: discord.Interaction, button: discord.ui.Button):
user = interaction.user.display_name
timestamp = datetime.now(tz=timezone(timedelta(hours=-7))).strftime("%a %b %-m %-I:%M %p")
assert interaction.message is not None
button.disabled = True
await interaction.response.edit_message(content=interaction.message.content + f"\nCompleted by {user} at {timestamp}", view=self)
# ------------------------------------------------------------
# GLOBAL JOB REGISTRY
# ------------------------------------------------------------
scheduled_jobs = {}
async def send_reminder(message: str, reminder_id: int):
channel = client.get_channel(BOTSPAM_CHANNEL_ID)
assert isinstance(channel, discord.TextChannel)
await channel.send(
f"⏰ **Reminder:** {message}",
view=DismissButton(reminder_id),
allowed_mentions=discord.AllowedMentions(everyone=True, users=True, roles=True)
)
async def schedule_reminder(reminder_id: int, message: str, cron_expr: str):
assert reminder_id not in scheduled_jobs
job = aiocron.crontab(cron_expr, func=send_reminder, args=(message, reminder_id))
scheduled_jobs[reminder_id] = job
# ------------------------------------------------------------
# COMMANDS
# ------------------------------------------------------------
@tree.command(name="reminder-add")
async def add_reminder(ctx: discord.Interaction, cron_expr: str, *, message: str):
"""
Add a new reminder using a cron expression.
"""
try:
aiocron.CronSim(cron_expr, datetime.now())
except CronSimError as e:
await ctx.response.send_message(f"Your cron expression did not parse: {e}", ephemeral=True)
else:
async with aiosqlite.connect(DATABASE) as db:
cursor = await db.execute(
"INSERT INTO reminders (message, cron) VALUES (?, ?)",
(message, cron_expr)
)
await db.commit()
reminder_id = cursor.lastrowid
assert reminder_id is not None
await schedule_reminder(reminder_id, message, cron_expr)
await ctx.response.send_message(f"✅ Reminder {reminder_id} added: '{message}' `{cron_expr}`")
@tree.command(name="reminder-list")
async def list_reminders(ctx: discord.Interaction):
"""List all active reminders."""
async with aiosqlite.connect(DATABASE) as db:
async with db.execute("SELECT id, message, cron FROM reminders") as cursor:
rows = await cursor.fetchall()
if not rows:
await ctx.response.send_message("No active reminders.", ephemeral=True)
return
lines = [f"**{r[0]}**: {r[1]} `{r[2]}`" for r in rows]
await ctx.response.send_message("\n".join(lines), ephemeral=True)
@tree.command(name="reminder-delete")
async def delete_reminder(ctx: discord.Interaction, reminder_id: int):
"""Delete a reminder by ID."""
async with aiosqlite.connect(DATABASE) as db:
async with db.execute("SELECT message FROM reminders WHERE id = ?", (reminder_id,)) as cursor:
rows = list(await cursor.fetchall())
if not rows:
await ctx.response.send_message(f"No such reminder {reminder_id}")
return
await db.execute("DELETE FROM reminders WHERE id = ?", (reminder_id,))
await db.commit()
job = scheduled_jobs.pop(reminder_id, None)
if job:
job.stop()
await ctx.response.send_message(f"🗑️ Reminder {reminder_id} ({rows[0][0]}) deleted.")
@tree.command(name="reminder-edit")
async def edit_reminder(ctx: discord.Interaction, reminder_id: int, message: str, cron_expr: str):
try:
aiocron.CronSim(cron_expr, datetime.now())
except CronSimError as e:
await ctx.response.send_message(f"Your cron expression did not parse: {e}", ephemeral=True)
else:
async with aiosqlite.connect(DATABASE) as db:
async with db.execute("SELECT message FROM reminders WHERE id = ?", (reminder_id,)) as cursor:
rows = list(await cursor.fetchall())
if not rows:
await ctx.response.send_message(f"No such reminder {reminder_id}")
return
await db.execute("UPDATE reminders SET message = ?, cron = ? WHERE id = ?", (message, cron_expr, reminder_id))
await db.commit()
job = scheduled_jobs.pop(reminder_id, None)
if job:
job.stop()
await schedule_reminder(reminder_id, message, cron_expr)
await ctx.response.send_message(f"✅ Reminder {reminder_id} updated: {message} `{cron_expr}`")
# ------------------------------------------------------------
# STARTUP
# ------------------------------------------------------------
@client.event
async def on_ready():
await init_db()
# Load all reminders from DB and schedule them
async with aiosqlite.connect(DATABASE) as db:
async with db.execute("SELECT id, message, cron FROM reminders") as cursor:
async for reminder_id, message, cron_expr in cursor:
await schedule_reminder(reminder_id, message, cron_expr)
log.info(f"Loaded {len(scheduled_jobs)} scheduled reminders.")
await tree.sync()
# ------------------------------------------------------------
# RUN
# ------------------------------------------------------------
client.run(TOKEN)