Added support for webhooks.
This commit is contained in:
1 parent
b91c86e01f
commit
d1927036b9
1 file changed
+38
-31
+38
-31
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Description: Monitor a website for changes.
|
# Description: Monitor a website for changes.
|
||||||
# Usage: python3 diff-web.py https://example.com --time 60 --email user@example.com
|
# Usage: python3 diff-web.py https://example.com --time 300 --email user@example.com --hook https://example.com/webhook
|
||||||
# Author: Justin Oros
|
# Author: Justin Oros
|
||||||
# Source: https://github.com/JustinOros
|
# Source: https://github.com/JustinOros
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ import requests
|
|||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
|
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -21,12 +22,30 @@ SMTP_USERNAME = os.getenv("EMAIL_USER")
|
|||||||
SMTP_PASSWORD = os.getenv("EMAIL_PASS")
|
SMTP_PASSWORD = os.getenv("EMAIL_PASS")
|
||||||
EMAIL_FROM = SMTP_USERNAME
|
EMAIL_FROM = SMTP_USERNAME
|
||||||
|
|
||||||
|
sys.argv = [arg if not arg.startswith('/') else '-' + arg[1:] for arg in sys.argv]
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Monitor a website for changes.", add_help=False)
|
||||||
|
parser.add_argument("--help", action="help", help="Show this help message and exit")
|
||||||
|
parser.add_argument("url", nargs='?', help="The website URL or domain to monitor.")
|
||||||
|
parser.add_argument("-d", "--domain", help="Domain to monitor.")
|
||||||
|
parser.add_argument("-t", "--time", type=int, default=60,
|
||||||
|
help="Time interval between checks in seconds (default: 60).")
|
||||||
|
parser.add_argument("-l", "--log", type=str,
|
||||||
|
help="Optional log file name (default: domain.tld.log)")
|
||||||
|
parser.add_argument("-q", "--quiet", action="store_true",
|
||||||
|
help="Suppress console output.")
|
||||||
|
parser.add_argument("-e", "--email", nargs='+',
|
||||||
|
help="Email address(es) to notify on changes.")
|
||||||
|
parser.add_argument("-h", "--hook", type=str,
|
||||||
|
help="Webhook URL to POST to on changes.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
def normalize_url(url):
|
def normalize_url(url):
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
if not parsed.scheme:
|
if not parsed.scheme:
|
||||||
url = 'https://' + url
|
url = 'https://' + url
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
|
|
||||||
query = parse_qs(parsed.query)
|
query = parse_qs(parsed.query)
|
||||||
stripped_query = {k: v for k, v in query.items() if not k.startswith(('utm_', 'fbclid', 'gclid'))}
|
stripped_query = {k: v for k, v in query.items() if not k.startswith(('utm_', 'fbclid', 'gclid'))}
|
||||||
new_query = urlencode(stripped_query, doseq=True)
|
new_query = urlencode(stripped_query, doseq=True)
|
||||||
@@ -82,21 +101,25 @@ def send_email(subject, body, recipients):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to send email: {e}")
|
print(f"Failed to send email: {e}")
|
||||||
|
|
||||||
def monitor_website(url, interval, log_file, quiet=False, recipients=None):
|
def send_webhook(url, payload):
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=payload, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to send webhook to {url}: {e}")
|
||||||
|
|
||||||
|
def monitor_website(url, interval, log_file, quiet=False, recipients=None, webhook_url=None):
|
||||||
url = normalize_url(url)
|
url = normalize_url(url)
|
||||||
domain = urlparse(url).netloc
|
domain = urlparse(url).netloc
|
||||||
cache_file = get_cache_filename(url)
|
cache_file = get_cache_filename(url)
|
||||||
|
|
||||||
if not os.path.exists(log_file):
|
if not os.path.exists(log_file):
|
||||||
with open(log_file, 'w') as f:
|
with open(log_file, 'w') as f:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
initial_message = f"[{timestamp()}] Monitoring {domain}"
|
initial_message = f"[{timestamp()}] Monitoring {domain}"
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print(initial_message)
|
print(initial_message)
|
||||||
with open(log_file, 'a') as f:
|
with open(log_file, 'a') as f:
|
||||||
f.write(initial_message + '\n')
|
f.write(initial_message + '\n')
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
current_content = fetch_content(url)
|
current_content = fetch_content(url)
|
||||||
@@ -105,7 +128,6 @@ def monitor_website(url, interval, log_file, quiet=False, recipients=None):
|
|||||||
log_message(f"[{timestamp()}] [ERROR] Failed to fetch {url}: {e}", log_file, quiet)
|
log_message(f"[{timestamp()}] [ERROR] Failed to fetch {url}: {e}", log_file, quiet)
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
is_changed = True
|
is_changed = True
|
||||||
if os.path.exists(cache_file):
|
if os.path.exists(cache_file):
|
||||||
with open(cache_file, 'r') as f:
|
with open(cache_file, 'r') as f:
|
||||||
@@ -117,45 +139,30 @@ def monitor_website(url, interval, log_file, quiet=False, recipients=None):
|
|||||||
subject = f"Website Change Detected: {domain}"
|
subject = f"Website Change Detected: {domain}"
|
||||||
body = f"A change was detected on {url} at {timestamp()}."
|
body = f"A change was detected on {url} at {timestamp()}."
|
||||||
send_email(subject, body, recipients)
|
send_email(subject, body, recipients)
|
||||||
|
if webhook_url:
|
||||||
|
payload = {
|
||||||
|
"url": url,
|
||||||
|
"domain": domain,
|
||||||
|
"timestamp": timestamp(),
|
||||||
|
"message": "Change detected on monitored website."
|
||||||
|
}
|
||||||
|
send_webhook(webhook_url, payload)
|
||||||
else:
|
else:
|
||||||
log_message(f"[{timestamp()}] First-time check for {url}: storing baseline.", log_file, quiet)
|
log_message(f"[{timestamp()}] First-time check for {url}: storing baseline.", log_file, quiet)
|
||||||
|
|
||||||
with open(cache_file, 'w') as f:
|
with open(cache_file, 'w') as f:
|
||||||
f.write(current_hash)
|
f.write(current_hash)
|
||||||
|
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(description="Monitor a website for changes.")
|
|
||||||
parser.add_argument("url", nargs='?', help="The website URL or domain to monitor.")
|
|
||||||
parser.add_argument("-d", "--domain", help="Domain to monitor.")
|
|
||||||
parser.add_argument("-t", "--time", type=int, default=60,
|
|
||||||
help="Time interval between checks in seconds (default: 60).")
|
|
||||||
parser.add_argument("-l", "--log", type=str,
|
|
||||||
help="Optional log file name (default: domain.tld.log)")
|
|
||||||
parser.add_argument("-q", "--quiet", action="store_true",
|
|
||||||
help="Suppress console output.")
|
|
||||||
parser.add_argument("-e", "--email", nargs='+',
|
|
||||||
help="Email address(es) to notify on changes.")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
target = args.domain if args.domain else args.url
|
target = args.domain if args.domain else args.url
|
||||||
|
|
||||||
if not target:
|
if not target:
|
||||||
parser.error("You must specify a URL/domain to monitor either as positional argument or with -d/--domain.")
|
parser.error("You must specify a URL/domain to monitor either as positional argument or with -d/--domain.")
|
||||||
|
|
||||||
# Normalize target for filename extraction
|
|
||||||
if not urlparse(target).scheme:
|
if not urlparse(target).scheme:
|
||||||
target = "https://" + target
|
target = "https://" + target
|
||||||
|
|
||||||
log_file = get_log_filename(target, args.log)
|
log_file = get_log_filename(target, args.log)
|
||||||
|
|
||||||
if not os.path.exists(log_file):
|
if not os.path.exists(log_file):
|
||||||
print(f"Log file created at {os.path.abspath(log_file)}")
|
print(f"Log file created at {os.path.abspath(log_file)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
monitor_website(target, args.time, log_file, args.quiet, args.email)
|
monitor_website(target, args.time, log_file, args.quiet, args.email, args.hook)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
message = f"[{timestamp()}] Monitoring halted by user (^C)."
|
message = f"[{timestamp()}] Monitoring halted by user (^C)."
|
||||||
if not args.quiet:
|
if not args.quiet:
|
||||||
|
|||||||
Reference in new issue
Block a user