seems working
This commit is contained in:
parent
c1c092bea7
commit
c4f7edac42
|
@ -27,6 +27,8 @@ in
|
|||
};
|
||||
|
||||
# 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";
|
||||
|
@ -74,8 +76,8 @@ in
|
|||
xwayland.enable = true;
|
||||
extraPackages = with pkgs; [
|
||||
swaylock
|
||||
swayr
|
||||
pavucontrol
|
||||
pasystray
|
||||
libnotify
|
||||
wdisplays
|
||||
playerctl
|
||||
|
@ -88,7 +90,6 @@ in
|
|||
adwaita-icon-theme
|
||||
glib
|
||||
kdePackages.kwallet
|
||||
swaynotificationcenter
|
||||
];
|
||||
extraSessionCommands = ''
|
||||
export ELECTRON_OZONE_PLATFORM_HINT=wayland
|
||||
|
@ -165,6 +166,16 @@ in
|
|||
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 = {
|
||||
|
@ -175,6 +186,16 @@ in
|
|||
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 = {
|
||||
enable = true;
|
||||
|
@ -200,6 +221,7 @@ in
|
|||
];
|
||||
};
|
||||
};
|
||||
environment.sessionVariables.TERMINAL = "foot";
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
dino
|
||||
|
|
|
@ -13,7 +13,7 @@ in {
|
|||
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 = {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
|
@ -23,7 +23,6 @@ in {
|
|||
nix.settings.cores = 0;
|
||||
nix.settings.secret-key-files = [ "/var/lib/nix/binary-cache-key" ];
|
||||
nix.settings.trusted-public-keys = builtins.filter (f: f != "") <| lib.strings.splitString "\n" <| builtins.readFile ./keys/nix;
|
||||
nix.package = pkgs.lixPackageSets.stable.lix;
|
||||
|
||||
# Select internationalisation properties.
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
|
@ -45,9 +44,9 @@ in {
|
|||
environment.systemPackages = with pkgs; [
|
||||
man-pages
|
||||
man-pages-posix
|
||||
bat
|
||||
gnumake
|
||||
wget
|
||||
moar
|
||||
ripgrep
|
||||
fd
|
||||
curl
|
||||
|
@ -55,11 +54,14 @@ in {
|
|||
file
|
||||
nettools
|
||||
psmisc
|
||||
units
|
||||
units-desktop
|
||||
patchelf
|
||||
gdb
|
||||
kubectl
|
||||
p7zip
|
||||
unzip
|
||||
zip
|
||||
foremost
|
||||
binwalk
|
||||
jq
|
||||
|
@ -117,7 +119,7 @@ in {
|
|||
pag = "ps aux | grep -v grep | grep -i";
|
||||
hd = "hexdump -C";
|
||||
hdc = "hexdump -ve '\"\\\x\" 1/1 \"%02x\"'";
|
||||
man = "MAN_POSIXLY_CORRECT=1 man";
|
||||
man = "batman";
|
||||
nose = "pytest -v --capture=no --pdbcls=IPython.terminal.debugger:TerminalPdb";
|
||||
mkvirtualenv = "mkvirtualenv -r /etc/venv-default.txt";
|
||||
};
|
||||
|
@ -140,8 +142,22 @@ in {
|
|||
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."gdb/gdbinit".source = ./dotfiles/gdb-init.gdb;
|
||||
|
|
|
@ -97,7 +97,7 @@ function precmd-osc-title() {
|
|||
print -Pn "\e]2;%n@%M | %~\a"
|
||||
}
|
||||
function preexec-osc-title() {
|
||||
print -Pn "\e]2;%n@%M | $1\a"
|
||||
print -Pn "\e]2;%n@%M | ${~1:gs/%/%%}\a"
|
||||
}
|
||||
|
||||
autoload -Uz add-zsh-hook
|
||||
|
@ -128,6 +128,8 @@ export PYTHONBREAKPOINT="ipdb.set_trace"
|
|||
export COLORTERM=1
|
||||
export SHELL=$(which zsh)
|
||||
export npm_config_prefix=~/.local
|
||||
export HISTSIZE=100000
|
||||
export SAVEHIST=100000
|
||||
|
||||
# site vars, functions, and aliases
|
||||
if [ -e ~/.site_aliases.sh ]; then
|
||||
|
|
20
flake.lock
20
flake.lock
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"bingosync": {
|
||||
"locked": {
|
||||
"lastModified": 1751571764,
|
||||
"narHash": "sha256-KEII5eCIBsBJy3eowAmmtRbdmV2aREOjU8AHypPz5y8=",
|
||||
"lastModified": 1760739933,
|
||||
"narHash": "sha256-yhgeHqfn0ZlXYkhZMqccTzWDvzre+DpcYzpk/7a7xec=",
|
||||
"owner": "rhelmot",
|
||||
"repo": "bingosync",
|
||||
"rev": "08ab441fee60360435c263430610fc10406b0602",
|
||||
"rev": "59f0555be4e7679750bc15396dc1b3e559325951",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -23,11 +23,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745193236,
|
||||
"narHash": "sha256-1hG3it3DDdn+VXmKUgF/KCbO3VMteEZ8xuh20jXqFus=",
|
||||
"lastModified": 1759863336,
|
||||
"narHash": "sha256-H8NRd03xQVKVunTYsd95pMzZS5nfYTDUw6R78dJESrs=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "5cb010a404c5a6c9fe2257aee2afe4aa3b288974",
|
||||
"revCount": 8,
|
||||
"rev": "bc6337d8f649f5afdc281b64fad2891bb2067a51",
|
||||
"revCount": 11,
|
||||
"type": "git",
|
||||
"url": "https://git.lain.faith/rhelmot/blog.rhelmot.io"
|
||||
},
|
||||
|
@ -45,11 +45,11 @@
|
|||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745191548,
|
||||
"narHash": "sha256-z8BhZIXNNNfGop+k/6+psSKp9BLz5eiuE16fDICdnyE=",
|
||||
"lastModified": 1759863318,
|
||||
"narHash": "sha256-6yXyEllmvAFgSg4KzFqJ3bx6K1+ZBsqOOdX08F29k08=",
|
||||
"owner": "rhelmot",
|
||||
"repo": "coricamu",
|
||||
"rev": "225536eafef6d8de6bdad3bc756587ad3ebaecb7",
|
||||
"rev": "f109bad2add146f3001805a8600b198473b3c9c2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -12,6 +12,7 @@ let overlay = final: prev: {
|
|||
});
|
||||
idapro9 = pkgs.callPackage ../pkgs/idapro9.nix {};
|
||||
condition-unmetered-network = pkgs.callPackage ../pkgs/condition-unmetered-network {};
|
||||
units-desktop = pkgs.callPackage ../pkgs/units-desktop.nix {};
|
||||
};
|
||||
|
||||
in {
|
||||
|
|
|
@ -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
|
||||
'';
|
||||
}
|
|
@ -48,6 +48,7 @@
|
|||
#} );
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.racket
|
||||
pkgs.idapro9
|
||||
pkgs.qemu_kvm
|
||||
(pkgs.runCommand "OVMF-fd" {} ''
|
||||
|
@ -183,4 +184,19 @@
|
|||
# enable = true;
|
||||
# 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;
|
||||
}
|
||||
|
|
|
@ -93,10 +93,6 @@
|
|||
};
|
||||
};
|
||||
|
||||
services.keycloak = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [
|
||||
|
@ -246,30 +242,20 @@
|
|||
};
|
||||
};
|
||||
|
||||
systemd.services.spamkick = let
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "maddie480";
|
||||
repo = "SpamKick";
|
||||
rev = "9dd5b5e3cc78e2520b13a0875ae7ef264a5a52c5";
|
||||
hash = "sha256-ZjxnqIiXBaxrZwrCfDPVTpGmRxtrL5kc5ZcDUaQtbZo=";
|
||||
};
|
||||
env = pkgs.python3.withPackages (ps: with ps; [ discordpy ]);
|
||||
in {
|
||||
path = [ env ];
|
||||
users.users.reminder-bot = {
|
||||
isSystemUser = true;
|
||||
group = "nogroup";
|
||||
};
|
||||
systemd.services.reminder-bot = {
|
||||
path = [ (pkgs.python3.withPackages (p: with p; [ discordpy aiocron aiosqlite cronsim ])) ];
|
||||
script = ''
|
||||
export TOKEN="$(cat /var/lib/spamkick/token.txt)"
|
||||
exec python ${src}/main.py
|
||||
exec python ${./reminder_bot.py}
|
||||
'';
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
User = "reminder-bot";
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
LOG_CHANNEL_ID = "532689319350108160";
|
||||
CHANNEL_COUNT = "4";
|
||||
DELAY_SECONDS = "5";
|
||||
DEBUG = "0";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue