This commit is contained in:
2025-01-01 00:00:00 +09:00
commit ee8494370d
12 changed files with 1070 additions and 0 deletions

152
BLEMouse.ino Normal file
View File

@@ -0,0 +1,152 @@
#include "BLEDevice.h"
#include "BLEHIDDevice.h"
#include "HIDTypes.h"
#define DEVICE_NAME "ESP32 Mouse"
#define BLE_MANUFACTURER "TinyUSB"
BLEHIDDevice* hid;
BLECharacteristic* input;
BLECharacteristic* output;
bool isBleConnected = false;
#define MOUSE_LEFT 1
#define MOUSE_RIGHT 2
#define MOUSE_MIDDLE 4
struct InputReport {
uint8_t buttons;
int8_t x;
int8_t y;
int8_t w;
int8_t hw;
};
void setup() {
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, 22, 23);
xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL);
}
void loop() {
while (Serial2.available()) {
String uartData = Serial2.readStringUntil('\n');
if (uartData.startsWith("REPORT: ")) {
int r, m, l, w, x, y;
sscanf(uartData.c_str(), "REPORT: BTN %d %d %d WHEEL %d X %d Y %d", &l, &m, &r, &w, &x, &y);
uint8_t button = 0;
if (r) button |= MOUSE_RIGHT;
else button &= ~MOUSE_RIGHT;
if (m) button |= MOUSE_MIDDLE;
else button &= ~MOUSE_MIDDLE;
if (l) button |= MOUSE_LEFT;
else button &= ~MOUSE_LEFT;
InputReport report = {
.buttons = button,
.x = x,
.y = y,
.w = w,
.hw = 0
};
if (isBleConnected) {
input->setValue((uint8_t*)&report, sizeof(report));
input->notify();
}
}
// Serial.println(uartData);
}
}
static const uint8_t _hidReportDescriptor[] = {
USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop)
USAGE(1), 0x02, // USAGE (Mouse)
COLLECTION(1), 0x01, // COLLECTION (Application)
USAGE(1), 0x01, // USAGE (Pointer)
COLLECTION(1), 0x00, // COLLECTION (Physical)
// ------------------------------------------------- Buttons (Left, Right, Middle, Back, Forward)
USAGE_PAGE(1), 0x09, // USAGE_PAGE (Button)
USAGE_MINIMUM(1), 0x01, // USAGE_MINIMUM (Button 1)
USAGE_MAXIMUM(1), 0x05, // USAGE_MAXIMUM (Button 5)
LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM(1), 0x01, // LOGICAL_MAXIMUM (1)
REPORT_SIZE(1), 0x01, // REPORT_SIZE (1)
REPORT_COUNT(1), 0x05, // REPORT_COUNT (5)
HIDINPUT(1), 0x02, // INPUT (Data, Variable, Absolute) ;5 button bits
// ------------------------------------------------- Padding
REPORT_SIZE(1), 0x03, // REPORT_SIZE (3)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1)
HIDINPUT(1), 0x03, // INPUT (Constant, Variable, Absolute) ;3 bit padding
// ------------------------------------------------- X/Y position, Wheel
USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop)
USAGE(1), 0x30, // USAGE (X)
USAGE(1), 0x31, // USAGE (Y)
USAGE(1), 0x38, // USAGE (Wheel)
LOGICAL_MINIMUM(1), 0x81, // LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM(1), 0x7f, // LOGICAL_MAXIMUM (127)
REPORT_SIZE(1), 0x08, // REPORT_SIZE (8)
REPORT_COUNT(1), 0x03, // REPORT_COUNT (3)
HIDINPUT(1), 0x06, // INPUT (Data, Variable, Relative) ;3 bytes (X,Y,Wheel)
// ------------------------------------------------- Horizontal wheel
USAGE_PAGE(1), 0x0c, // USAGE PAGE (Consumer Devices)
USAGE(2), 0x38, 0x02, // USAGE (AC Pan)
LOGICAL_MINIMUM(1), 0x81, // LOGICAL_MINIMUM (-127)
LOGICAL_MAXIMUM(1), 0x7f, // LOGICAL_MAXIMUM (127)
REPORT_SIZE(1), 0x08, // REPORT_SIZE (8)
REPORT_COUNT(1), 0x01, // REPORT_COUNT (1)
HIDINPUT(1), 0x06, // INPUT (Data, Var, Rel)
END_COLLECTION(0), // END_COLLECTION
END_COLLECTION(0) // END_COLLECTION
};
class BleHIDCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* server) {
isBleConnected = true;
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(true);
Serial.println("CONNECTED");
}
void onDisconnect(BLEServer* server) {
isBleConnected = false;
BLE2902* cccDesc = (BLE2902*)input->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
cccDesc->setNotifications(false);
Serial.println("DISCONNECTED");
}
};
void bluetoothTask(void*) {
BLEDevice::init(DEVICE_NAME);
BLEServer* server = BLEDevice::createServer();
server->setCallbacks(new BleHIDCallbacks());
hid = new BLEHIDDevice(server);
input = hid->inputReport(0);
hid->manufacturer()->setValue(BLE_MANUFACTURER);
hid->pnp(0x02, 0xe502, 0xa111, 0x0210);
hid->hidInfo(0x00, 0x02);
BLESecurity* security = new BLESecurity();
security->setAuthenticationMode(ESP_LE_AUTH_BOND);
hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor));
hid->startServices();
hid->setBatteryLevel(100);
BLEAdvertising* advertising = server->getAdvertising();
advertising->setAppearance(HID_MOUSE);
advertising->addServiceUUID(hid->hidService()->getUUID());
advertising->addServiceUUID(hid->deviceInfo()->getUUID());
advertising->addServiceUUID(hid->batteryService()->getUUID());
advertising->start();
Serial.println("BLE READY");
delay(portMAX_DELAY);
};

282
anthropic.py Normal file
View File

@@ -0,0 +1,282 @@
#!/usr/bin/env python3
import json
import re
import time
import httpx
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import Response, StreamingResponse, JSONResponse
from starlette.middleware.cors import CORSMiddleware
CLAUDE_BASE_URL = "https://api.anthropic.com/v1/messages"
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=True,
)
def get_api_key(headers: dict) -> str:
auth = headers.get("authorization")
if auth:
parts = auth.split(" ")
if len(parts) > 1:
return parts[1]
return
def format_stream_response_json(claude_response: dict) -> dict:
typ = claude_response.get("type")
if typ == "message_start":
return {
"id": claude_response["message"]["id"],
"model": claude_response["message"]["model"],
"inputTokens": claude_response["message"]["usage"]["input_tokens"],
}
elif typ in ("content_block_start", "ping", "content_block_stop", "message_stop"):
return None
elif typ == "content_block_delta":
return {"content": claude_response["delta"]["text"]}
elif typ == "message_delta":
return {
"stopReason": claude_response["delta"].get("stop_reason"),
"outputTokens": claude_response["usage"]["output_tokens"],
}
elif typ == "error":
return {
"errorType": claude_response["error"].get("type"),
"errorMsg": claude_response["error"]["message"],
}
else:
return None
def claude_to_chatgpt_response(claude_response: dict, meta_info: dict, stream: bool = False) -> dict:
timestamp = int(time.time())
completion_tokens = meta_info.get("outputTokens", 0) or 0
prompt_tokens = meta_info.get("inputTokens", 0) or 0
if meta_info.get("stopReason") and stream:
return {
"id": meta_info.get("id"),
"object": "chat.completion.chunk",
"created": timestamp,
"model": meta_info.get("model"),
"choices": [
{
"index": 0,
"delta": {},
"logprobs": None,
"finish_reason": "stop",
}
],
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
},
}
message_content = claude_response.get("content", "")
result = {
"id": meta_info.get("id", "unknown"),
"created": timestamp,
"model": meta_info.get("model"),
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
},
"choices": [{"index": 0}],
}
message = {"role": "assistant", "content": message_content}
if not stream:
result["object"] = "chat.completion"
result["choices"][0]["message"] = message
result["choices"][0]["finish_reason"] = "stop" if meta_info.get("stopReason") == "end_turn" else None
else:
result["object"] = "chat.completion.chunk"
result["choices"][0]["delta"] = message
return result
async def stream_generator(response: httpx.Response, model: str):
meta_info = {"model": model}
buffer = ""
regex = re.compile(r"event:\s*.*?\s*\ndata:\s*(.*?)(?=\n\n|\s*$)", re.DOTALL)
async for chunk in response.aiter_text():
buffer += chunk
for match in regex.finditer(buffer):
try:
decoded_line = json.loads(match.group(1).strip())
except Exception:
continue
formated_chunk = format_stream_response_json(decoded_line)
if formated_chunk is None:
continue
if formated_chunk.get("errorType", None):
etyp = formated_chunk.get("errorType")
emsg = formated_chunk.get("errorMsg")
data = {"error": {"type": etyp, "code": etyp, "message": emsg, "param": None}}
yield f"data: {json.dumps(data)}\n\n"
meta_info["id"] = formated_chunk.get("id", meta_info.get("id"))
meta_info["model"] = formated_chunk.get("model", meta_info.get("model"))
meta_info["inputTokens"] = formated_chunk.get("inputTokens", meta_info.get("inputTokens"))
meta_info["outputTokens"] = formated_chunk.get("outputTokens", meta_info.get("outputTokens"))
meta_info["stopReason"] = formated_chunk.get("stopReason", meta_info.get("stopReason"))
transformed_line = claude_to_chatgpt_response(formated_chunk, meta_info, stream=True)
yield f"data: {json.dumps(transformed_line)}\n\n"
else:
try:
resp = json.loads(buffer)
etyp = resp["error"]["type"]
emsg = resp["error"]["message"]
data = {"error": {"type": etyp, "code": etyp, "message": emsg, "param": None}}
yield f"data: {json.dumps(data)}\n\n"
except Exception:
pass
last_end = 0
for m in regex.finditer(buffer):
last_end = m.end()
buffer = buffer[last_end:]
yield "data: [DONE]"
@app.get("/v1/models")
async def get_models(request: Request):
headers = dict(request.headers)
api_key = get_api_key(headers)
if not api_key:
raise HTTPException(status_code=403, detail="Not Allowed")
async with httpx.AsyncClient() as client:
anthro_resp = await client.get(
"https://api.anthropic.com/v1/models",
headers={
"x-api-key": api_key,
"anthropic-version": "2023-06-01",
},
)
if anthro_resp.status_code != 200:
raise HTTPException(status_code=anthro_resp.status_code, detail="Error getting models")
data = anthro_resp.json()
models_list = [
{"id": m["id"], "object": m["type"], "owned_by": "Anthropic"}
for m in data.get("data", [])
]
return JSONResponse(content={"object": "list", "data": models_list})
@app.post("/v1/chat/completions")
async def chat_completions(request: Request):
headers = dict(request.headers)
api_key = get_api_key(headers)
if not api_key:
raise HTTPException(status_code=403, detail="Not Allowed")
try:
body = await request.json()
except Exception:
raise HTTPException(status_code=400, detail="Invalid JSON payload")
model = body.get("model")
messages = body.get("messages", [])
temperature = body.get("n", 1)
max_tokens = body.get("max_tokens", 4096)
stop = body.get("stop")
stream = body.get("stream", False)
system_message = next((m for m in messages if m.get("role") == "system"), [])
filtered_messages = [m for m in messages if m.get("role") != "system"]
claude_request_body = {
"model": model,
"messages": filtered_messages,
"temperature": temperature,
"max_tokens": max_tokens,
"stop_sequences": stop,
"system": system_message.get("content") if system_message else [],
"stream": stream,
}
if not stream:
async with httpx.AsyncClient(timeout=None) as client:
claude_resp = await client.post(
CLAUDE_BASE_URL,
headers={
"Content-Type": "application/json",
"x-api-key": api_key,
"anthropic-version": "2023-06-01",
},
json=claude_request_body,
)
if claude_resp.status_code != 200:
print(claude_resp.content)
return Response(status_code=claude_resp.status_code, content=claude_resp.content)
resp_json = claude_resp.json()
if resp_json.get("type") == "error":
result = {
"error": {
"message": resp_json.get("error", {}).get("message"),
"type": resp_json.get("error", {}).get("type"),
"param": None,
"code": resp_json.get("error", {}).get("type"),
}
}
else:
formated_info = {
"id": resp_json.get("id"),
"model": resp_json.get("model"),
"inputTokens": resp_json.get("usage", {}).get("input_tokens"),
"outputTokens": resp_json.get("usage", {}).get("output_tokens"),
"stopReason": resp_json.get("stop_reason"),
}
content = ""
try:
content = resp_json.get("content", [])[0].get("text", "")
except Exception:
pass
result = claude_to_chatgpt_response({"content": content}, formated_info)
return JSONResponse(
status_code=claude_resp.status_code,
content=result
)
else:
async def stream_response(model: str, claude_request_body: dict, api_key: str):
async with httpx.AsyncClient(timeout=None) as client:
async with client.stream(
"POST",
CLAUDE_BASE_URL,
headers={
"Content-Type": "application/json",
"x-api-key": api_key,
"anthropic-version": "2023-06-01",
},
json=claude_request_body,
) as response:
async for event in stream_generator(response, model):
yield event
return StreamingResponse(
stream_response(model, claude_request_body, api_key),
media_type = "text/event-stream"
)

77
cfddns.py Normal file
View File

@@ -0,0 +1,77 @@
import requests
import socket
import sys
import os
CF_GLOBAL_KEY = os.environ.get("CF_GLOBAL_KEY")
CF_EMAIL = os.environ.get("CF_EMAIL")
auth = {"X-Auth-Email": CF_EMAIL, "X-Auth-Key": CF_GLOBAL_KEY, "Content-Type": "application/json"}
def update_dns(domain, new_ip):
for i in list_zones():
if domain.endswith(i[1]):
zone = i[0]
if not zone:
print("ERROR DOMAIN NOT FOUND")
if create_domain(zone, domain, new_ip):
print(f"RECORD {domain} -> {new_ip} CREATED")
return
list_api = f"https://api.cloudflare.com/client/v4/zones/{zone}/dns_records?name="
edit_api = f"https://api.cloudflare.com/client/v4/zones/{zone}/dns_records"
dns_info = {"type": "A", "name": domain, "ttl": 3600, "proxied": False}
try:
old_ip = socket.gethostbyname(dns_info["name"])
except socket.gaierror:
old_ip = None
if old_ip == new_ip:
print(f"RECORD {dns_info['name']} = {old_ip} NOT CHANGED")
else:
dns_id = requests.get(f"{list_api}{domain}", headers=auth).json()["result"][0]["id"]
dns_info["content"] = new_ip
upd = requests.put(f"{edit_api}/{dns_id}", headers=auth, json=dns_info).json()
dn_name = upd["result"]["name"]
dn_type = upd["result"]["type"]
dn_content = upd["result"]["content"]
print(f"RECORD {dn_name} {dn_type} {dn_content} UPDATED")
def list_zones():
api_url = "https://api.cloudflare.com/client/v4/zones"
resp = requests.get(api_url, headers=auth).json()
return [(z["id"], z["name"]) for z in resp["result"]]
def list_domains(zid):
api_url = f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records"
resp = requests.get(api_url, headers=auth).json()
return resp["result"]
def create_domain(zone, domain, ip=None):
existing = [i["name"] for i in list_domains(zone)]
if domain not in existing:
create_record(domain, zone, ip)
return True
else:
return False
def create_record(sub, zid, ip="1.1.1.1", record="A"):
api_url = f"https://api.cloudflare.com/client/v4/zones/{zid}/dns_records"
data = {"content": ip, "name": sub, "proxied": False, "type": record, "comment": "", "ttl": 3600}
res_txt = requests.post(api_url, headers=auth, json=data).text
print(res_txt)
return res_txt
if __name__ == "__main__":
TARGET_DOMAIN = sys.argv[1]
TARGET_ADDR = requests.get("https://ping.api.morgan.kr").json()["info"]["client"]
print(f"UPDATE {TARGET_DOMAIN} <- {TARGET_ADDR}")
update_dns(TARGET_DOMAIN, TARGET_ADDR)

43
desktop-file-list Normal file
View File

@@ -0,0 +1,43 @@
#!/bin/bash
parse_desktop_file() {
local file="$1"
local section=""
local mainexec=""
local mainname=""
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^\;.* || "$line" =~ ^#.* || -z "$line" ]]; then
continue
fi
if [[ "$line" =~ ^\[.*\]$ ]]; then
section="${line:1:-1}"
continue
fi
if [[ "$line" =~ ^([^=]+)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
value="${BASH_REMATCH[2]}"
# echo "[$section] $key = $value"
if [ "$section" == "Desktop Entry" ]; then
if [ "$key" == "Exec" ]; then mainexec=$value; fi
if [ "$key" == "Name" ]; then mainname=$value; fi
fi
fi
done < "$file"
echo "{\"file\": \"$file\", \"name\": \"$mainname\", \"exec\": \"$mainexec\"}"
}
shopt -s nullglob
IFS=':' read -r -a paths <<< "$XDG_DATA_DIRS:/home/$USER/.local/share/"
for path in "${paths[@]}"; do
AP=$(realpath "$path/applications")
for appfile in $AP/*.desktop; do
parse_desktop_file $appfile;
done
done
shopt -u nullglob

108
en_XX Normal file
View File

@@ -0,0 +1,108 @@
comment_char %
escape_char /
LC_IDENTIFICATION
title "English International"
source ""
address ""
contact ""
email ""
tel ""
fax ""
language ""
territory ""
revision ""
date ""
category "i18n:2012";LC_IDENTIFICATION
category "i18n:2012";LC_CTYPE
category "i18n:2012";LC_COLLATE
category "i18n:2012";LC_TIME
category "i18n:2012";LC_NUMERIC
category "i18n:2012";LC_MONETARY
category "i18n:2012";LC_MESSAGES
category "i18n:2012";LC_PAPER
category "i18n:2012";LC_NAME
category "i18n:2012";LC_ADDRESS
category "i18n:2012";LC_TELEPHONE
category "i18n:2012";LC_MEASUREMENT
END LC_IDENTIFICATION
LC_CTYPE
copy "en_US"
END LC_CTYPE
LC_COLLATE
copy "iso14651_t1"
END LC_COLLATE
LC_MONETARY
int_curr_symbol ""
currency_symbol ""
mon_decimal_point ","
mon_thousands_sep " "
mon_grouping 3
positive_sign ""
negative_sign "-"
int_frac_digits 2
frac_digits 2
p_cs_precedes 1
p_sep_by_space 0
n_cs_precedes 1
n_sep_by_space 0
p_sign_posn 1
n_sign_posn 1
END LC_MONETARY
LC_NUMERIC
decimal_point ","
thousands_sep " "
grouping 3
END LC_NUMERIC
LC_TIME
abday "Sun";"Mon";"Tue";"Wed";"Thu";"Fri";"Sat"
day "Sunday";"Monday";"Tuesday";"Wednesday";"Thursday";/
"Friday";"Saturday"
abmon "Jan";"Feb";"Mar";"Apr";"May";"Jun";"Jul";"Aug";"Sep";/
"Oct";"Nov";"Dec"
mon "January";"February";"March";"April";"May";"June";"July";/
"August";"September";"October";"November";"December"
week 7;19971130;4
first_weekday 1
first_workday 2
d_t_fmt "%a %Y-%m-%d %H:%M:%S"
d_fmt "%Y-%m-%d"
t_fmt "%H:%M:%S"
t_fmt_ampm ""
am_pm "";""
date_fmt "%a %Y-%m-%d %H:%M:%S %Z"
END LC_TIME
LC_MESSAGES
yesexpr "^[yY]"
noexpr "^[nN]"
yesstr ""
nostr ""
END LC_MESSAGES
LC_PAPER
height 297
width 210
END LC_PAPER
LC_NAME
name_fmt "%p%t%g%t%m%t%f"
END LC_NAME
LC_ADDRESS
postal_fmt "%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N"
END LC_ADDRESS
LC_TELEPHONE
tel_int_fmt "+%c %a%t%l"
END LC_TELEPHONE
LC_MEASUREMENT
measurement 1
END LC_MEASUREMENT

79
extract Normal file
View File

@@ -0,0 +1,79 @@
#!/bin/bash
# Archive eXtractor
error() {
echo "Error: $1" >&2
}
run() {
echo "Debug: $@"
$@
}
if [ ! -f "$1" ]; then
error "No file provided"
exit 1
fi
BN="${1%.*}"
BASE="${1%.*}"
TRY=0
while [ -d "$BASE" ]; do
if [ $TRY -gt 10 ]; then
error "Already Exists (10)"
exit 1
fi
error "Already exists ($BASE)"
TRY=$((TRY + 1))
BASE="${BN}_${TRY}"
done
FILE_TYPE=$(file --mime-type -b "$1")
EXT="${1##*.}"
case "$FILE_TYPE" in
application/x-tar) FORMAT="tar" ;;
application/zip) FORMAT="zip" ;;
application/x-7z-compressed) FORMAT="7z" ;;
*)
case "$EXT" in
tar.gz|tar.bz2|tar.xz|tar.zst|tgz|tbz2|txz) FORMAT="tar" ;;
gz) FORMAT="gzip" ;;
bz2) FORMAT="bzip2" ;;
xz) FORMAT="xz" ;;
*)
error "Unsupported file format"
exit 1
;;
esac
;;
esac
run mkdir "$BASE"
case $FORMAT in
tar)
run tar -xvf "$1" -C "$BASE"
;;
zip)
run unzip "$1" -d "$BASE"
;;
7z)
run 7z x "$1" -o"$BASE"
;;
gzip)
run gunzip -c "$1" > "$BASE/$(basename "$BN")"
;;
bzip2)
run bunzip2 -c "$1" > "$BASE/$(basename "$BN")"
;;
xz)
run unxz -c "$1" > "$BASE/$(basename "$BN")"
;;
*)
error "Unexpected error occurred"
exit 1
;;
esac

68
fallback.py Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python
import uvicorn
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import RedirectResponse, FileResponse
import requests
import datetime
app = FastAPI()
hosts = [
"SERVER1",
"SERVER2",
"SERVER3",
"SERVER4"
]
cache = {}
def find_fit(parent):
print(cache)
hosts_local = hosts.copy()
if parent in cache.keys():
print(f"DEBUG: Cache Hit {parent}: {cache[parent]}")
idx = hosts.index(cache[parent])
if idx:
hosts_local[idx], hosts_local[0] = hosts_local[0], hosts_local[idx]
for host in hosts_local:
try:
print(f"DEBUG: Tesing for http://{host}/{parent}")
if requests.head(f"http://{host}/{parent}", allow_redirects=True).status_code == 200:
cache[parent] = host
return host
else:
cache[parent] = ""
except Exception:
pass
return None
def log(level, status_code, path, header):
print(f'{level}: [{datetime.datetime.now()}] "{status_code} {path} "{header.get("User-Agent")}"')
@app.get("/{path:path}")
async def index(request: Request, path: str):
if path in [None, "", "favicon.ico", "index.html"]:
if path in [None, ""]:
path = "index.html"
print(f'RETURN: [{datetime.datetime.now()}] "200 {path} "{request.headers.get("User-Agent")}"')
return FileResponse(path)
server = find_fit(path.split("/")[0])
if server:
print(f'RETURN: [{datetime.datetime.now()}] "301 http://{server}/{path}" "{request.headers.get("User-Agent")}"')
return RedirectResponse(url=f"http://{server}/{path}")
else:
print(f'RETURN: [{datetime.datetime.now()}] "404 Not found" "{request.headers.get("User-Agent")}"')
raise HTTPException(status_code=404, detail="No available server.")
if __name__=="__main__":
print('Starting Server...')
uvicorn.run(
"fallback:app",
host="0.0.0.0",
port=8080,
log_level="info",
reload=True,
)

48
hls_dl Normal file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
CURRENT=$(pwd)
WORKDIR=$(mktemp -d)
cd $WORKDIR
M3U8="$1"
NAME="$2"
curl "$M3U8" > PLAYLIST
touch MONITOR
{
cat PLAYLIST | \
grep -v '^#.*' | \
xargs -P 100 -I {} bash -c 'FN=$(echo "{}" | sha1sum | cut -d" " -f1); if [ ! -f $FN ]; then curl "{}" -s --output $FN; fi; echo .';
rm MONITOR;
} > MONITOR &
echo DOWNLOADING
TOTAL=$(cat PLAYLIST | grep -v '^#' | wc -l)
while [ -f MONITOR ]; do
echo $(cat MONITOR | wc -l)/$TOTAL
sleep 1;
done
wait
echo DONE
for i in $(<PLAYLIST); do
{ echo $i | grep -v '^#' > /dev/null; } \
&& {
FN=$(echo $i | sha1sum | cut -d" " -f1);
echo $FN;
} \
|| echo $i;
done > MODIFIED_PLAYLIST.m3u8
ffmpeg -allowed_extensions ALL -extension_picky 0 -i MODIFIED_PLAYLIST.m3u8 -c copy $CURRENT/$NAME
cd $CURRENT
rm -r $WORKDIR

8
hwplibre Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
bef=$1
aft=${bef%.*}.odt
zenity --info --text="Converting $bef to $aft.";
eval "~/.python38/bin/hwp5odt \"$bef\" --output \"$aft\""
eval "libreoffice --writer \"$aft\""

47
netwatch Normal file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
cleanup () {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch exited. ($$)" | tee -a /var/log/netwatch.log;
rm /var/.netwatch.lock
}
if [ -f /var/.netwatch.lock ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch already running. ($$)" | tee -a /var/log/netwatch.log;
exit;
fi
trap cleanup EXIT
FAIL=0
RELOADED=0
if [ ! -f /var/.netwatch.lock ]; then
touch /var/.netwatch.lock
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch started ($$)" | tee -a /var/log/netwatch.log;
while true; do
if [ $FAIL -gt 20 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch triggered ($$) - $FAIL" | tee -a /var/log/netwatch.log;
if [ $RELOADED == 1 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch triggered ($$) - $FAIL - HARD RESET" | tee -a /var/log/netwatch.log;
reboot;
fi
systemctl restart networking
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $(systemctl status networking; ip a;)" | tee -a /var/log/netwatch.log;
FAIL=0
RELOADED=1
fi
curl -s https://ping.api.morgan.kr/ >/dev/null && FAIL=0 || FAIL=$(($FAIL+1))
ping 1.1.1.1 -c 1 -w 1 >/dev/null && FAIL=0 || FAIL=$(($FAIL+1))
if [ $FAIL -ne 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Netwatch failed ($$) - $FAIL" | tee -a /var/log/netwatch.log;
else
RELOADED=0
fi
sleep 60
done
fi

131
pkgupdate Normal file
View File

@@ -0,0 +1,131 @@
#!/bin/bash
PKGS=()
update_librewolf () {
PKGNAME="librewolf"
TMPDIR="$(mktemp -d)"
PWD=$(pwd)
echo "[*] Updating $PKGNAME..."
echo "[*] - Entering $TMPDIR"
cd $TMPDIR
URL=$(curl -L https://gitlab.com/api/v4/projects/44042130/releases/permalink/latest | jq -r '.assets.links[] | select(.name | test("^librewolf-[0-9\\.-]+-linux-x86_64-package\\.tar\\.xz$")) | .url')
echo "[*] - Downloading from $URL"
curl "$URL" -o librewolf.tar.xz
echo "[*] - Extracting..."
sudo tar -xvf librewolf.tar.xz >> log
echo "[*] - Installing..."
sudo cp -r /opt/librewolf ./previous
sudo rsync -avx librewolf/ /opt/librewolf/ >> log
echo "[*] - Running post-script..."
sudo chown root:root /opt/librewolf/ /opt/librewolf/librewolf
echo "[*] - Done!"
cd $PWD
}
update_1password () {
PKGNAME="1Password"
TMPDIR="$(mktemp -d)"
PWD=$(pwd)
echo "[*] Updating $PKGNAME..."
echo "[*] - Entering $TMPDIR"
cd $TMPDIR
URL="https://downloads.1password.com/linux/tar/stable/x86_64/1password-latest.tar.gz"
echo "[*] - Downloading from $URL"
curl "$URL" -o 1password-latest.tar.gz
echo "[*] - Extracting..."
sudo tar -xvf 1password-latest.tar.gz >> log
echo "[*] - Installing..."
sudo cp -r /opt/1Password ./.previous
sudo rsync -avx 1password-*/ /opt/1Password/ >> log
echo "[*] - Running post-script..."
sudo /opt/1Password/after-install.sh >> log
echo "[*] - Done!"
cd $PWD
}
update_ungoogled_chromium () {
PKGNAME="ungoogled-chromium"
TMPDIR="$(mktemp -d)"
PWD=$(pwd)
PROJ_URL="https://api.github.com/repos/ungoogled-software/ungoogled-chromium-portablelinux/releases/latest"
URL=$(curl $PROJ_URL | jq -r '.assets.[] | select(.name | endswith(".tar.xz")) | .browser_download_url')
download () {
curl -L "$URL" -o package.tar.xz
}
extract () {
sudo tar -xvf package.tar.xz >> log
}
install () {
mv ./ungoogled-chromium_* ./NEW
sudo mv /opt/ungoogled-chromium/ ./PREV/
sudo mv ./NEW /opt/ungoogled-chromium/ | tee log
}
postscript () {
sudo chown root:root /opt/ungoogled-chromium/ /opt/ungoogled-chromium/chrome
sudo bash -c 'bash <(curl https://raw.githubusercontent.com/morgan9e/chrome-blank-newtab/refs/heads/main/patch.sh) /opt/ungoogled-chromium/resources.pak'
}
echo "[*] Updating $PKGNAME..."
echo "[*] - Entering $TMPDIR"
cd $TMPDIR
echo "[*] - Downloading from $URL"
download
echo "[*] - Extracting..."
extract
echo "[*] - Installing..."
install
echo "[*] - Running post-script..."
postscript
echo "[*] - Done!"
cd $PWD
}
PKGS+=("librewolf")
PKGS+=("1password")
PKGS+=("ungoogled_chromium")
# main
if [[ "$1" == "list" ]]; then
echo "${PKGS[*]}"
exit 0
elif [[ -z "$1" ]]; then
echo "Usage: $(basename $0) [package_name | all | list]"
echo "Available: ${PKGS[*]}"
exit 1
fi
if [ "$EUID" -ne 0 ]; then
sudo "$0" "$@"
exit $?
fi
if [[ "$1" == "all" ]]; then
for pkg in "${PKGS[@]}"; do
"update_$pkg"
done
elif [[ -n "$1" ]]; then
"update_$1"
fi
exit 0

27
snap Normal file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]
then echo "Please run as root."
exit
fi
TIME=$(date +%Y_%m_%d-%H_%M)
echo $TIME > /SNAPSHOT
echo $TIME > /home/SNAPSHOT
mkdir /tmp/btrfsnap-$TIME
mount $(findmnt / -no SOURCE | cut -d '[' -f 1) /tmp/btrfsnap-$TIME
btrfs subvol snap -r /tmp/btrfsnap-$TIME/@root /tmp/btrfsnap-$TIME/@snapshots/@root-$TIME
btrfs subvol snap -r /tmp/btrfsnap-$TIME/@home /tmp/btrfsnap-$TIME/@snapshots/@home-$TIME
umount /tmp/btrfsnap-$TIME
rmdir /tmp/btrfsnap-$TIME
rm /SNAPSHOT
rm /home/SNAPSHOT