Compare commits
2 Commits
a2f926dccc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a90b024db0 | ||
|
|
5f683ca307 |
51
daily_report.py
Executable file
51
daily_report.py
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Gmail SMTP settings
|
||||||
|
SMTP_SERVER = "smtp.gmail.com"
|
||||||
|
SMTP_PORT = 587
|
||||||
|
EMAIL_ADDRESS = "ddns.updates@gmail.com"
|
||||||
|
EMAIL_PASSWORD = "tacssyofvkgqvbzo"
|
||||||
|
TO_EMAIL = "rafjaniak@outlook.com"
|
||||||
|
|
||||||
|
|
||||||
|
# Log file path
|
||||||
|
LOG_FILE = "/var/log/ddns.log"
|
||||||
|
|
||||||
|
def send_email(subject, body):
|
||||||
|
msg = MIMEText(body)
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = EMAIL_ADDRESS
|
||||||
|
msg["To"] = TO_EMAIL
|
||||||
|
|
||||||
|
try:
|
||||||
|
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
|
||||||
|
server.ehlo()
|
||||||
|
server.starttls()
|
||||||
|
server.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
|
||||||
|
server.send_message(msg)
|
||||||
|
print("✅ Email sent successfully")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Failed to send email: {e}")
|
||||||
|
|
||||||
|
def get_last_log_entries():
|
||||||
|
last_entries = {}
|
||||||
|
with open(LOG_FILE) as f:
|
||||||
|
for line in f:
|
||||||
|
# Match domain entries with status ✅ or ❌
|
||||||
|
match = re.search(r"\] (✅|❌|🌍) (\S+)", line)
|
||||||
|
if match:
|
||||||
|
status, domain = match.groups()
|
||||||
|
last_entries[domain] = line.strip()
|
||||||
|
return last_entries
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
entries = get_last_log_entries()
|
||||||
|
if entries:
|
||||||
|
body = "\n".join(entries.values())
|
||||||
|
else:
|
||||||
|
body = "No log entries found."
|
||||||
|
|
||||||
|
send_email("DDNS Daily Report", body)
|
||||||
107
ddns.py
Executable file
107
ddns.py
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# CONFIG - only edit this:
|
||||||
|
API_TOKEN = "HLNmBZ-4fyCSvR6BErdFtch4jQj3GACePD4yD0xs"
|
||||||
|
DOMAINS = [
|
||||||
|
"snieznykoczkodan.com", # root domain
|
||||||
|
"proxy.snieznykoczkodan.com", # Nginx Proxy Manager .102
|
||||||
|
"proxmox.snieznykoczkodan.com", # Proxmox GUI .5
|
||||||
|
"nas.snieznykoczkodan.com", # Synology - rafnasbox .3
|
||||||
|
"jellyfin.snieznykoczkodan.com", # Jellyfin .104
|
||||||
|
"seer.snieznykoczkodan.com", # jellyseer .105
|
||||||
|
"prowlarr.snieznykoczkodan.com", # porwlarr .105 9696
|
||||||
|
"radarr.snieznykoczkodan.com", # radarr .105 7878
|
||||||
|
"sonarr.snieznykoczkodan.com", # sonarr .105 8989
|
||||||
|
"torrent.snieznykoczkodan.com", # qbitorrent .106
|
||||||
|
"grafana.snieznykoczkodan.com", # grafana .108
|
||||||
|
"git.snieznykoczkodan.com" # gittea .111 3000
|
||||||
|
]
|
||||||
|
TTL = 120 # 2 minutes
|
||||||
|
PROXIED = False # True = Cloudflare proxy (orange cloud), False = DNS only
|
||||||
|
# -----------------------------
|
||||||
|
|
||||||
|
BASE_URL = "https://api.cloudflare.com/client/v4"
|
||||||
|
HEADERS = {
|
||||||
|
"Authorization": f"Bearer {API_TOKEN}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def log(msg):
|
||||||
|
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
print(f"[{now}] {msg}")
|
||||||
|
|
||||||
|
def get_public_ip():
|
||||||
|
try:
|
||||||
|
return requests.get("https://api.ipify.org", timeout=10).text.strip()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"❌ Failed to get public IP: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def get_zone_id(domain):
|
||||||
|
try:
|
||||||
|
# extract the root domain (last two parts)
|
||||||
|
parts = domain.split('.')
|
||||||
|
root_domain = '.'.join(parts[-2:])
|
||||||
|
r = requests.get(f"{BASE_URL}/zones?name={root_domain}", headers=HEADERS)
|
||||||
|
r.raise_for_status()
|
||||||
|
zones = r.json()["result"]
|
||||||
|
if not zones:
|
||||||
|
log(f"❌ No zone found for {root_domain}")
|
||||||
|
sys.exit(1)
|
||||||
|
return zones[0]["id"]
|
||||||
|
except Exception as e:
|
||||||
|
log(f"❌ Error fetching Zone ID for {domain}: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def get_record_id(zone_id, domain):
|
||||||
|
try:
|
||||||
|
r = requests.get(f"{BASE_URL}/zones/{zone_id}/dns_records?type=A&name={domain}", headers=HEADERS)
|
||||||
|
r.raise_for_status()
|
||||||
|
records = r.json()["result"]
|
||||||
|
if not records:
|
||||||
|
log(f"❌ No A record found for {domain}")
|
||||||
|
sys.exit(1)
|
||||||
|
return records[0]["id"], records[0]["content"]
|
||||||
|
except Exception as e:
|
||||||
|
log(f"❌ Error fetching DNS record for {domain}: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def update_dns_record(zone_id, record_id, domain, ip):
|
||||||
|
data = {
|
||||||
|
"type": "A",
|
||||||
|
"name": domain,
|
||||||
|
"content": ip,
|
||||||
|
"ttl": TTL,
|
||||||
|
"proxied": PROXIED
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
r = requests.put(f"{BASE_URL}/zones/{zone_id}/dns_records/{record_id}", headers=HEADERS, json=data)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json().get("success", False)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"❌ Failed to update DNS record for {domain}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ip = get_public_ip()
|
||||||
|
log(f"🌍 Current public IP: {ip}")
|
||||||
|
|
||||||
|
for domain in DOMAINS:
|
||||||
|
zone_id = get_zone_id(domain)
|
||||||
|
record_id, current_ip = get_record_id(zone_id, domain)
|
||||||
|
|
||||||
|
if current_ip == ip:
|
||||||
|
log(f"✅ {domain} already up to date")
|
||||||
|
else:
|
||||||
|
log(f"🔄 Updating {domain} from {current_ip} → {ip}")
|
||||||
|
if update_dns_record(zone_id, record_id, domain, ip):
|
||||||
|
log(f"✅ {domain} updated successfully")
|
||||||
|
else:
|
||||||
|
log(f"❌ {domain} update failed")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
23
test_report.py
Executable file
23
test_report.py
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
import smtplib
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
SMTP_SERVER = "smtp.office365.com"
|
||||||
|
SMTP_PORT = 587
|
||||||
|
EMAIL_ADDRESS = "ddnsupdater@outlook.com"
|
||||||
|
EMAIL_PASSWORD = "ltehpesmqnkjenah"
|
||||||
|
TO_EMAIL = "rafjaniak@outlook.com"
|
||||||
|
|
||||||
|
msg = MIMEText("This is a test email from DDNS updater.")
|
||||||
|
msg["Subject"] = "Test Email"
|
||||||
|
msg["From"] = EMAIL_ADDRESS
|
||||||
|
msg["To"] = TO_EMAIL
|
||||||
|
|
||||||
|
try:
|
||||||
|
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
|
||||||
|
server.ehlo()
|
||||||
|
server.starttls()
|
||||||
|
server.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
|
||||||
|
server.send_message(msg)
|
||||||
|
print("✅ Email sent successfully")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Failed to send email: {e}")
|
||||||
Reference in New Issue
Block a user