This commit is contained in:
commit
c7c1ff32a0
4 changed files with 250 additions and 0 deletions
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
some naturally occuring scripts
|
||||
|
||||
i dont' have a place to put them, but deleting them feels like a waste
|
||||
54
openwrt_pbr.sh
Normal file
54
openwrt_pbr.sh
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/sh
|
||||
|
||||
help() {
|
||||
echo 'up/down [DEVICE]'
|
||||
echo 'status [DEVICE]'
|
||||
}
|
||||
|
||||
is_available() {
|
||||
uci show pbr | grep -q "pbr.$1=policy"
|
||||
}
|
||||
|
||||
|
||||
# EXIT_SUCCESS if enabled
|
||||
# EXIT_FAILURE if disabled
|
||||
status() {
|
||||
uci get pbr."$1".enabled >/dev/null 2>&1
|
||||
}
|
||||
|
||||
up() {
|
||||
is_available "$1" || exit 1
|
||||
status "$1" || exit 1
|
||||
uci del pbr."$1".enabled >/dev/null
|
||||
uci commit >/dev/null
|
||||
/etc/init.d/pbr reload > /dev/null
|
||||
}
|
||||
|
||||
down() {
|
||||
is_available "$1" || exit 1
|
||||
status "$1" && exit 1
|
||||
uci set pbr."$1".enabled=0 >/dev/null
|
||||
uci commit >/dev/null
|
||||
/etc/init.d/pbr reload > /dev/null
|
||||
}
|
||||
|
||||
toggle() {
|
||||
is_available "$1" || exit 1
|
||||
if status "$1"; then
|
||||
up "$1"
|
||||
else
|
||||
down "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
help >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
"up") up "$2";;
|
||||
"down") down "$2" ;;
|
||||
"toggle") toggle "$2" ;;
|
||||
"status") status "$2" && echo '0' || echo '1' ;;
|
||||
esac
|
||||
148
poller.py
Normal file
148
poller.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import json
|
||||
import random
|
||||
import time
|
||||
import requests
|
||||
from collections.abc import Callable, Generator
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
class Poller:
|
||||
@dataclass
|
||||
class ChangedValue[T]:
|
||||
value: T
|
||||
|
||||
@dataclass
|
||||
class FoundValue:
|
||||
value: str
|
||||
|
||||
class _LogTypes(Enum):
|
||||
NOTHING = "Nothing found."
|
||||
CHANGED = "The value has changed, but nothing was found."
|
||||
FOUND = "Found!"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
filter: Callable[[requests.Response], str | None],
|
||||
requester: Callable[[], requests.Response],
|
||||
transformer: Callable[[requests.Response], Any] = lambda x: x.text,
|
||||
base_delay: tuple[int, int] = (20 * 60, 45 * 60),
|
||||
retry_delay: tuple[int, int] = (3 * 60, 10 * 60)
|
||||
) -> None:
|
||||
"""
|
||||
Initialize a Poller instance for periodically polling an API endpoint
|
||||
until a specific result is found.
|
||||
This class repeatedly sends requests to an API and inspects the responses
|
||||
using a filter function. If the filter detects the desired result,
|
||||
polling stops. Otherwise, it continues after a delay. A transformer
|
||||
function is used to normalize or clean the response content (e.g., removing
|
||||
noise such as timestamps or metadata) to avoid unnecessary triggering of
|
||||
the "changed" state.
|
||||
:param filter: A callable that examines a ``requests.Response`` object and
|
||||
returns a unique string (e.g., a key, ID, or result indicator)
|
||||
if the target condition is met. Returns ``None`` if polling
|
||||
should continue. This is the key signal for terminating the poll.
|
||||
:type filter: Callable[[requests.Response], str | None]
|
||||
:param requester: A callable that performs the actual API request and returns
|
||||
a ``requests.Response`` object. This abstracts the HTTP
|
||||
communication from the polling logic.
|
||||
:type requester: Callable[[], requests.Response]
|
||||
:param transformer: A callable used to clean or transform the response for
|
||||
change detection purposes. It helps filter out inconsequential
|
||||
differences like timestamps, headers, etc., that shouldn't
|
||||
be interpreted as meaningful changes. Defaults to
|
||||
``lambda x: x.text``.
|
||||
:type transformer: Callable[[requests.Response], Any], optional
|
||||
:param base_delay: A tuple representing the range of time in seconds to wait
|
||||
between polling cycles when no change is detected. This helps
|
||||
reduce the frequency of API calls when responses remain stable.
|
||||
:type base_delay: tuple[int, int], optional
|
||||
:param retry_delay: A tuple representing the range of time in seconds to wait
|
||||
after a change is detected (but not a final result), often
|
||||
to give the API time to settle or provide consistent data.
|
||||
:type retry_delay: tuple[int, int], optional
|
||||
:returns: None
|
||||
:rtype: None
|
||||
"""
|
||||
self.prev_resp = None
|
||||
self.filter = filter
|
||||
self.transformer = transformer
|
||||
self.requester = requester
|
||||
self.base_delay = base_delay
|
||||
self.retry_delay = retry_delay
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _log(t: _LogTypes, r: Any):
|
||||
print(f'[{time.strftime("%X %x %Z")}] {t.value} Response: {r}')
|
||||
|
||||
def _set_and_return[T](self, value: T) -> tuple[tuple[int, int], ChangedValue[T] | None]:
|
||||
if self.prev_resp != value:
|
||||
self.prev_resp = value
|
||||
self._log(self._LogTypes.CHANGED, value)
|
||||
return self.retry_delay, self.ChangedValue(value)
|
||||
self._log(self._LogTypes.NOTHING, value)
|
||||
return self.base_delay, None
|
||||
|
||||
def poll(self) -> Generator[ChangedValue | FoundValue | None]:
|
||||
while True:
|
||||
r = self.requester()
|
||||
|
||||
match self.filter(r):
|
||||
case None:
|
||||
delay, value = self._set_and_return(self.transformer(r))
|
||||
yield value
|
||||
case value:
|
||||
self._log(self._LogTypes.FOUND, value)
|
||||
yield self.FoundValue(value)
|
||||
return
|
||||
|
||||
time.sleep(random.randint(*delay))
|
||||
|
||||
|
||||
TOKEN = ""
|
||||
ADMIN_ID = 0
|
||||
TG_API_URL = f'https://api.telegram.org/bot{TOKEN}/sendMessage'
|
||||
|
||||
def send_message(prefix: str, body: Any):
|
||||
prefix += '\n'
|
||||
body = str(body)
|
||||
|
||||
entities = {
|
||||
'type': 'pre',
|
||||
'offset': len(prefix) + 1,
|
||||
'length': len(body)
|
||||
}
|
||||
|
||||
r = requests.post(TG_API_URL, data = {
|
||||
'chat_id': ADMIN_ID,
|
||||
'entities': json.dumps([entities]),
|
||||
'text': prefix + body,
|
||||
})
|
||||
if r.status_code != 200:
|
||||
print(r.text)
|
||||
|
||||
|
||||
def make_request() -> requests.Response:
|
||||
return requests.get("https://google.com")
|
||||
|
||||
|
||||
def my_filter(r: requests.Response) -> str | None:
|
||||
if r.status_code != 200:
|
||||
return
|
||||
return r.text
|
||||
|
||||
|
||||
def main():
|
||||
print('Running!')
|
||||
poller = Poller(my_filter, make_request)
|
||||
|
||||
for resp in poller.poll():
|
||||
match resp:
|
||||
case Poller.ChangedValue(x):
|
||||
send_message("🟡", x)
|
||||
case Poller.FoundValue(x):
|
||||
send_message("🟢", x)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
45
socket_wildcard.py
Normal file
45
socket_wildcard.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import socket
|
||||
import struct
|
||||
|
||||
# use with
|
||||
# iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 2000
|
||||
|
||||
SO_ORIGINAL_DST = 80 # from linux/netfilter_ipv4.h
|
||||
|
||||
def get_original_dst(conn):
|
||||
"""Return (ip, port) of the original destination before REDIRECT."""
|
||||
# Get raw sockaddr_in (16 bytes)
|
||||
odest = conn.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
|
||||
port, raw_ip = struct.unpack_from('!2xH4s', odest)
|
||||
ip = socket.inet_ntoa(raw_ip)
|
||||
return ip, port
|
||||
|
||||
def main():
|
||||
HOST = '0.0.0.0'
|
||||
PORT = 2000
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind((HOST, PORT))
|
||||
s.listen(10)
|
||||
|
||||
print(f"Listening on {HOST}:{PORT}")
|
||||
|
||||
while True:
|
||||
conn, addr = s.accept()
|
||||
try:
|
||||
orig_ip, orig_port = get_original_dst(conn)
|
||||
print(f"Connection from {addr}, originally to {orig_ip}:{orig_port}")
|
||||
except OSError as e:
|
||||
print(f"Could not get original destination: {e}")
|
||||
|
||||
with conn:
|
||||
while True:
|
||||
data = conn.recv(1024)
|
||||
if not data:
|
||||
break
|
||||
conn.sendall(data)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue