Compare commits

...

16 Commits

Author SHA1 Message Date
9754ea32a6 enabled nix module 2023-08-04 23:25:24 +02:00
fcd9326cf9 updated nix modules 2023-08-04 23:10:47 +02:00
e4f94e04a6 formatting, minor tweaks, fixed txpo 2023-08-03 23:14:04 +02:00
67e4afd0bf added direnv support 2023-08-03 14:22:29 +02:00
b9960c46a3 added type annotation 2023-08-02 20:48:06 +02:00
e732a3457d changed path to a standard config path 2023-08-02 20:47:28 +02:00
99d387c692 made more concise 2023-08-02 20:35:08 +02:00
91e4b5f73a added default path 2023-08-02 20:34:16 +02:00
28e7a2855e updated modules in flake 2023-08-01 21:59:54 +02:00
12a4ab7ae7 added support for keys in external files 2023-08-01 21:58:56 +02:00
db71bf8452 fixed typo 2023-08-01 21:12:03 +02:00
b49f8a6a03 used formatter 2023-07-27 13:34:55 +02:00
28901f25f1 added formatter 2023-07-27 13:34:32 +02:00
2511d6ae4d used more elegant library features 2023-07-26 22:31:05 +02:00
8beb0a9fb3 deleted comment 2023-07-26 22:28:14 +02:00
263c6f0a10 working flake 2023-07-26 22:27:02 +02:00
13 changed files with 500 additions and 98 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

6
.gitignore vendored
View File

@ -1,2 +1,6 @@
result
config.json config.json
log.txt log.txt
__pycache__/
.vscode/
.direnv/

View File

@ -31,7 +31,7 @@ Resolvers are queried one after another until one returns a valid IP.
}, },
"ttl": 3600, "ttl": 3600,
"resolvers": [ "resolvers": [
"https://ifconfig.me/ip" "https://ifconfig.me/ip",
"https://me.gandi.net" "https://me.gandi.net"
], ],
"log_path": "./log.txt" "log_path": "./log.txt"

View File

@ -1,93 +0,0 @@
import requests, json, re
from datetime import datetime
def loadSettings() -> dict:
# TODO: check integrity
try:
with open('config.json', 'r') as file:
return json.load(file)
except FileNotFoundError:
sample_config = {
'api': {},
'ttl': 3600,
'resolvers': [ 'https://ifconfig.me' ],
'log_path': './log.txt'
}
with open('config.json', 'w') as file:
json.dump(sample_config, file, indent = 2)
return sample_config
# Appends logline at the end of the file file with a timestamp
def log(logline: str, path: str = './log.txt') -> None:
with open(path, 'a') as file:
file.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]{logline}\n")
# Go through resolvers until one retuns an IP
def getCurrentIP(resolvers: list[str], log: callable) -> None | str:
for resolver in resolvers:
response = requests.request('GET', resolver)
if response.status_code == 200:
current_ip = response.text.strip()
is_ipv4 = re.search('^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\\b){4}$', current_ip) is not None
if is_ipv4:
log(f"[OK][{resolver}] Current IP: '{current_ip}'")
return current_ip # Break if we have found our IP
else:
log(f"[ERROR][{resolver}] '{current_ip}' is not IPv4")
else:
log(f"[ERROR][{resolver}][{response.status_code}] {response.text}")
return None
# Returns the records that need to be renewed
# TODO: Also detect TTL change
def checkRecords(api: dict, current_ip: str, log: callable) -> dict:
records_to_renew = api
for key, domains in api.items():
headers = { 'authorization': f"Apikey {key}" }
for domain, records in domains.items():
response = requests.request('GET', f"https://api.gandi.net/v5/livedns/domains/{domain}/records?rrset_type=A", headers = headers)
if response.status_code == 200:
records_to_renew[key][domain] = list(set(records_to_renew[key][domain]) - set(record['rrset_name'] for record in (filter(lambda x: current_ip in x['rrset_values'], json.loads(response.text)))))
else:
log(f"[ERROR][{domain}][{response.status_code}] {response.text}")
return records_to_renew
# Updates the records and reates them if they don't exist
def renewRecords(records, current_ip, log, ttl = 3600):
payload = json.dumps({
'items': [{
'rrset_type': 'A',
'rrset_values': [ current_ip ],
'rrset_ttl': ttl
}]
})
for key, domains in records.items():
headers = {
'authorization': f"Apikey {key}",
'content-type': 'application/json',
}
for domain, records in domains.items():
for record in records:
response = requests.request('PUT', f"https://api.gandi.net/v5/livedns/domains/{domain}/records/{record}", data = payload, headers = headers)
if response.status_code == 201:
log(f"[OK][{domain}][{record}] Renewed with IP '{current_ip}'")
else:
log(f"[ERROR][{domain}][{record}][{response.status_code}] {response.text}")
def main():
settings = loadSettings()
log_path = settings['log_path']
ttl = settings['ttl']
log = lambda x: log(x, path = log_path)
current_ip = getCurrentIP(settings['resolvers'], log)
records_to_renew = checkRecords(settings['api'], current_ip, log)
renewRecords(records_to_renew, current_ip, log, ttl = ttl)
if __name__ == '__main__':
main()

0
dyn_gandi/__init__.py Normal file
View File

110
dyn_gandi/main.py Normal file
View File

@ -0,0 +1,110 @@
import requests
import json
import re
import os
from datetime import datetime
def loadSettings(path: str = f"{os.path.expanduser('~')}/.config/dyn-gandi/config.json") -> dict:
# TODO: check integrity of the config file
try:
with open(path, 'r') as file:
# Read if the API keys are path to files
config = json.load(file)
keys = config['api'].keys()
mask = dict()
for key in keys:
api_key = key
if os.path.exists(key):
with open(key, 'r') as file:
api_key = file.readline().strip()
mask[key] = api_key
config['api'] = {mask[key]: value for key,
value in config['api'].items()}
except FileNotFoundError:
quit()
# TODO log error and remove code below (do not create an unwanted config)
def log(logline: str, path: str = './log.txt') -> None:
# Appends logline at the end of the file file with a timestamp
with open(path, 'a') as file:
file.write(
f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]{logline}\n")
def getCurrentIP(resolvers: list[str], log: callable) -> None | str:
# Go through resolvers until one retuns an IP
for resolver in resolvers:
response = requests.get(resolver)
if response.status_code == 200:
current_ip = response.text.strip()
is_ipv4 = re.search(
'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\\b){4}$', current_ip) is not None
if is_ipv4:
log(f"[OK][{resolver}] Current IP: '{current_ip}'")
return current_ip # Break if we have found our IP
else:
log(f"[ERROR][{resolver}] '{current_ip}' is not IPv4")
else:
log(f"[ERROR][{resolver}][{response.status_code}] {response.text}")
return None
def checkRecords(api: dict, current_ip: str, log: callable) -> dict:
# Returns the records that need to be renewed
# TODO: Also detect TTL change
records_to_renew = api
for key, domains in api.items():
headers = {'authorization': f"Apikey {key}"}
for domain, records in domains.items():
response = requests.get(
f"https://api.gandi.net/v5/livedns/domains/{domain}/records?rrset_type=A", headers=headers)
if response.status_code == 200:
records_to_renew[key][domain] = list(set(records_to_renew[key][domain]) - set(record['rrset_name'] for record in (
filter(lambda x: current_ip in x['rrset_values'], json.loads(response.json())))))
else:
log(f"[ERROR][{domain}][{response.status_code}] {response.json()}")
return records_to_renew
def renewRecords(records, current_ip, log, ttl=3600):
# Updates the records and reates them if they don't exist
payload = json.dumps({
'items': [{
'rrset_type': 'A',
'rrset_values': [current_ip],
'rrset_ttl': ttl
}]
})
for key, domains in records.items():
headers = {
'authorization': f"Apikey {key}",
'content-type': 'application/json',
}
for domain, records in domains.items():
for record in records:
response = requests.put(
f"https://api.gandi.net/v5/livedns/domains/{domain}/records/{record}", data=payload, headers=headers)
if response.status_code == 201:
log(f"[OK][{domain}][{record}] Renewed with IP '{current_ip}'")
else:
log(f"[ERROR][{domain}][{record}][{response.status_code}] {response.json()}")
def main():
settings = loadSettings()
log_path = settings['log_path']
ttl = settings['ttl']
def myLog(x): return log(x, log_path)
current_ip = getCurrentIP(settings['resolvers'], myLog)
records_to_renew = checkRecords(settings['api'], current_ip, myLog)
renewRecords(records_to_renew, current_ip, myLog, ttl=ttl)
if __name__ == '__main__':
main()

27
flake.lock generated Normal file
View File

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1690271650,
"narHash": "sha256-qwdsW8DBY1qH+9luliIH7VzgwvL+ZGI3LZWC0LTiDMI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "6dc93f0daec55ee2f441da385aaf143863e3d671",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,11 +1,47 @@
{ {
description = "A very basic flake"; description = "A very basic flake";
outputs = { self, nixpkgs }: { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
};
packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; outputs = {
self,
nixpkgs,
} @ inputs: let
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
in {
# Formatter
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.alejandra);
packages.x86_64-linux.default = self.packages.x86_64-linux.hello; # Packages
packages = forAllSystems (system: {
default = pkgs.${system}.poetry2nix.mkPoetryApplication {projectDir = self;};
});
# Dev Shell
devShells = forAllSystems (system: {
default = pkgs.${system}.mkShellNoCC {
packages = with pkgs.${system}; [
(poetry2nix.mkPoetryEnv {projectDir = self;})
poetry
];
};
});
# Run as apps
apps = forAllSystems (system: {
default = {
program = "${self.packages.${system}.default}/bin/dyn_gandi";
type = "app";
};
});
# Home Manager Module
homeManagerModules.default = import ./nix/hm-module.nix self;
# NixOS Module
nixosModules.default = import ./nix/module.nix inputs;
}; };
} }

71
nix/hm-module.nix Normal file
View File

@ -0,0 +1,71 @@
self: {
config,
lib,
pkgs,
...
}: let
cfg = config.services.dyn-gandi;
package = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
inherit (lib) mkIf mkEnableOption mkOption types;
in {
options.services.dyn-gandi = {
enable = mkEnableOption "dyn-gandi";
timer = lib.mkOption {
type = types.nullOr types.int;
default = null;
description = lib.mdDoc ''
The time intervall in seconds the script should be repeated.
'';
};
settings = mkOption {
type = with types; let
valueType = nullOr (oneOf [
# TODO: restrict type to actual config file structure
bool
int
float
str
path
(attrsOf valueType)
(listOf valueType)
]);
in
valueType;
default = throw "Please specify services.dyn-gandi.settings";
};
};
config = mkIf cfg.enable {
home.packages = [package]; # TODO: make this architecture independent
xdg.configFile."dyn-gandi/config.json" = {
text = builtins.toJSON cfg.settings;
};
systemd.user.services.dyn-gandi = mkIf (cfg.timer
!= null) {
Unit = {
After = ["network.target"];
};
Install = {WantedBy = ["default.target"];};
Service = {
Type = "oneshot";
ExecStart = "${package}/bin/dyn_gandi"; # TODO: add config file via command line options
};
};
systemd.user.timers.dyn-gandi = mkIf (cfg.timer
!= null) {
Install = {WantedBy = ["timers.target"];};
Timer = {
OnBootSec = "0s";
OnUnitActiveSec = "${toString cfg.timer}s";
Unit = "dyn-gandi.service";
};
};
};
}

74
nix/module.nix Normal file
View File

@ -0,0 +1,74 @@
self: {
config,
lib,
pkgs,
system,
...
}: let
cfg = config.services.dyn-gandi;
package = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
inherit (lib) mkIf mkEnableOption mkOption types;
in {
options.services.dyn-gandi = {
enable = mkEnableOption "dyn-gandi";
timer = lib.mkOption {
type = types.nullOr types.int;
default = null;
description = lib.mdDoc ''
The time intervall in seconds the script should be repeated.
'';
};
settings = mkOption {
type = with types; let
valueType = nullOr (oneOf [
# TODO: restrict type to actual config file structure
bool
int
float
str
path
(attrsOf valueType)
(listOf valueType)
]);
in
valueType;
default = throw "Please specify services.dyn-gandi.settings";
};
};
config = mkIf cfg.enable {
environment = {
systemPackages = [package];
}; # TODO: make this architecture independent
xdg.configFile."dyn-gandi/config.json" = {
text = builtins.toJSON cfg.settings;
};
systemd.services.dyn-gandi = mkIf (cfg.timer
!= null) {
Unit = {
After = ["network.target"];
};
Install = {WantedBy = ["default.target"];};
Service = {
Type = "oneshot";
ExecStart = "${package}/bin/dyn_gandi"; # TODO: add config file via command line options
};
};
systemd.timers.dyn-gandi = mkIf (cfg.timer
!= null) {
Install = {WantedBy = ["timers.target"];};
Timer = {
OnBootSec = "0s";
OnUnitActiveSec = "${toString cfg.timer}s";
Unit = "dyn-gandi.service";
};
};
};
}

154
poetry.lock generated Normal file
View File

@ -0,0 +1,154 @@
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]]
name = "certifi"
version = "2023.7.22"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
]
[[package]]
name = "charset-normalizer"
version = "3.2.0"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"},
{file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"},
{file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"},
{file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"},
{file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"},
{file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"},
{file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"},
{file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"},
{file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"},
{file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"},
{file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"},
]
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
[[package]]
name = "requests"
version = "2.29.0"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "requests-2.29.0-py3-none-any.whl", hash = "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b"},
{file = "requests-2.29.0.tar.gz", hash = "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "urllib3"
version = "1.26.16"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
{file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"},
{file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "ee132bd4e431294d08ddbc6bd9a59272e394e2a275ec7982d40592401aa07350"

18
pyproject.toml Normal file
View File

@ -0,0 +1,18 @@
[tool.poetry]
name = "dyn-gandi"
version = "0.1.0"
description = "A DNS updater"
authors = ["Kristian Krsnik <git@krsnik.at>"]
readme = "README.md"
packages = [{include = "dyn_gandi"}]
[tool.poetry.dependencies]
python = "^3.10"
requests = "2.29.0"
[tool.poetry.scripts]
dyn_gandi = "dyn_gandi.main:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

0
tests/__init__.py Normal file
View File