diff --git a/arch_news.sh b/arch_news.sh new file mode 100755 index 0000000..8aaadb7 --- /dev/null +++ b/arch_news.sh @@ -0,0 +1,254 @@ +#!/bin/bash + +# Arch Linux News Notifier +# Fetches latest news from Arch Linux RSS feed and shows notifications for new items + +# Configuration +RSS_URL="https://archlinux.org/feeds/news/" +CACHE_FILE="$HOME/.cache/arch-news-seen" +TEMP_FILE="/tmp/arch-news-latest" + +ICON_NORMAL="/usr/share/pixmaps/tabler--coffee.svg" +ICON_URGENT="/usr/share/pixmaps/charm--circle-warning.svg" + +# Create cache directory if it doesn't existnClick to open: +mkdir -p "$(dirname "$CACHE_FILE")" + +# Function to extract and format news items +parse_rss() { + # Debug: show what we're working with + if [[ "$DEBUG" == "1" ]]; then + echo "=== DEBUG: RSS file content preview ===" >&2 + head -20 "$TEMP_FILE" >&2 + echo "=== END DEBUG ===" >&2 + fi + + if command -v xmllint >/dev/null 2>&1; then + # Use xmllint to extract items properly + xmllint --xpath "//item[position()<=5]" "$TEMP_FILE" 2>/dev/null | + sed 's||\n|g' | + sed 's||\n|g' | + grep -A 20 '' | + awk -v RS='' ' + // { + title = ""; link = ""; pubdate = "" + + # Extract title + if (match($0, /]*>([^<]*)<\/title>/, arr)) { + title = arr[1] + gsub(/>/, ">", title) + gsub(/</, "<", title) + gsub(/&/, "&", title) + } + + # Extract link (not atom:link) + if (match($0, /([^<]*)<\/link>/, arr)) { + link = arr[1] + } + + # Extract pubDate + if (match($0, /([^<]*)<\/pubDate>/, arr)) { + pubdate = arr[1] + } + + if (title && link && pubdate) { + print pubdate "|" title "|" link + } + }' + else + # Fallback: simple grep approach + local temp_items="/tmp/arch-items-$" + + # Extract each item block + awk '//,/<\/item>/' "$TEMP_FILE" >"$temp_items" + + # Process with simple pattern matching + while IFS= read -r line; do + if [[ "$line" =~ \([^<]*)\ ]]; then + title="${BASH_REMATCH[1]}" + title="${title//>/>}" + title="${title//</<}" + title="${title//&/&}" + elif [[ "$line" =~ \([^<]*)\ ]] && [[ ! "$line" =~ atom:link ]]; then + link="${BASH_REMATCH[1]}" + elif [[ "$line" =~ \([^<]*)\ ]]; then + pubdate="${BASH_REMATCH[1]}" + + # When we have all three, output and reset + if [[ -n "$title" && -n "$link" && -n "$pubdate" ]]; then + echo "$pubdate|$title|$link" + title="" + link="" + pubdate="" + fi + fi + done <"$temp_items" + + rm -f "$temp_items" + fi +} + +# Function to send notification +send_notification() { + local title="$1" + local body="$2" + local url="$3" + + # Determine urgency level based on content + local urgency="normal" + local icon=$ICON_NORMAL + + # Check for urgent intervention keywords (case insensitive) + if echo "$title $body" | grep -qi "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)"; then + urgency="critical" + icon=$ICON_URGENT + fi + + # Send notification with appropriate urgency + notify-send \ + --urgency="$urgency" \ + --app-name="Arch News" \ + --icon="$icon" \ + --expire-time=20000 \ + "$title" \ + "$body\n$url" + + # Debug output + if [[ "$DEBUG" == "1" ]]; then + echo "=== DEBUG: Notification sent ===" >&2 + echo "Title: $title" >&2 + echo "Urgency: $urgency" >&2 + echo "Reason: $(echo "$title $body" | grep -i "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)" || echo "normal news")" >&2 + echo "=== END DEBUG ===" >&2 + fi +} + +# Main execution +main() { + # Parse command line options + local force_show=false + local show_latest=false + + while [[ $# -gt 0 ]]; do + case $1 in + --force) + force_show=true + shift + ;; + --show-latest) + show_latest=true + shift + ;; + --clear-cache) + rm -f "$CACHE_FILE" + echo "Cache cleared" + exit 0 + ;; + --help | -h) + echo "Usage: $0 [options]" + echo "Options:" + echo " --force Show notifications for latest news even if already seen" + echo " --show-latest Show notification for the latest news item only" + echo " --clear-cache Clear the seen items cache" + echo " --help Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac + done + + # Check if required tools are available + if ! command -v xmllint >/dev/null 2>&1; then + echo "Error: xmllint is required (install libxml2-utils)" >&2 + exit 1 + fi + + if ! command -v notify-send >/dev/null 2>&1; then + echo "Error: notify-send is required" >&2 + exit 1 + fi + + # Fetch RSS feed + if ! curl -s --max-time 30 "$RSS_URL" >"$TEMP_FILE"; then + echo "Error: Failed to fetch RSS feed" >&2 + exit 1 + fi + + # Debug: Check if file was created and has content + if [[ "$DEBUG" == "1" ]]; then + echo "=== DEBUG: Temp file info ===" >&2 + ls -la "$TEMP_FILE" >&2 + echo "First few lines:" >&2 + head -5 "$TEMP_FILE" >&2 + echo "=== END DEBUG ===" >&2 + fi + + # Check if file is not empty + if [[ ! -s "$TEMP_FILE" ]]; then + echo "Error: RSS feed is empty" >&2 + exit 1 + fi + + # Create cache file if it doesn't exist + [[ ! -f "$CACHE_FILE" ]] && touch "$CACHE_FILE" + + # Parse RSS and check for new items + local new_items=0 + local items_processed=0 + + # Debug: show all parsed items + if [[ "$DEBUG" == "1" ]]; then + echo "=== DEBUG: All parsed RSS items ===" >&2 + parse_rss | nl >&2 + echo "=== END DEBUG ===" >&2 + fi + + while IFS='|' read -r pubdate title link; do + [[ -z "$title" ]] && continue + ((items_processed++)) + + # Create a unique identifier for this news item + local item_id=$(echo "$title$pubdate" | md5sum | cut -d' ' -f1) + + # Show latest item only if requested + if [[ "$show_latest" == true && $items_processed -eq 1 ]]; then + local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") + send_notification "$title" "Published: $formatted_date" "$link" + echo "$item_id" >>"$CACHE_FILE" + ((new_items++)) + break + fi + + # Check if we've already seen this item (or force showing) + if [[ "$force_show" == true ]] || ! grep -q "$item_id" "$CACHE_FILE"; then + # New item found - send notification + local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") + send_notification "$title" "Published: $formatted_date" "$link" + + # Mark as seen (only if not forcing) + if [[ "$force_show" == false ]]; then + echo "$item_id" >>"$CACHE_FILE" + fi + ((new_items++)) + + # Add delay between notifications for better mako stacking + [[ $new_items -gt 1 ]] && sleep 1.5 + fi + done < <(parse_rss) + + # Clean up + rm -f "$TEMP_FILE" + + # Limit cache file size (keep last 100 entries) + if [[ -f "$CACHE_FILE" ]]; then + tail -100 "$CACHE_FILE" >"$CACHE_FILE.tmp" && mv "$CACHE_FILE.tmp" "$CACHE_FILE" + fi + + echo "Checked Arch Linux news - $new_items new items found" +} + +# Run main function +main "$@" diff --git a/lock b/lock new file mode 100755 index 0000000..e143482 --- /dev/null +++ b/lock @@ -0,0 +1,55 @@ +#!/bin/bash +# Ayu Dark Theme Colors for i3lock-color + +alpha='dd' +background='#0B0E14' # Ayu dark background +selection='#1B3A5B' # Ayu selection background +golden_yellow='#FFB454' # Ayu accent color +bright_blue='#39BAE6' # Ayu tag color +orange='#FFB454' # Ayu function color +light_blue='#59C2FF' # Ayu entity color +green='#AAD94C' # Ayu string color +mint='#95E6CB' # Ayu regexp color +coral='#F07178' # Ayu markup color +orange_red='#FF8F40' # Ayu keyword color +purple='#D2A6FF' # Ayu constant color +red='#D95757' # Ayu error color + +i3lock \ + --insidever-color=$selection$alpha \ + --insidewrong-color=$selection$alpha \ + --inside-color=$background$alpha \ + --ringver-color=$green$alpha \ + --ringwrong-color=$red$alpha \ + --ring-color=$orange$alpha \ + --line-uses-ring \ + --keyhl-color=$purple$alpha \ + --bshl-color=$orange_red$alpha \ + --separator-color=$selection$alpha \ + --verif-color=$green \ + --wrong-color=$red \ + --modif-color=$red \ + --layout-color=$bright_blue \ + --date-color=$bright_blue \ + --time-color=$orange \ + --screen 1 \ + --blur 1 \ + --clock \ + --indicator \ + --time-str="%H:%M:%S" \ + --date-str="%A %e %B %Y" \ + --verif-text="Checking..." \ + --wrong-text="Wrong pswd" \ + --noinput="No Input" \ + --lock-text="Locking..." \ + --lockfailed="Lock Failed" \ + --radius=120 \ + --ring-width=10 \ + --pass-media-keys \ + --pass-screen-keys \ + --pass-volume-keys \ + --date-font="BlexMono Nerd Font Mono" \ + --verif-font="BlexMono Nerd Font Mono" \ + --layout-font="BlexMono Nerd Font Mono" \ + --time-font="BlexMono Nerd Font Mono" \ + --wrong-font="BlexMono Nerd Font Mono" diff --git a/lorem b/lorem new file mode 100755 index 0000000..23ea9a5 --- /dev/null +++ b/lorem @@ -0,0 +1,40 @@ +#!/bin/bash + +# Check if argument is provided +if [ $# -eq 0 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Check if argument is a number +if ! [[ "$1" =~ ^[0-9]+$ ]]; then + echo "Error: Please provide a valid number" + exit 1 +fi + +# Lorem ipsum word bank +words=( + "lorem" "ipsum" "dolor" "sit" "amet" "consectetur" "adipiscing" "elit" + "sed" "do" "eiusmod" "tempor" "incididunt" "ut" "labore" "et" "dolore" + "magna" "aliqua" "enim" "ad" "minim" "veniam" "quis" "nostrud" + "exercitation" "ullamco" "laboris" "nisi" "aliquip" "ex" "ea" "commodo" + "consequat" "duis" "aute" "irure" "in" "reprehenderit" "voluptate" + "velit" "esse" "cillum" "fugiat" "nulla" "pariatur" "excepteur" "sint" + "occaecat" "cupidatat" "non" "proident" "sunt" "culpa" "qui" "officia" + "deserunt" "mollit" "anim" "id" "est" "laborum" +) + +# Generate requested number of words +result="" +for ((i=1; i<=$1; i++)); do + # Get random word from array + word=${words[$RANDOM % ${#words[@]}]} + + if [ $i -eq 1 ]; then + result="$word" + else + result="$result $word" + fi +done + +echo "$result" diff --git a/rentry b/rentry new file mode 100755 index 0000000..1b2ea15 --- /dev/null +++ b/rentry @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 + +import getopt +import http.cookiejar +import sys +import urllib.parse +import urllib.request +from http.cookies import SimpleCookie +from json import loads as json_loads +from os import environ + +_headers = {"Referer": 'https://rentry.co'} + + +class UrllibClient: + """Simple HTTP Session Client, keeps cookies.""" + + def __init__(self): + self.cookie_jar = http.cookiejar.CookieJar() + self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookie_jar)) + urllib.request.install_opener(self.opener) + + def get(self, url, headers={}): + request = urllib.request.Request(url, headers=headers) + return self._request(request) + + def post(self, url, data=None, headers={}): + postdata = urllib.parse.urlencode(data).encode() + request = urllib.request.Request(url, postdata, headers) + return self._request(request) + + def _request(self, request): + response = self.opener.open(request) + response.status_code = response.getcode() + response.data = response.read().decode('utf-8') + return response + + +def raw(url): + client = UrllibClient() + return json_loads(client.get('https://rentry.co/api/raw/{}'.format(url)).data) + + +def new(url, edit_code, text): + client, cookie = UrllibClient(), SimpleCookie() + + cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie']) + csrftoken = cookie['csrftoken'].value + + payload = { + 'csrfmiddlewaretoken': csrftoken, + 'url': url, + 'edit_code': edit_code, + 'text': text + } + + return json_loads(client.post('https://rentry.co/api/new', payload, headers=_headers).data) + + +def edit(url, edit_code, text): + client, cookie = UrllibClient(), SimpleCookie() + + cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie']) + csrftoken = cookie['csrftoken'].value + + payload = { + 'csrfmiddlewaretoken': csrftoken, + 'edit_code': edit_code, + 'text': text + } + + return json_loads(client.post('https://rentry.co/api/edit/{}'.format(url), payload, headers=_headers).data) + + +def usage(): + print(''' +Usage: rentry {new | edit | raw} {-h | --help} {-u | --url} {-p | --edit-code} text + +Commands: + new create a new entry + edit edit an existing entry + raw get raw markdown text of an existing entry + +Options: + -h, --help show this help message and exit + -u, --url URL url for the entry, random if not specified + -p, --edit-code EDIT-CODE edit code for the entry, random if not specified + +Examples: + rentry new 'markdown text' # new entry with random url and edit code + rentry new -p pw -u example 'text' # with custom edit code and url + rentry edit -p pw -u example 'text' # edit the example entry + cat file | rentry new # read from pipe and paste it to rentry + rentry raw -u example # get raw markdown text + rentry raw -u https://rentry.co/example # -u accepts absolute and relative urls + ''') + + +if __name__ == '__main__': + try: + environ.pop('POSIXLY_CORRECT', None) + opts, args = getopt.gnu_getopt(sys.argv[1:], "hu:p:", ["help", "url=", "edit-code="]) + except getopt.GetoptError as e: + sys.exit("error: {}".format(e)) + + command, url, edit_code, text = None, '', '', None + + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-u", "--url"): + url = urllib.parse.urlparse(a).path.strip('/') + elif o in ("-p", "--edit-code"): + edit_code = a + + command = (args[0:1] or [None])[0] + command or sys.exit(usage()) + command in ['new', 'edit', 'raw'] or sys.exit('error: command must be new, edit or raw') + + text = (args[1:2] or [None])[0] + if not text and command != 'raw': + text = sys.stdin.read().strip() + text or sys.exit('error: text is required') + + if command == 'new': + response = new(url, edit_code, text) + if response['status'] != '200': + print('error: {}'.format(response['content'])) + try: + for i in response['errors'].split('.'): + i and print(i) + sys.exit(1) + except: + sys.exit(1) + else: + print('Url: {}\nEdit code: {}'.format(response['url'], response['edit_code'])) + + elif command == 'edit': + url or sys.exit('error: url is required') + edit_code or sys.exit('error: edit code is required') + + response = edit(url, edit_code, text) + if response['status'] != '200': + print('error: {}'.format(response['content'])) + try: + for i in response['errors'].split('.'): + i and print(i) + sys.exit(1) + except: + sys.exit(1) + else: + print('Ok') + + elif command == 'raw': + url or sys.exit('error: url is required') + response = raw(url) + if response['status'] != '200': + sys.exit('error: {}'.format(response['content'])) + print(response['content']) diff --git a/waybar/config.jsonc b/waybar/config.jsonc index 381404a..22e3b66 100644 --- a/waybar/config.jsonc +++ b/waybar/config.jsonc @@ -78,7 +78,7 @@ }, "custom/power": { "format": "󰐥", - "on-click": "zenity --question --text='Are you sure you want to power off?' --icon-name='system-shutdown' --title='Power Off System' && sleep 1 && systemctl poweroff", + "on-click": "zenity --question --text='Are you sure you want to power off?' --icon-name='system-shutdown' --title='Power Off System' && sleep 1 && loginctl poweroff", "tooltip": true, "tooltip-format": "Power Off" }, @@ -188,9 +188,9 @@ }, "clock": { "interval": 1, - "format": "󰃰 {:%a:%H:%M}", + "format": "󰃰 {:%a %H:%M}", "tooltip": true, - "tooltip-format": "{:L%Y-%m-%d, %A}" + "tooltip-format": "{:L%A, %d-%m-%Y}" }, "tray": { "icon-size": 14, diff --git a/waylock b/waylock new file mode 100755 index 0000000..2d7bb4a --- /dev/null +++ b/waylock @@ -0,0 +1,47 @@ +#!/bin/bash + +# Gruvbox Dark Theme Swaylock Command +# Colors based on the Gruvbox color scheme +# Background: dark0_hard (#1d2021) +# Foreground: light1 (#ebdbb2) +# Red: bright_red (#fb4934) +# Green: bright_green (#b8bb26) +# Yellow: bright_yellow (#fabd2f) +# Blue: bright_blue (#83a598) +# Purple: bright_purple (#d3869b) +# Orange: bright_orange (#fe8019) + +swaylock \ + --color 1d2021 \ + --inside-color 282828 \ + --inside-clear-color 458588 \ + --inside-caps-lock-color fabd2f \ + --inside-ver-color d3869b \ + --inside-wrong-color fb4934 \ + --ring-color 3c3836 \ + --ring-clear-color 689d6a \ + --ring-caps-lock-color fe8019 \ + --ring-ver-color b16286 \ + --ring-wrong-color cc241d \ + --line-color 00000000 \ + --line-clear-color 00000000 \ + --line-caps-lock-color 00000000 \ + --line-ver-color 00000000 \ + --line-wrong-color 00000000 \ + --text-color ebdbb2 \ + --text-clear-color 1d2021 \ + --text-caps-lock-color 1d2021 \ + --text-ver-color 1d2021 \ + --text-wrong-color 1d2021 \ + --key-hl-color b8bb26 \ + --bs-hl-color fb4934 \ + --caps-lock-key-hl-color fe8019 \ + --caps-lock-bs-hl-color fb4934 \ + --separator-color 00000000 \ + --indicator-radius 100 \ + --indicator-thickness 7 \ + --indicator-idle-visible \ + --font "BlexMono Nerd Font Mono" \ + --font-size 24 \ + --show-failed-attempts \ + --show-keyboard-layout