1
0
mirror of https://github.com/kmein/niveum synced 2026-03-16 18:21:07 +01:00

20 Commits

Author SHA1 Message Date
8fd51be217 prometheus: monitor iching 2025-12-22 14:01:15 +01:00
6ac0c0bae4 prometheus: drop hu and fu monitoring 2025-12-22 14:01:06 +01:00
2eb69eb1fe update secrets 2025-12-22 13:59:11 +01:00
0b7308e602 dashboard: remove 2025-12-22 11:50:22 +01:00
f329f25992 picom: remove 2025-12-22 11:31:21 +01:00
11647db257 xsecurelock: replace slock, run before suspend 2025-12-22 11:31:21 +01:00
9f65360713 getty: add greeting 2025-12-22 11:27:56 +01:00
7c2e5533db remove fritzbox residue 2025-12-22 11:27:38 +01:00
32fa3e75ea mp3player-write 2025-12-22 08:51:07 +01:00
435aa4a365 secrets: update 2025-12-22 08:50:53 +01:00
8d955bf640 fatteh: configure systemd-boot 2025-12-22 08:50:44 +01:00
a44d15a166 nethack 2025-12-22 08:50:14 +01:00
b33e1d3569 zaatar: 25.11 2025-12-22 08:49:57 +01:00
cba0f92a7a zaatar: remove NAS 2025-12-22 08:49:35 +01:00
1f163d65cd atuin: remove 2025-12-22 08:40:28 +01:00
e816145b13 redshift: enable 2025-12-22 08:35:11 +01:00
4cb62b382b Merge remote-tracking branch 'origin/nethack' 2025-12-20 11:46:20 +01:00
ad2c922ab4 iching: init 2025-12-20 11:45:58 +01:00
a0f7867a25 tarot: generalize 2025-12-20 11:31:12 +01:00
dd75268d60 update secrets 2025-12-20 11:27:04 +01:00
19 changed files with 413 additions and 467 deletions

114
.bin/mp3player-write Executable file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Usage:
# ./mp3_transfer.sh -s 1.3 /mnt/mp3player file1.m4a file2.m4a ...
set -e
# Default speed
SPEED=1.0
# Parse options
while getopts ":s:" opt; do
case $opt in
s)
SPEED=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires a value." >&2
exit 1
;;
esac
done
# Shift past the options
shift $((OPTIND -1))
# Check arguments
if [ "$#" -lt 2 ]; then
echo "Usage: $0 [-s speed] MOUNT_POINT FILE1 [FILE2 ...]"
exit 1
fi
MOUNT_POINT=$1
shift
FILES=("$@")
# Check mount point exists
if [ ! -d "$MOUNT_POINT" ]; then
echo "Error: Mount point '$MOUNT_POINT' does not exist."
exit 1
fi
# Estimate required space
TOTAL_SIZE=0
for f in "${FILES[@]}"; do
if [ ! -f "$f" ]; then
echo "Warning: File '$f' does not exist, skipping."
continue
fi
# Get file size in bytes
FILE_SIZE=$(stat --printf="%s" "$f")
# Estimate mp3 output size: roughly 1/2 of original m4a (adjust if needed)
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE / 2))
done
# Get available space in bytes
AVAILABLE=$(df --output=avail "$MOUNT_POINT" | tail -n 1)
AVAILABLE=$((AVAILABLE * 1024)) # df reports in KB
if [ "$TOTAL_SIZE" -gt "$AVAILABLE" ]; then
echo "Error: Not enough space on device. Required: $TOTAL_SIZE bytes, Available: $AVAILABLE bytes"
exit 1
fi
echo "Enough space available. Starting conversion..."
sanitize_filename() {
local name="$1"
# Remove path, keep only base name
name=$(basename "$name" .m4a)
# Replace spaces and special chars with underscore
name=$(echo "$name" | tr ' ' '_' | tr -cd '[:alnum:]_-')
# Truncate to max 50 chars
echo "${name:0:50}"
}
# Convert and copy files
for f in "${FILES[@]}"; do
if [ ! -f "$f" ]; then
continue
fi
# Determine the next prefix
existing_prefixes=$(ls "$MOUNT_POINT" | grep -E '^[0-9].*\.mp3$' | sed -E 's/^([0-9]).*/\1/' | sort -n | uniq)
for i in {0..9}; do
if ! echo "$existing_prefixes" | grep -q "^$i$"; then
PREFIX=$i
break
fi
done
echo "Using prefix: $PREFIX"
BASENAME=$(sanitize_filename "$f")
OUT_PATTERN="$MOUNT_POINT/${PREFIX}%02d_${BASENAME}.mp3"
echo "Converting '$f' to '$OUT_PATTERN' at speed $SPEED..."
ffmpeg -i "$f" \
-filter:a "atempo=$SPEED" -ar 44100 -ac 2 -c:a libmp3lame -b:a 128k \
-f segment -segment_time 300 \
"$OUT_PATTERN"
# Update prefix for next file
# Count how many segments were created
SEG_COUNT=$(ls "$MOUNT_POINT" | grep -E "^${PREFIX}[0-9]{2}_" | wc -l)
PREFIX=$((PREFIX + SEG_COUNT))
done
echo "All files processed successfully."

View File

@@ -161,7 +161,7 @@ in
}
{
services.getty = {
greetingLine = lib.mkForce "";
greetingLine = lib.mkForce "As-salamu alaykum wa rahmatullahi wa barakatuh!";
helpLine = lib.mkForce "";
};
}
@@ -232,7 +232,6 @@ in
./flameshot.nix
./packages.nix
./virtualization.nix
./picom.nix
./stardict.nix
./polkit.nix
./printing.nix
@@ -257,25 +256,6 @@ in
}
./tor.nix
./mastodon-bot.nix
{
fileSystems."${remoteDir}/fritz" = {
device = "//192.168.178.1/FRITZ.NAS/Backup";
fsType = "cifs";
options = [
"username=ftpuser"
"password=ftppassword"
"noauto"
"nounix"
"rw"
# "noserverino" # ref https://askubuntu.com/a/1265165
"uid=${toString config.users.users.me.uid}"
"gid=${toString config.users.groups.users.gid}"
"x-systemd.automount"
"x-systemd.device-timeout=1"
"x-systemd.idle-timeout=1min"
];
};
}
{
home-manager.users.me = {
xdg.userDirs = rec {

View File

@@ -4,6 +4,12 @@
pkgs.zeroad
pkgs.mari0
pkgs.luanti # fka minetest
# pkgs.openarena
# pkgs.teeworlds
pkgs.nethack
# pkgs.freeciv
# pkgs.lincity-ng
# pkgs.superTuxKart
];
networking.firewall = {
# for 0ad multiplayer

View File

@@ -5,11 +5,6 @@
niveumPackages,
...
}: let
dashboard = pkgs.writers.writeDashBin "dashboard" ''
${pkgs.alacritty}/bin/alacritty --option font.size=4 --class dashboard --command ${pkgs.writers.writeDash "dashboard-inner" ''
exec ${pkgs.procps}/bin/watch -c -n 10 ${niveumPackages.q}/bin/q
''}
'';
inherit (import ../lib) defaultApplications;
klem = niveumPackages.klem.override {
config.dmenu = "${pkgs.dmenu}/bin/dmenu -i -p klem";
@@ -86,9 +81,14 @@ in {
};
};
programs.slock.enable = true;
environment.systemPackages = [dashboard];
environment.systemPackages = [
pkgs.xsecurelock
];
environment.sessionVariables = {
XSECURELOCK_NO_COMPOSITE = "1";
XSECURELOCK_BACKGROUND_COLOR = "navy";
XSECURELOCK_PASSWORD_PROMPT = "time_hex";
};
services.displayManager.defaultSession = "none+i3";
services.xserver = {
@@ -113,7 +113,6 @@ in {
'';
};
home-manager.users.me = let
modifier = "Mod4";
infoWorkspace = "";
@@ -275,7 +274,7 @@ in {
xsession.windowManager.i3 = {
enable = true;
extraConfig = ''
bindsym --release ${modifier}+Shift+w exec /run/wrappers/bin/slock
bindsym --release ${modifier}+Shift+w exec xsecurelock
exec "${pkgs.obsidian}/bin/obsidian"
for_window [class="obsidian"] , move scratchpad
@@ -284,8 +283,7 @@ in {
exec "${pkgs.writers.writeDash "irc" "exec ${pkgs.alacritty}/bin/alacritty --class message -e ssh weechat@makanek -t tmux attach-session -t IM"}"
exec "${pkgs.writers.writeDash "email" "exec ${pkgs.alacritty}/bin/alacritty --class message -e aerc"}"
assign [class="dashboard"] ${infoWorkspace}
exec ${dashboard}/bin/dashboard
exec --no-startup-id ${pkgs.xss-lock}/bin/xss-lock -- xsecurelock
'';
config = {
inherit modifier gaps modes bars floating window colors;

View File

@@ -1,25 +0,0 @@
{
services.picom = {
enable = true;
# activeOpacity = 1;
fade = true;
fadeDelta = 1;
# inactiveOpacity = 0.9;
# shadow = true;
# menuOpacity = 0.9;
# shadowOpacity = 0.3;
fadeExclude = [
"class_g = 'slock'" # don't want a transparent lock screen!
"name *?= 'slock'"
"focused = 1"
];
opacityRules = [
# opacity-rule overrides both inactive and active opacity
# video in browser tabs
# substring /regex match of title bar text
"99:name *?= 'Youtube'"
"99:WM_CLASS@:s *= 'mpv$'"
];
};
}

View File

@@ -1 +1 @@
{services.redshift.enable = false;}
{ services.redshift.enable = true; }

View File

@@ -6,15 +6,6 @@
promptColours.success = "cyan";
promptColours.failure = "red";
in {
environment.systemPackages = [pkgs.atuin];
environment.variables.ATUIN_CONFIG_DIR = toString (pkgs.writeTextDir "/config.toml" ''
auto_sync = true
update_check = false
sync_address = "http://zaatar.r:8888"
sync_frequency = 0
style = "compact"
'');
programs.zsh = let
zsh-completions = pkgs.fetchFromGitHub {
owner = "zsh-users";
@@ -67,13 +58,6 @@ in {
zstyle ':vcs_info:*' formats "%c%u%F{cyan}%b%f"
zstyle ':vcs_info:*' actionformats "(%a) %c%u%F{cyan}%b%f"
# atuin distributed shell history
export ATUIN_NOBIND="true" # disable all keybdinings of atuin
eval "$(atuin init zsh)"
bindkey '^r' _atuin_search_widget # bind ctrl+r to atuin
# use zsh only session history
fc -p
precmd () {
vcs_info
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] || [ -n "$SSH_CONNECTION" ]; then

View File

@@ -331,7 +331,6 @@
cro = pkgs.callPackage packages/cro.nix {};
default-gateway = pkgs.callPackage packages/default-gateway.nix {};
depp = pkgs.callPackage packages/depp.nix {};
dashboard = pkgs.callPackage packages/dashboard {};
fkill = pkgs.callPackage packages/fkill.nix {};
fzfmenu = pkgs.callPackage packages/fzfmenu.nix {};
gpt35 = pkgs.callPackage packages/gpt.nix {model = "gpt-3.5-turbo";};

View File

@@ -1,247 +0,0 @@
{
writers,
formats,
acpi,
wtf,
himalaya,
lib,
jq,
gh,
curl,
khal,
todoman,
gnused,
coreutils,
astrolog,
weatherCityIds ? [2950159],
}: let
rowCount = 10;
columnCount = 6;
yaml = formats.yaml {};
command = args:
{
enabled = true;
type = "cmdrunner";
}
// args;
configuration.wtf = rec {
grid = {
columns = lib.replicate columnCount 32;
rows = lib.replicate rowCount 5;
};
mods.vdir_khal = command {
title = "Calendar";
cmd = "${khal}/bin/khal";
args = ["--color" "list" "--exclude-calendar" "calendarium-tridentinum"];
refreshInterval = "1m";
position = {
top = 4;
left = 0;
height = 4;
width = 2;
};
};
mods.vdir_todo = command {
enabled = true;
title = "Agenda";
cmd = writers.writeDash "vdir_todo" "${todoman}/bin/todo --color=always -h | ${coreutils}/bin/tac";
refreshInterval = "1m";
position = {
top = 4;
left = 2;
height = 4;
width = 2;
};
};
mods.weather = {
enabled = true;
cityids = weatherCityIds;
position = {
top = 8;
left = 2;
height = 2;
width = 2;
};
refreshInterval = "15m";
language = "DE";
tempUnit = "C";
useEmoji = true;
compact = true;
};
mods.top = command {
title = "uptime";
cmd = writers.writeDash "top" "top -b -n 1 -E g | ${gnused}/bin/sed -n '1,5p'";
refreshInterval = "30s";
position = {
top = 4;
left = 4;
height = 2;
width = 2;
};
enabled = true;
};
mods.resourceusage = {
enabled = true;
cpuCombined = false;
position = {
top = 6;
left = 4;
height = 2;
width = 2;
};
refreshInterval = "1s";
showCPU = true;
showMem = true;
showSwp = false;
};
mods.ipapi = {
enabled = false;
position = {
top = 0;
left = 1;
height = 2;
width = 2;
};
refreshInterval = "150s";
};
mods.battery-status = command {
enabled = true;
cmd = writers.writeDash "battery-status" ''
${acpi}/bin/acpi --battery --details | sed 's/^Battery //'
'';
refreshInterval = "1m";
position = {
top = 8;
left = 4;
height = 2;
width = 2;
};
};
mods.disk-usage = command {
enabled = false;
cmd = "df";
args = ["-h"];
refreshInterval = "1m";
position = {
top = 8;
left = 4;
height = 2;
width = 2;
};
};
mods.email = command {
title = "Email";
cmd = writers.writeDash "email" ''
${himalaya}/bin/himalaya accounts --output json \
| ${jq}/bin/jq -r 'map(.name) | join("\n")' \
| while read -r account
do
${himalaya}/bin/himalaya list --account "$account" -o json \
| ${jq}/bin/jq -r '
map(select(.flags == [])
| "\u001b[33m\(.from.addr)\u001b[0m \(.subject)") | join("\n")
'
done
'';
refreshInterval = "5m";
position = {
top = 0;
left = 0;
height = 4;
width = 2;
};
};
mods.gh-status = command {
enabled = true;
title = "GitHub";
cmd = writers.writeDash "gh-status" ''
${gh}/bin/gh api notifications \
| ${jq}/bin/jq -r 'map("\u001b[35m\(.repository.full_name)\u001b[0m \(.subject.title)") | join("\n")'
'';
refreshInterval = "5m";
position = {
top = 0;
left = 2;
height = 2;
width = 2;
};
};
mods.gh-issues = command {
enabled = true;
title = "GitHub";
cmd = writers.writeDash "gh-issues" ''
${gh}/bin/gh api issues \
| ${jq}/bin/jq -r 'map(select(.repository.owner.login == "kmein") | "\u001b[35m\(.repository.name)\u001b[0m \(.title)") | join("\n")'
'';
refreshInterval = "5m";
position = {
top = 2;
left = 2;
height = 2;
width = 2;
};
};
mods.calendar = command {
title = "Calendar";
cmd = "cal";
args = ["-3" "-m" "-w"];
pty = true;
refreshInterval = "5m";
position = {
top = 8;
left = 0;
height = 2;
width = 2;
};
};
mods.astro-aspects = command {
title = "Aspects";
enabled = false;
cmd = writers.writeDash "astro-aspects" "${astrolog}/bin/astrolog -n -zN Berlin -d";
refreshInterval = "1h";
position = {
top = 7;
left = 3;
height = 1;
width = 2;
};
};
mods.feed = command {
enabled = true;
title = "Feed";
cmd = writers.writeDash "feed" ''
${curl}/bin/curl -u "$WTF_MINIFLUX_API_KEY" --basic -s 'https://feed.kmein.de/v1/entries?status=unread&direction=desc' \
| ${jq}/bin/jq -r '
.total as $total | (
.entries
| map(select(.feed | .hide_globally| not) | "\(.feed.category.title) \u001b[32m\(.author)\u001b[0m \(.title)")
| join("\n")
)'
'';
# position = { top = 0; left = 5; height = 5; width = 1; };
position = {
top = 0;
left = 4;
height = 4;
width = 2;
};
refreshInterval = "15m";
};
mods.astro-positions = command {
enabled = false;
title = "Positions";
cmd = writers.writeDash "astro-positions" "${astrolog}/bin/astrolog -q $(date +'%m %d %Y %H:%M') -zN Berlin | ${gnused}/bin/sed -n '4,16p' | ${coreutils}/bin/cut -c 1-33";
refreshInterval = "1h";
position = {
top = 5;
left = 5;
height = 3;
width = 1;
};
};
};
in
writers.writeDashBin "dashboard" ''
exec ${wtf}/bin/wtfutil --config=${yaml.generate "config.yml" configuration}
''

Submodule secrets updated: 3f3a8d1334...83d9103f20

View File

@@ -16,8 +16,17 @@
boot.initrd.kernelModules = [];
boot.kernelModules = ["kvm-intel"];
boot.extraModulePackages = [];
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot = {
loader = {
efi.canTouchEfiVariables = true;
systemd-boot = {
enable = true;
configurationLimit = 5;
consoleMode = "max";
};
};
};
boot.initrd.luks.devices."luks-aa6beb1b-3e54-4a0e-ac9c-e0c007d73cd5".device = "/dev/disk/by-uuid/aa6beb1b-3e54-4a0e-ac9c-e0c007d73cd5";

View File

@@ -20,7 +20,7 @@ in {
./scrabble.nix
# ./onlyoffice.nix
./retiolum-map.nix
./tarot.nix
./oracle
./tt-rss.nix
./weechat.nix
../../configs/monitoring.nix

View File

@@ -405,15 +405,11 @@ in
static_configs = [
{
targets = [
"https://alew.hu-berlin.de"
"https://beta.alew.hu-berlin.de"
"https://alew.hu-berlin.de/api/search?substring=die&domain=lemma&derivations=true&addition=true&diacritics=false&position=infix"
"https://beta.alew.hu-berlin.de/api/search?substring=die&domain=lemma&derivations=true&addition=true&diacritics=false&position=infix"
"https://zodiac.fly.dev/api/lemma/get?lemmaId=2582"
"https://pad.kmein.de"
"https://code.kmein.de"
"https://radio.kmein.de"
"https://tarot.kmein.de"
"https://iching.kmein.de"
"https://social.krebsco.de"
"https://cloud.kmein.de"
"http://grafana.kmein.r"

View File

@@ -6,6 +6,7 @@
}:
let
tarotPort = 7407;
ichingPort = 1819;
tarotFiles = pkgs.fetchzip {
url = "https://c.krebsco.de/tarot.zip";
sha256 = "0jl5vdwlj17pqp94yj02xgsb1gyvs9i08m83kac0jdnhfjl2f75a";
@@ -21,39 +22,30 @@ in
enable = true;
serviceConfig.Type = "simple";
wantedBy = [ "multi-user.target" ];
environment = {
TAROT_FILES = tarotFiles;
TAROT_PORT = toString tarotPort;
};
serviceConfig.ExecStart = pkgs.writers.writePython3 "tarot-server" {
libraries = py: [ py.pillow py.flask ];
} ''
from flask import Flask, send_file
from pathlib import Path
from random import choice, randint
from io import BytesIO
from PIL import Image
libraries = py: [
py.pillow
py.flask
];
} ./tarot.py;
};
app = Flask(__name__)
TAROT_DIR = Path("${tarotFiles}")
@app.route("/")
def tarot():
card_path = choice(list(TAROT_DIR.glob("*")))
with Image.open(card_path) as img:
if randint(0, 1):
img = img.rotate(180)
buf = BytesIO()
img.save(buf, format="JPEG")
buf.seek(0)
return send_file(
buf,
mimetype='image/jpeg',
as_attachment=False
)
if __name__ == "__main__":
app.run(port=${toString tarotPort})
'';
systemd.services.iching = {
enable = true;
serviceConfig.Type = "simple";
wantedBy = [ "multi-user.target" ];
environment = {
ICHING_PORT = toString ichingPort;
};
serviceConfig.ExecStart = pkgs.writers.writePython3 "iching-server" {
libraries = py: [
py.flask
];
} ./iching.py;
};
niveum.passport.services = [
@@ -62,8 +54,21 @@ in
title = "Tarot";
description = "draws Tarot cards for you. See <a href=\"${link}/files/key.pdf\">here</a> for information on how to interpret them.";
}
{
link = "https://iching.kmein.de";
title = "I Ching";
description = "draws I Ching hexagrams for you.";
}
];
services.nginx.virtualHosts."iching.kmein.de" = {
enableACME = true;
forceSSL = true;
locations = {
"/".proxyPass = "http://127.0.0.1:${toString ichingPort}/";
};
};
services.nginx.virtualHosts."tarot.kmein.de" = {
enableACME = true;
forceSSL = true;

View File

@@ -0,0 +1,202 @@
from flask import Flask
import random
from typing import List
from enum import Enum
import os
app = Flask(__name__)
KING_WEN_SEQUENCE: List[int] = [
0b111111,
0b000000,
0b100010,
0b010001,
0b111010,
0b010111,
0b010000,
0b000010,
0b111011,
0b110111,
0b111000,
0b000111,
0b101111,
0b111101,
0b001000,
0b000100,
0b100110,
0b011001,
0b110000,
0b000011,
0b100101,
0b101001,
0b000001,
0b100000,
0b100111,
0b111001,
0b100001,
0b011110,
0b010010,
0b101101,
0b001110,
0b011100,
0b001111,
0b111100,
0b000101,
0b101000,
0b101011,
0b110101,
0b001010,
0b010100,
0b110001,
0b100011,
0b111110,
0b011111,
0b000110,
0b011000,
0b010110,
0b011010,
0b101110,
0b011101,
0b100100,
0b001001,
0b001011,
0b110100,
0b101100,
0b001101,
0b011011,
0b110110,
0b010011,
0b110010,
0b110011,
0b001100,
0b101010,
0b010101,
]
class Line(Enum):
"""
Represents a line in an I Ching hexagram.
Each line can be one of the following:
- 6: Old Yin (changing yin)
- 7: Young Yang (static yang)
- 8: Young Yin (static yin)
- 9: Old Yang (changing yang)
"""
OLD_YIN = 6 # changing yin
YOUNG_YANG = 7 # static yang
YOUNG_YIN = 8 # static yin
OLD_YANG = 9 # changing yang
def is_changing(self) -> bool:
"""Returns True if the line is changing (old)."""
return self in [Line.OLD_YIN, Line.OLD_YANG]
def symbol(self) -> str:
"""Returns the textual representation of the line."""
symbols = {
Line.YOUNG_YANG: "───────",
Line.YOUNG_YIN: "─── ───",
Line.OLD_YANG: "───o───",
Line.OLD_YIN: "───x───",
}
return symbols[self]
def binary_value(self) -> int:
"""Returns the binary value of the line (1 for yang, 0 for yin)."""
return 1 if self in [Line.YOUNG_YANG, Line.OLD_YANG] else 0
class Hexagram:
"""
Represents an I Ching hexagram.
Each hexagram consists of six lines.
"""
def __init__(self, lines: List[Line] | None = None) -> None:
"""
Initializes a Hexagram.
If lines are not provided, generates a random hexagram.
:param lines: List of six integers = the lines of the hexagram.
"""
if lines is None:
self.lines = random.choices(
[Line.OLD_YIN, Line.YOUNG_YANG, Line.YOUNG_YIN, Line.OLD_YANG],
weights=[1, 5, 7, 3],
k=6,
)
else:
self.lines = lines
def print(self) -> str:
"""Prints the hexagram details."""
return "\n".join(
[
"HEXAGRAM {} {}".format(
self.king_wen_number(), self.unicode_representation()
),
"Binary: {:06b}".format(self.binary_representation()),
"Lines (bottom → top): {}".format(
" ".join(str(line.value) for line in self.lines)
),
self.render_text(),
]
)
def render_text(self) -> str:
"""Renders the hexagram in a textual box format."""
lines = []
lines.append("┌─────────┐")
for line in reversed(self.lines):
body = line.symbol()
lines.append(f"{body}")
lines.append("└─────────┘")
return "\n".join(lines)
def binary_representation(self) -> int:
"""Returns the binary representation of the hexagram."""
return sum(
val << i
for i, val in enumerate(
line.binary_value() for line in reversed(self.lines)
)
)
def king_wen_number(self) -> int:
"""Returns the King Wen number of the hexagram."""
return KING_WEN_SEQUENCE.index(self.binary_representation()) + 1
def unicode_representation(self) -> str:
"""Returns the Unicode character representing the hexagram."""
return chr(0x4DC0 + self.king_wen_number() - 1)
def changing_hexagram(self):
"""Returns the changing hexagram
if there are changing lines, else None."""
if any(line.is_changing() for line in self.lines):
new_lines = [
(
Line.YOUNG_YIN
if line == Line.OLD_YANG
else Line.YOUNG_YANG if line == Line.OLD_YIN else line
)
for line in self.lines
]
return Hexagram(new_lines)
return None
@app.route("/")
def main():
result = ""
hexagram = Hexagram()
result += hexagram.print()
if changing_hex := hexagram.changing_hexagram():
result += "\n"
result += changing_hex.print()
return f"<pre>{result}</pre>"
if __name__ == "__main__":
app.run(port=int(os.environ["ICHING_PORT"]))

View File

@@ -0,0 +1,26 @@
from flask import Flask, send_file
from pathlib import Path
from random import choice, randint
from io import BytesIO
from PIL import Image
import os
app = Flask(__name__)
TAROT_DIR = Path(os.environ["TAROT_FILES"])
@app.route("/")
def tarot():
card_path = choice(list(TAROT_DIR.glob("*")))
with Image.open(card_path) as img:
if randint(0, 1):
img = img.rotate(180)
buf = BytesIO()
img.save(buf, format="JPEG")
buf.seek(0)
return send_file(buf, mimetype="image/jpeg", as_attachment=False)
if __name__ == "__main__":
app.run(port=int(os.environ["TAROT_PORT"]))

View File

@@ -1,16 +0,0 @@
{pkgs, ...}: {
services.postgresqlBackup = {
enable = true;
databases = ["atuin"];
};
services.postgresql.package = pkgs.postgresql_14;
services.atuin = {
host = "0.0.0.0";
openFirewall = true;
openRegistration = true;
port = 8888;
enable = true;
};
}

View File

@@ -7,11 +7,9 @@
inherit (import ../../lib) retiolumAddresses restic;
in {
imports = [
./atuin.nix
./backup.nix
./gaslight.nix
./hardware-configuration.nix
./nas.nix
../../configs/mycelium.nix
./home-assistant.nix
../../configs/monitoring.nix
@@ -66,11 +64,9 @@ in {
];
};
services.logind = {
lidSwitch = "ignore";
lidSwitchDocked = "ignore";
lidSwitchExternalPower = "ignore";
};
services.logind.settings.Login.HandleLidSwitchDocked = "ignore";
services.logind.settings.Login.HandleLidSwitchExternalPower = "ignore";
services.logind.settings.Login.HandleLidSwitch = "ignore";
services.illum.enable = true;
@@ -88,9 +84,6 @@ in {
pkgs.python3 # for sshuttle
];
# since 22.05 timeout fails?
# systemd.services.systemd-networkd-wait-online.enable = false;
networking = {
hostName = "zaatar";
wireless.interfaces = ["wlp2s0"];

View File

@@ -1,78 +0,0 @@
{ config, ... }:
{
users.extraUsers.nas = {
isSystemUser = true;
group = "nas";
uid = 7451;
};
users.extraGroups.nas = {
gid = 7452;
};
fileSystems."/media/sd" = {
device = "/dev/disk/by-id/5E4S5_0x4c585d15-part1";
fsType = "ext4";
options = [
"nofail"
"defaults"
"uid=${toString config.users.extraUsers.nas.uid}"
"gid=${toString config.users.extraGroups.nas.gid}"
];
};
fileSystems."/media/hdd" = {
device = "/dev/disk/by-id/0x50014ee658872039-part1";
fsType = "ntfs";
options = [ # ref https://askubuntu.com/a/113746
"nofail"
"defaults"
"nls=utf8"
"umask=000"
"dmask=027"
"fmask=137"
"uid=${toString config.users.extraUsers.nas.uid}"
"gid=${toString config.users.extraGroups.nas.gid}"
"windows_names"
];
};
# ref https://dataswamp.org/~solene/2020-10-18-nixos-nas.html
# ref https://www.reddit.com/r/NixOS/comments/relwsh/comment/hoapgrr/
services.samba = {
enable = true;
securityType = "user";
openFirewall = true;
settings = {
global = {
"guest account" = "nobody";
"hosts allow" = ["192.168.178." "127.0.0.1" "localhost"];
"hosts deny" = ["0.0.0.0/0"];
"map to guest" = "Bad User";
"netbios name" = "zaatar";
"security" = "user";
"server role" = "standalone server";
"server string" = "zaatar";
"workgroup" = "WORKGROUP";
};
};
shares.nas = {
path = "/media";
browseable = "yes";
writable = "yes";
# "read only" = "no";
"guest ok" = "yes";
"create mask" = "0644";
"directory mask" = "0755";
"force user" = config.users.extraUsers.nas.name;
"force group" = config.users.extraUsers.nas.group;
};
};
services.samba-wsdd = {
enable = true;
openFirewall = true;
};
networking.firewall.enable = true;
networking.firewall.allowPing = true;
}