From 8b2d527c2315d26a0e473407d4c041eb3d805870 Mon Sep 17 00:00:00 2001 From: "Lennart J. Kurzweg (Nx2)" Date: Thu, 18 Apr 2024 18:41:53 +0200 Subject: [PATCH] no hypr folder + waybar cclock, nx_gcal_event --- .vscode/launch.json | 22 ++ .../hyprland-autoname-workspaces.nix | 0 home-modules/{hyprland => }/hyprland.nix | 36 --- home-modules/nx-gcal-event.nix | 222 ++++++++++++++++++ home-modules/python.nix | 18 ++ home-modules/{hyprland => }/waybar.nix | 52 +++- home.nix | 20 +- secrets/passwords-and-certificates.nix | Bin 2129 -> 2312 bytes 8 files changed, 317 insertions(+), 53 deletions(-) create mode 100644 .vscode/launch.json rename home-modules/{hyprland => }/hyprland-autoname-workspaces.nix (100%) rename home-modules/{hyprland => }/hyprland.nix (97%) create mode 100644 home-modules/nx-gcal-event.nix create mode 100644 home-modules/python.nix rename home-modules/{hyprland => }/waybar.nix (71%) diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..30a66d0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + + + + + + { + "name": "Python Debugger: Current File with Arguments", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": "${command:pickArgs}" + } + ] +} \ No newline at end of file diff --git a/home-modules/hyprland/hyprland-autoname-workspaces.nix b/home-modules/hyprland-autoname-workspaces.nix similarity index 100% rename from home-modules/hyprland/hyprland-autoname-workspaces.nix rename to home-modules/hyprland-autoname-workspaces.nix diff --git a/home-modules/hyprland/hyprland.nix b/home-modules/hyprland.nix similarity index 97% rename from home-modules/hyprland/hyprland.nix rename to home-modules/hyprland.nix index dd7463e..4844a49 100644 --- a/home-modules/hyprland/hyprland.nix +++ b/home-modules/hyprland.nix @@ -50,42 +50,6 @@ in pkgs-unstable.hyprlock pkgs-unstable.hypridle - (pkgs.writeShellScriptBin "waybar_mode" '' - #!/bin/bash - - # Function to print help message - print_help() { - echo "Usage: waybar_mode {set |unset}" - } - if [ $# -lt 1 ]; then - print_help; exit 1; - fi - - case "$1" in - set) - # Check if there is a second argument for the 'set' operation - if [ $# -eq 2 ]; then - echo "$2" > /tmp/waybar-mode - pkill -RTMIN+8 waybar - else - echo "Error: 'set' operation requires exactly one string argument." - print_help - exit 1 - fi - ;; - unset) - echo "" > /tmp/waybar-mode - pkill -RTMIN+8 waybar - ;; - *) - echo "Error: Unknown command '$1'" - print_help - exit 1 - ;; - esac - - exit 0 - '') ]; wayland.windowManager.hyprland = { diff --git a/home-modules/nx-gcal-event.nix b/home-modules/nx-gcal-event.nix new file mode 100644 index 0000000..6a7effe --- /dev/null +++ b/home-modules/nx-gcal-event.nix @@ -0,0 +1,222 @@ +{ config, pkgs, secrets, ... }: +let in +{ + home = { + file."${config.xdg.dataHome}/nx-gcal-event-credentials.json".text = '' + { + "installed": { + "client_id": "${secrets.nx-gcal-event.client-client-id}", + "project_id": "my-own-cal", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_secret": "${secrets.nx-gcal-event.client-secret}", + "redirect_uris": [ + "http://localhost" + ] + } + } + ''; + + packages = with pkgs; [ + # TODO: make into real package, currently dependencies are in home.nix + # (pkgs.python311.withPackages (python-pkgs: [ + # python-pkgs.google + # ])) + (writeScriptBin "nx_gcal_event" '' + #!${pkgs.python3}/bin/python3 + import datetime + import os + import pickle + import sys + + from google.auth.transport.requests import Request + from google.oauth2.credentials import Credentials + from google_auth_oauthlib.flow import InstalledAppFlow + from googleapiclient.discovery import build + from googleapiclient.errors import HttpError + from html import escape + + + + CREDENTIALS_PATH = f"{os.environ['XDG_DATA_HOME']}/nx-gcal-event-credentials.json" + TOKEN_PATH = f"{os.environ['XDG_CACHE_HOME']}/nx-gcal-event-token.json" + PICKLE_PATH = "/tmp/nx-gcal-event.pickle" + + + def sec_to_nice_string(seconds: int): + (hours, rsec) = divmod(seconds, 3600) + minutes = rsec // 60 + sep = " " + if hours == 0: + s_hours = f"" + sep = "" + elif hours == 1: s_hours = f"{hours} hour" + else: s_hours = f"{hours} hours" + if minutes == 0: + s_minutes = f"" + sep = "" + elif minutes == 1: s_minutes = f"{minutes} minute" + else: s_minutes = f"{minutes} minutes" + if hours + minutes == 0: + s_minutes = "~ No time" + os.remove(PICKLE_PATH) + + return f"{s_hours}{sep}{s_minutes}" + + def get_event_from_api(): + creds = None + SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"] + + if os.path.exists(TOKEN_PATH): + creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES) + + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_PATH, SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open(TOKEN_PATH, "w") as token: + token.write(creds.to_json()) + + try: + service = build("calendar", "v3", credentials=creds) + now = datetime.datetime.utcnow().isoformat() + "Z" # 'Z' indicates UTC time + in_24_h = (datetime.datetime.utcnow() + datetime.timedelta(days=1)).isoformat() + "Z" + calendar_list = service.calendarList().list().execute() + calendars = calendar_list.get("items", []) + + # List events from all calendars + all_events = [] + for calendar in calendars: + calendar_id = calendar["id"] + events_result = service.events().list( + calendarId=calendar_id, + timeMin=now, + timeMax=in_24_h, + singleEvents=True, + orderBy="startTime", + ).execute() + events = events_result.get("items", []) + + all_events.extend(events) + + # Filter out all-day events + all_events = [event for event in all_events if "dateTime" in event["start"]] + + # Find the earliest event + earliest_event = None + for event in all_events: + event_start = event["start"]["dateTime"] + if not earliest_event or event_start < earliest_event["start"]["dateTime"]: + earliest_event = event + + # Now earliest_event contains the event that starts earliest + return earliest_event + + except HttpError as error: + print("An error occurred: %s" % error) + exit(1) + + def dump_dict_to_file(event, now): + if not event: + event = {} + event['nxWriteTime'] = now + with open(PICKLE_PATH, 'wb') as f: + pickle.dump(event, f) + + def load_dict_from_file(now): + with open(PICKLE_PATH, 'rb') as f: + event = pickle.load(f) + # recheck all 15 minutes + if (now - event['nxWriteTime']).seconds > 900: + event = get_event_from_api() + os.remove(PICKLE_PATH) + return event + + def lookup(): + # set now (timezone CEST) + now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=2))) + + no_event_string = "~ Zen ~" + + if os.path.exists(PICKLE_PATH): + event = load_dict_from_file(now) + else: + event = get_event_from_api() + if not event: + print(no_event_string) + dump_dict_to_file(event, now) + exit(0) + dump_dict_to_file(event, now) + + try: # when the saved even was empty (there was no event) + end = datetime.datetime.strptime(event["end"]["dateTime"], "%Y-%m-%dT%H:%M:%S%z") + start = datetime.datetime.strptime(event["start"]["dateTime"], "%Y-%m-%dT%H:%M:%S%z") + except: + if (now - event['nxWriteTime']).seconds > 900: + event = get_event_from_api() + os.remove(PICKLE_PATH) + else: + print(no_event_string) + exit(0) + + # set mode, remaining + if start.day != now.day: # event start tomorrow + print(no_event_string) + exit(0) + elif (start - now).days < 0: # today, started alredy + remaining = end - now + mode = " remaining in " + else: # today, not started yet + remaining = start - now + mode = " until the start of " + + name = escape(event['summary']) + + print(f"󱙬 {sec_to_nice_string(remaining.seconds)}{mode}\'{name}\'") + exit(0) + + def print_help(): + print("Usage: nx_gcal_event [lookup|force-lookup|reauthenicate|help]") + + def forece_lookup(): + try: + os.remove(PICKLE_PATH) + os.system('notify-send --app-name="nx_gcal_event" "Saved event deleted!"') + except: + os.system('notify-send --app-name="nx_gcal_event" "No saved event found!"') + finally: + lookup() + + def reauthenicate(): + os.remove(PICKLE_PATH) + os.remove(TOKEN_PATH) + os.system('notify-send --app-name="nx_gcal_event" "Deleted Token"') + lookup() + + def print_help(): + print("Usage: nx_gcal_event [lookup|force-lookup|reauthenicate|help]") + + if __name__ == "__main__": + if len(sys.argv) != 2: + print("Incorrect number of arguments.") + print_help() + else: + arg = sys.argv[1] + if arg == "lookup": + lookup() + elif arg == "force-lookup": + forece_lookup() + elif arg == "reauthenticate": + reauthenicate() + elif arg == "help": + print_help() + else: + print_help() + '') + ]; + }; +} diff --git a/home-modules/python.nix b/home-modules/python.nix new file mode 100644 index 0000000..4d53a84 --- /dev/null +++ b/home-modules/python.nix @@ -0,0 +1,18 @@ +{ config, pkgs, ... }: +let + python-with-packages = pkgs.python3.withPackages (pp: with pp; [ + ipython + pipdeptree + requests + google google-api-python-client google-auth-httplib2 google-auth-oauthlib + ]); +in +{ + home.packages = [ + python-with-packages + ]; + + home.sessionVariables = { + PYTHONPATH = "${python-with-packages}/${python-with-packages.sitePackages}"; + }; +} \ No newline at end of file diff --git a/home-modules/hyprland/waybar.nix b/home-modules/waybar.nix similarity index 71% rename from home-modules/hyprland/waybar.nix rename to home-modules/waybar.nix index fab7142..292d2bf 100644 --- a/home-modules/hyprland/waybar.nix +++ b/home-modules/waybar.nix @@ -2,8 +2,56 @@ let in { + imports = [ + ./nx-gcal-event.nix + ]; + home.packages = with pkgs; [ waybar + + (pkgs.writeShellScriptBin "waybar_mode" '' + #!/usr/bin/env bash + print_help() { + echo "Usage: waybar_mode {set |unset}" + } + if [ $# -lt 1 ]; then + print_help; exit 1; + fi + case "$1" in + set) + # Check if there is a second argument for the 'set' operation + if [ $# -eq 2 ]; then + echo "$2" > /tmp/waybar-mode + pkill -RTMIN+8 waybar + else + echo "Error: 'set' operation requires exactly one string argument." + print_help + exit 1 + fi + ;; + unset) + echo "" > /tmp/waybar-mode + pkill -RTMIN+8 waybar + ;; + *) + echo "Error: Unknown command '$1'" + print_help + exit 1 + ;; + esac + exit 0 + '') + (pkgs.writeShellScriptBin "cclock" '' + #!/bin/bash + #ord=$(date +"%e" | awk '{printf("%d%s\n", $1, substr("thstndrd", ($1%100-20)%10*2+1, 2))}') + ord=$(date +"%e" | awk '{printf("%d%s\n", $1, ($1==11||$1==12||$1==13)?"th":((($1%10)==1)?"st":((($1%10)==2)?"nd":((($1%10)==3)?"rd":"th"))))}') + if [ $# -eq 0 ]; then + echo "󰃮 $(date +'%A the')" "$ord" "of" "$(date +'%B')" "  " "$(date +'%R')" + elif [ "$1" = "--no-icons" ]; then + echo "$(date +'%A the')" "$ord" "of" "$(date +'%B')" "$(date +'%R')" + fi + '') + ]; programs.waybar = { @@ -48,11 +96,11 @@ in separate-outputs = true; }; "custom/cclock" = { - exec = "/home/nx2/scripts/cclock.sh"; + exec = "cclock"; restart-interval = 60; }; "custom/ctimeremaining" = { - exec = "python /home/nx2/scripts/NxGCalEvent/get-remaining-time.py"; + exec = "nx_gcal_event lookup"; restart-interval = 60; }; "custom/mode" = { diff --git a/home.nix b/home.nix index af1e6ff..7e0d3dc 100644 --- a/home.nix +++ b/home.nix @@ -14,9 +14,9 @@ ./home-modules/pnx/pnx.nix # ./home-modules/hsmw.nix - ./home-modules/hyprland/hyprland.nix - ./home-modules/hyprland/hyprland-autoname-workspaces.nix - ./home-modules/hyprland/waybar.nix + ./home-modules/hyprland.nix + ./home-modules/hyprland-autoname-workspaces.nix + ./home-modules/waybar.nix ./home-modules/wlogout.nix ./home-modules/kitty.nix @@ -31,6 +31,8 @@ ./home-modules/theme/gtk.nix ./home-modules/theme/qt.nix + + ./home-modules/python.nix ]; home.username = "nx2"; home.homeDirectory = "/home/nx2"; @@ -66,18 +68,6 @@ gnumake speedtest-go - - (pkgs.python3.withPackages (python-pkgs: [ - python-pkgs.ipython - python-pkgs.pipdeptree - python-pkgs.requests - ])) - # (writeShellScriptBin "nxrbs-nix" '' - # set -e - # pushd ~/.nix-dots/ - # git diff - # '') - ]; xdg = { diff --git a/secrets/passwords-and-certificates.nix b/secrets/passwords-and-certificates.nix index 80b568a0ec568984047ac36567c7783d1a143277..40e0a49d2690b016992a5ba249a5039d9f3a0dcf 100644 GIT binary patch literal 2312 zcmZQ@_Y83kiVO&0XcJ8GZQE;;varejzuWCQg{m*xtCU`cWvI8A&XH2Mz9TWKe6@B! z*u&i-kHiY>&L7^ESpTDaEyvl<{Svmk*O=XodRTtZT%DK}uQW|*$JLiMrg{oj8#b0% zyuvsuZca;&?x7v ze2>CL*=4nTc8)A=Z6W~FUOHyYpAszx|qXXxTZJ(ELMO<~J`e zHEYiO=PtOUuZC@->7L;HY8DNjOT}9mb8l|$miW@NY4WyNqE6pmYdo4-vhnyiX2-LR zMl(AkcW63pwk_?DSXQoZ=!o3^2`!#plb7!LD$65fTF35qWBtyUX|FbK%3brDt#szi z2H#yNNiW5Y{OED5mI#XJ7F`z>JHPU7Rl}jnc^~4Lr4+KCb9$QR1=L=Caklu@Gl}zI zN}JstaF?%BJf_;KDx&bLqV%$!{@y!g1$OmCr}|o2wkvM2C^4MGWjXy|mfMyf-@9A+ z6Mh(WIxf(6WuA3 z-7N0kbz~Ga&f7F$n}Q8XD?8^hiTJ8RMo*8Pi@T)x*4yyFhGe~6ejUF9`4-IH<5=8~ zl(6Oeqb=eY0@CxAwR(EgzFN~NQIsjQWsa;W;~dT{PioEcUwe!HjVW5QpWQrl%fGC$ z!qB<{Qwle)@=e!ZTv(jE^+n#Ko7>kNF70f;{OYVg#vQ3ER^Q(4Q7>xh{dc77^>wW| zr+u0>rbVQ+rteqUeD$3o!-|8wo_ppnWJF0QUp{!oX`SMRM_b=%>)&H9aNxSJ^GktY zT$DBUg}43LvSKNQj!Nyf7nS`x!dd0^LbGv?L_>Vhwz@j^H0v_mIhDpbf9C$4`Fz(r zMu`K*w?C*bRf@~_^_c0!P3Nz@4vXjVrpZY?a}t?saQxc^7G>)|t6DG4h`0xCSt_CW zFD4(e{u;YBle=sBKg;Pq8~ww5_|snfRbRrt!Dx|W>ZTo^ezO(jvd$I@@6b%+3OH~f zs6FXx)`6$X@CB~spYTFt%CvULi!y&7G?3%dCFj*nUItffbHt6tAlS4$$oKsd`a?wTMUVLF!#@*RvU1LvrRS^tH~M`hr2lmg`@$Bbj>nDHuZS7_`z6Eh zagzFLmDPtj3MVf+_Nm(Myjav!#T5$jI(A&|HwMj)$uyfFBO(9nl3dVZb$_0teR|7T z-leU+Z}sG;OyTZzlkUh*z0$lhaj&idkLLRMCHwcu$8CHy#U(IhvN@bob{onU4OHS8Bdz z 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 PtEaTf-~W#+_}2pfj#nxy