Init commit

This commit is contained in:
Archie Fox
2025-06-08 06:59:19 +03:00
commit 06b8ee8caf
148 changed files with 18659 additions and 0 deletions

21
bin/battery Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python
import subprocess
def main():
with open("/sys/class/power_supply/BAT0/capacity", "r") as f:
capacity = f.read()
# capacity = 10
if int(capacity) <= 20:
text = "Внимание, низкий уровень заряда батареи! Подключите зарядное устройство"
subprocess.run(["notify-send", "-u", "critical", text])
subprocess.run(["rhvoice_say", text])
elif int(capacity) >= 80 and int(capacity) < 100:
text = "Батарея достаточно заряжена, можно отключать!"
subprocess.run(["notify-send", "-t", "15000", text])
subprocess.run(["rhvoice_say", text])
main()

3
bin/clipboard.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cliphist list | rofi -theme archer -font "Iosevka Nerd Font Medium 18" -dmenu -p "Clipboard" | cliphist decode | wl-copy

279
bin/clockv2 Executable file
View File

@@ -0,0 +1,279 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Работа со временем.
import subprocess
from datetime import datetime
import requests
# noqa
minutes = [
"ноль минут",
"одна минута",
"две минуты",
"три минуты",
"четыре минуты",
"пять минут",
"шесть минут",
"семь минут",
"восемь минут",
"девять минут",
"десять минут",
"одиннадцать минут",
"двенадцать минут",
"тринадцать минут",
"четырнадцать минут",
"пятнадцать минут",
"шестнадцать минут",
"семнадцать минут",
"восемнадцать минут",
"девятнадцать минут",
"двадцать минут",
"двадцать одна минута",
"двадцать две минуты",
"двадцать три минуты",
"двадцать четыре минуты",
"двадцать пять минут",
"двадцать шесть минут",
"двадцать семь минут",
"двадцать восемь минут",
"двадцать девять минут",
"тридцать минут",
"тридцать одна минута",
"тридцать две минуты",
"тридцать три минуты",
"тридцать четыре минуты",
"тридцать пять минут",
"тридцать шесть минут",
"тридцать семь минут",
"тридцать восемь минут",
"тридцать девять минут",
"сорок минут",
"сорок одна минута",
"сорок две минуты",
"сорок три минуты",
"сорок четыре минуты",
"сорок пять минут",
"сорок шесть минут",
"сорок семь минут",
"сорок восемь минут",
"сорок девять минут",
"пятьдесят минут",
"пятьдесят одна минута",
"пятьдесят две минуты",
"пятьдесят три минуты",
"пятьдесят четыре минуты",
"пятьдесят пять минут",
"пятьдесят шесть минут",
"пятьдесят семь минут",
"пятьдесят восемь минут",
"пятьдесят девять минут",
]
clocks = [
"ноль часов",
"один час",
"два часа",
"три часа",
"четыре часа",
"пять часов",
"шесть часов",
"семь часов",
"восемь часов",
"девять часов",
"десять часов",
"одиннадцать часов",
"двенадцать часов",
"тринадцать часов",
"четы*рнадцать часов",
"пятнадцать часов",
"шестнадцать часов",
"семнадцать часов",
"восемнадцать часов",
"девятнадцать часов",
"двадцать часов",
"двадцать один час",
"двадцать два часа",
"двадцать три часа",
]
days = [
"ноль",
"первое",
"второе",
"третье",
"четвертое",
"пятое",
"шестое",
"седьмое",
"восьмое",
"девятое",
"десятое",
"одиннадцатое",
"двенадцатое",
"тринадцатое",
"четырнадцатое",
"пятнадцатое",
"шестнадцатое",
"семнадцатое",
"восемнадцатое",
"девятнадцатое",
"двадцатое",
"двадцать первое",
"двадцать второе",
"двадцать третье",
"двадцать четвертое",
"двадцать пятое",
"двадцать шестое",
"двадцать седьмое",
"двадцать восьмое",
"двадцать девятое",
"тридцатое",
"тридцать первое",
]
w_day = [
"понед*ельник",
"вт*орник",
"сред*а",
"четв*ерг",
"п*ятница",
"субб*ота",
"воскрес*енье",
]
month_dic = {
1: "январ*я",
2: "феврал*я",
3: "м*арта",
4: "апр*еля",
5: "м*ая",
6: "и*юня",
7: "и*юля",
8: "*августа",
9: "сентябр*я",
10: "октябр*я",
11: "ноябр*я",
12: "декабр*я",
}
def get_date():
"""
Получение текущего времени.
В формате: "HH часов YY минут"
"""
now_time = datetime.now() # текущая дата со временем
week_day = datetime.today().weekday()
hour = clocks[now_time.hour]
minute = minutes[now_time.minute]
day = now_time.day
month = now_time.month
say_date = (
hour
+ " "
+ minute
+ ", "
+ w_day[week_day]
+ ", "
+ days[day]
+ " "
+ month_dic.get(month)
+ ", "
)
return say_date
def inflect(n: object, form1: object, form2: object, form5: object) -> object:
"""
Приведение в соответствие слова в зависимости от числа.
Например: inflect(18,'градус','градуса','градусов') -> 'градусов'
"""
n = abs(n) # убираем минус, если есть
n = n % 100 # две последних цифры
n1 = n % 10 # последняя цифра
if 10 < n < 20:
form = form5
elif 1 < n1 < 5:
form = form2
elif n1 == 1:
form = form1
else:
form = form5
return form
def temperature(n):
if n < 0:
f = "мор*оза"
elif n >= 1:
f = "тепл*а"
else:
f = ""
return f
with open("/sys/class/power_supply/BAT0/capacity", "r") as f:
capacity = f.read()
with open("/sys/class/power_supply/BAT0/cycle_count", "r") as f:
cycle = f.read()
with open("/sys/class/power_supply/BAT0/status", "r") as f:
status = f.read()
if int(capacity) % 10 == 1:
percent = "процент"
elif int(capacity) % 10 == 2 or int(capacity) % 10 == 3 or int(capacity) % 10 == 4:
percent = "процента"
else:
percent = "процентов"
if status.strip() == "Discharging":
bstat = "Разряжается"
else:
bstat = "Заряжается"
def main():
str_time = get_date()
weather_url = (
"http://api.openweathermap.org/data/2.5/"
"weather?id=472757&lang=ru"
"&appid=6f9d925282c57d29a95977d346e2d528&units=metric"
)
# 539221
r = requests.get(weather_url).text.split(sep=",")
hum = r[4].split(sep=":")[1]
t = r[7].split(sep=":")[2]
temp = round(float(t))
temp_f = temperature(temp)
lc_weather = inflect(temp, "градус", "градуса", "градусов")
weather_string = (
# " Сейч*ас в стан*ице Кум*ылженской" + str(temp) + lc_weather + temp_f + hum
" Сейч*ас в Волгогр*аде"
+ str(temp)
+ lc_weather
+ temp_f
+ hum
)
battery = f" Заряд батареи - {capacity} {percent}. {bstat}"
a = "Текущее Время " + str_time + weather_string + battery
return subprocess.run(["rhvoice_say", a])
try:
main()
except KeyboardInterrupt:
print("\rПрервано пользователем!")

86
bin/conv Executable file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env lua
local lfs = require("lfs")
local function file_rename(file)
local patterns = "[%(%)[%]{}%s]"
local new_name = string.gsub(file, patterns, "_")
os.rename(file, new_name)
os.rename(new_name, string.gsub(new_name, "mkv.", ""))
os.rename(new_name, string.gsub(new_name, "webm.", ""))
if file ~= new_name then
print("Файл " .. file .. " был переименован ==> " .. new_name)
end
end
-- -----------------------------------------------
-- Скачивание файлов
-- -----------------------------------------------
local function downloads(url)
local tempdir = "temp"
lfs.mkdir(tempdir)
lfs.chdir(tempdir)
print("Качаем видео...")
os.execute("yt-dlp -o '%(title)s.%(ext)s' " .. url)
print("Качаем перевод...")
os.execute("vot-cli " .. url .. " --output .")
local video_t = ""
local audio_t = ""
for f in lfs.dir(".") do
local mode = lfs.attributes("./" .. f, "mode")
local ext = f:match("[^.]+$")
if mode == "file" and (ext == "webm" or ext == "mkv" or ext == "mp4") then
video_t = f
elseif mode == "file" and ext == "mp3" then
audio_t = f
else
print("Файлы не найдены...")
end
end
print("Конвертируем...")
os.execute(
'ffmpeg -i "'
.. video_t
.. '" -i '
.. audio_t
.. ' -c:v copy -filter_complex "[0:a] volume=0.15 [original]; [original][1:a] amix=inputs=2:duration=longest [audio_out]" -map 0:v -map "[audio_out]" -y ../'
.. '"'
.. video_t
.. '"'
)
print("Удаляется файл ==> ", video_t)
os.remove(video_t)
print("Удаляется файл ==> ", audio_t)
os.remove(audio_t)
lfs.chdir("../")
print("Удаляется директория ==> " .. tempdir)
lfs.rmdir(tempdir)
end
-- Открываем файл для чтения
local file = io.open(arg[1], "r")
if file then
-- Читаем и выводим построчно содержимое файла
for line in file:lines() do
downloads(line)
end
-- Закрываем файл
file:close()
else
print("Не удалось открыть файл")
end
for f in lfs.dir(".") do
local mode = lfs.attributes("./" .. f, "mode")
if mode == "file" then
file_rename(f)
end
end

103
bin/daily_backup_hardlink.sh Executable file
View File

@@ -0,0 +1,103 @@
#!/bin/bash
export DISPLAY=:1
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
# === НАСТРОЙКИ ===
# Папка-источник, откуда копируем
SRC="$HOME"
# Корневая папка для хранения бэкапов
BACKUP_ROOT="$HOME/Share/backup/lap"
# Только эти подпапки будут участвовать в бэкапе (относительно SRC)
INCLUDE_DIRS=(
".config/nvim"
".config/qtile"
".config/fish"
".config/kitty"
".config/gtk-3.0"
".config/gtk-4.0"
".config/mpv"
".config/rofi"
".tmux"
)
# Дата сегодняшнего бэкапа
TODAY=$(date +%Y%m%d-%H%M%S)
TODAY_DIR="$BACKUP_ROOT/$TODAY"
LATEST_LINK="$BACKUP_ROOT/LATEST"
# === СОЗДАНИЕ ПАПКИ ДЛЯ БЭКАПА ===
mkdir -p "$TODAY_DIR"
echo "📦 Начало бэкапа: $TODAY_DIR из $SRC"
# === ЕСЛИ ЕСТЬ LATEST, СРАВНИВАЕМ С НИМ ===
if [[ -L "$LATEST_LINK" && -d "$LATEST_LINK" ]]; then
PREV_DIR=$(readlink -f "$LATEST_LINK")
echo "🔁 Сравнение с предыдущим бэкапом: $PREV_DIR"
for rel_dir in "${INCLUDE_DIRS[@]}"; do
# Найдём все файлы внутри указанной папки
find "$SRC/$rel_dir" -type f | while read -r src_file; do
rel_path="${src_file#$SRC/}" # путь относительно SRC
prev_file="$PREV_DIR/$rel_path" # путь в предыдущем бэкапе
dest_file="$TODAY_DIR/$rel_path" # путь в новом бэкапе
dest_dir=$(dirname "$dest_file")
mkdir -p "$dest_dir"
if [[ -f "$prev_file" ]]; then
# сравнение хешей
hash_src=$(sha256sum "$src_file" | awk '{print $1}')
hash_prev=$(sha256sum "$prev_file" | awk '{print $1}')
if [[ "$hash_src" == "$hash_prev" ]]; then
ln "$prev_file" "$dest_file"
echo "🔗 Жёсткая ссылка: $rel_path" >/dev/null
else
cp "$src_file" "$dest_file"
echo "📄 Изменён → копия: $rel_path"
fi
else
cp "$src_file" "$dest_file"
echo "📄 Новый → копия: $rel_path"
fi
done
done
else
echo "🚨 LATEST не найден. Полный бэкап."
for rel_dir in "${INCLUDE_DIRS[@]}"; do
if [[ -d "$SRC/$rel_dir" ]]; then
mkdir -p "$TODAY_DIR/$(dirname "$rel_dir")"
cp -a "$SRC/$rel_dir" "$TODAY_DIR/$(dirname "$rel_dir")"
echo "✅ Скопировано: $rel_dir"
fi
done
fi
# === ОБНОВЛЯЕМ ССЫЛКУ НА LATEST ===
rm -f "$LATEST_LINK"
ln -s "$TODAY" "$LATEST_LINK"
echo "✅ Обновлена ссылка LATEST → $TODAY"
notify-send "✅ Завершили бекап → $TODAY"
# === УДАЛЯЕМ СТАРЫЕ БЭКАПЫ (оставляем один на каждый день по формату YYYYMMDD-HHMMSS) ===
echo "Удаляем лишние бэкапы"
cd "$BACKUP_ROOT" || exit 1
for day in $(ls -1 | grep -E '^[0-9]{8}-[0-9]{6}$' | cut -c1-8 | sort -u); do
keep=$(ls -1 | grep "^$day-" | sort | tail -n 1)
for snapshot in $(ls -1 | grep "^$day-" | grep -v "^$keep$"); do
echo "Удаляю $snapshot"
rm -rf "$snapshot"
done
done

5
bin/hibernate.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
sudo rtcwake -m mem -s 1800
sleep 3
sudo systemctl hibernate

71
bin/net_traf.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# network_traffic.sh NETWORK_INTERFACE [POLLING_INTERVAL]
iface=${1:-lo}
# # iface='wlp0s20f3'
isecs=${2:-1}
# `snore` adapted from https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever
# without MacOS workaround, TODO: with _snore_fd initialized separatedly, also i dont touch IFS so dont bother with it
snore() {
local IFS
[[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null
read ${1:+-t "$1"} -u $_snore_fd || :
}
# `human_readable` adapted from https://gist.github.com/cjsewell/96463db7fec6faeab291
human_readable() {
local value=$1
local units=(B K M G T P)
local index=0
while ((value > 1000 && index < 5)); do
((value /= 1000, index++))
done
echo "$value${units[$index]}"
}
# sanity checking, timing here is not an issue anymore -- TODO: check how waybar reacts to `exit 1`
test -n "${iface}" && grep -q "${iface}:" /proc/net/dev || {
printf '{"text": "%s"}\n' "${iface} not found"
exit 1
}
test "$isecs" -gt 0 || {
printf '{"text": "%s"}\n' "${isecs} not valid"
exit 1
}
# NOTE: `/proc/net/dev` format is:
# RX bytes packets errs drop fifo frame compressed multicast
# TX bytes packets errs drop fifo colls carrier compressed
declare -a traffic_prev traffic_curr traffic_delta
# NOTE: array items are:
# 0=rx_bytes 1=rx_packets 2=rx_errs 3=rx_drop
# 4=tx_bytes 5=tx_packets 6=tx_errs 7=tx_drop
# TODO: rearrange the loop, do not show bogus on first iteration
traffic_prev=(0 0 0 0 0 0 0 0)
while snore ${isecs}; do
traffic_curr=($(awk '/^ *'${iface}':/{print $2 " " $3 " " $4 " " $5 " " $10 " " $11 " " $12 " " $13}' /proc/net/dev))
for i in {0..7}; do
((traffic_delta[i] = (traffic_curr[i] - traffic_prev[i]) / isecs))
done
traffic_prev=(${traffic_curr[@]})
printf '{"text": "%5s⇣ %5s⇡"}\n' $(human_readable ${traffic_delta[0]}) $(human_readable ${traffic_delta[4]})
#printf '{"text": "%5s⇣ %5s⇡", "alt": "%s", "tooltip": "%s", "class": "%s", "percentage": %d }\n' $(human_readable ${traffic_delta[0]}) $(human_readable ${traffic_delta[4]}) '_alt' '_tooltip' '_class' 0
done
# TODO: handle errors
# TODO: aggregate interfaces (default to all from `ls /sys/class/net | grep -E '^(eth|wlan|enp|wlp)'`)
# TODO: tooltip with details per each interface
# TODO: colors (?)
# TODO: styling (in waybar .css, using {percent})
# TODO: unicode meter (" ","▁","▁","▂","▃","▄","▅","▆","▇","█")
# TODO: split rx/tx (?)
# TODO: test and optimize
# NOTE: in waybar config (do NOT use "interval"):
# "custom/network_traffic": {
# "exec": "~/.config/waybar/scripts/network_traffic.sh enp14s0",
# },

10
bin/powermenu Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
choice=$(echo -e "Lock\nLogout\nReboot\nShutdown\nSleep\nHibernate" | rofi -theme archer -font "Iosevka Nerd Font Medium 18" -dmenu -p "Power menu")
case "$choice" in
"Lock") betterlockscreen -l ;;
"Logout") pkill -u $USER ;;
"Reboot") doas systemctl reboot ;;
"Shutdown") doas systemctl poweroff ;;
"Sleep") doas systemctl suspend ;;
"Hibernate") doas systemctl hibernate ;;
esac

19
bin/rename_v3.lua Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env lua
local lfs = require("lfs")
local function file_rename(file)
local patterns = "[%(%)[%]{}%s]"
local new_name = string.gsub(file, patterns, "_")
os.rename(file, new_name)
if file ~= new_name then
print("Файл " .. file .. " был переименован ==> " .. new_name)
end
end
for f in lfs.dir(".") do
local mode = lfs.attributes("./" .. f, "mode")
if mode == "file" then
file_rename(f)
end
end

74
bin/rsync.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/bin/bash
export DISPLAY=:1
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
SRC="/home/fox/Share"
DST="fox@archevod.netcraze.pro:/home/fox/"
DST_LOCAL="fox@seeed:/home/fox/"
LOCKFILE="/home/fox/bin/rsync_watch.lock"
LOGFILE="/home/fox/bin/log/rsync_watch.log"
NOTIFY="notify-send"
IP=$(ip -4 addr show wlan0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
TARGET_IP="192.168.1.3"
# Проверяем размер файла в байтах
FILESIZE=$(stat -c%s "$LOGFILE")
# 100 мегабайт в байтах
LIMIT=$((100 * 1024 * 1024))
log() {
echo -e "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOGFILE"
}
if [ "$FILESIZE" -gt "$LIMIT" ]; then
: >"$LOGFILE" # Очищаем файл (альтернативно можно использовать > "$FILE")
log "Файл был очищен \"$(date)\""
log ""
fi
# Проверка наличия inotifywait
if ! command -v inotifywait &>/dev/null; then
log "Ошибка: inotifywait не установлен. Установите inotify-tools."
exit 1
fi
if [[ "$IP" == "$TARGET_IP" ]]; then
log "Старт наблюдения за $SRC\n Цель: $DST_LOCAL\n"
else
log "Старт наблюдения за $SRC\n Цель: $DST\n"
fi
while true; do
inotifywait -r -e modify,create,delete,move "$SRC" >/dev/null 2>&1
# Проверка на наличие блокировки
if [ -f "$LOCKFILE" ] && kill -0 "$(cat "$LOCKFILE")" 2>/dev/null; then
log "Пропущено: rsync уже запущен (PID $(cat "$LOCKFILE"))"
continue
fi
# Установка блокировки
echo $$ >"$LOCKFILE"
sleep 20
SECONDS=0
$NOTIFY "🔁 Начата синхронизация..."
log "----------------------------------------"
log "Изменения обнаружены. Запуск rsync..."
log "Local IP address = $IP"
if [[ "$IP" == "$TARGET_IP" ]]; then
rsync -auvhH --delete "$SRC" "$DST_LOCAL" >>"$LOGFILE" 2>&1
else
rsync -auvhH -e "ssh -p 3022" --delete "$SRC" "$DST" >>"$LOGFILE" 2>&1
fi
log "Синхронизация завершена за $SECONDS сек."
log "----------------------------------------\n"
$NOTIFY "🔁 Синхронизация завершена за $SECONDS сек."
# Удаление блокировки
rm -f "$LOCKFILE"
done

36
bin/test_ip.lua Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env lua
local http = require("socket.http")
local ltn12 = require("ltn12")
-- Функция для получения внешнего IP-адреса
function get_external_ip()
local services = {
"https://api.ipify.org",
"https://ident.me",
"https://ifconfig.me/ip",
}
for _, url in ipairs(services) do
local response = {}
local _, status = http.request({
url = url,
sink = ltn12.sink.table(response),
})
if status == 200 then
return table.concat(response)
end
end
return nil, "Не удалось получить IP-адрес"
end
-- Основная часть скрипта
local ip, err = get_external_ip()
if ip then
print(ip)
else
print("Ошибка: " .. err)
end

5
bin/waybar_clock.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
date=$(date '+%a %d %b | %H:%M |')
echo $date

5
bin/wttr.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
wttr=$(curl 'https://wttr.in/Kumylzhenskaya?format=1')
echo $wttr