Compare commits
2 Commits
8b8f80a93a
...
60a14e10a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 60a14e10a5 | |||
| d5239a9d78 |
50
main.py
50
main.py
@ -4,12 +4,16 @@ import schedule
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import datetime
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
URL_TO_MONITOR = "https://univesp.br/vestibular"
|
||||
HASH_FILE = os.path.join("/data", "last_hash.txt")
|
||||
NTFY_URL = os.getenv("NTFY_URL")
|
||||
FLARESOLVERR_URL = os.getenv("FLARESOLVERR_URL")
|
||||
MONITOR_SELECTOR = os.getenv("MONITOR_SELECTOR", "div.conteudo")
|
||||
HASH_FILE = os.path.join("/data", "last_hash.txt")
|
||||
LAST_CHANGE_FILE = os.path.join("/data", "last_change_timestamp.txt")
|
||||
LAST_NOTIFY_FILE = os.path.join("/data", "last_staleness_notification.txt")
|
||||
|
||||
|
||||
def send_notification(message):
|
||||
@ -52,7 +56,14 @@ def get_page_hash(url):
|
||||
html_content = fs_response['solution']['response']
|
||||
print(f"[{datetime.now()}] FlareSolverr request successful.")
|
||||
|
||||
return hashlib.sha256(html_content.encode('utf-8')).hexdigest()
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
element = soup.select_one(MONITOR_SELECTOR)
|
||||
|
||||
if element:
|
||||
return hashlib.sha256(str(element).encode('utf-8')).hexdigest()
|
||||
else:
|
||||
print(f"[{datetime.now()}] ❌ Element '{MONITOR_SELECTOR}' not found in the page.")
|
||||
return None
|
||||
else:
|
||||
print(f"[{datetime.now()}] FlareSolverr failed to solve the challenge. Status: {fs_response.get('status')}. Error: {fs_response.get('message')}")
|
||||
return None
|
||||
@ -82,6 +93,10 @@ def check_for_updates():
|
||||
|
||||
with open(HASH_FILE, 'w') as f:
|
||||
f.write(current_hash)
|
||||
|
||||
# Update last change timestamp
|
||||
with open(LAST_CHANGE_FILE, 'w') as f:
|
||||
f.write(str(datetime.now().timestamp()))
|
||||
else:
|
||||
print(f"[{datetime.now()}] No changes found.")
|
||||
else:
|
||||
@ -89,8 +104,39 @@ def check_for_updates():
|
||||
with open(HASH_FILE, 'w') as f:
|
||||
f.write(current_hash)
|
||||
|
||||
# Initialize last change timestamp
|
||||
with open(LAST_CHANGE_FILE, 'w') as f:
|
||||
f.write(str(datetime.now().timestamp()))
|
||||
|
||||
return
|
||||
|
||||
# Check for staleness (no changes for > 7 days)
|
||||
if os.path.exists(LAST_CHANGE_FILE):
|
||||
try:
|
||||
with open(LAST_CHANGE_FILE, 'r') as f:
|
||||
last_change_ts = float(f.read().strip())
|
||||
|
||||
time_since_change = datetime.now().timestamp() - last_change_ts
|
||||
if time_since_change > 7 * 24 * 60 * 60: # 7 days in seconds
|
||||
|
||||
# Check if we recently notified about this
|
||||
should_notify = True
|
||||
if os.path.exists(LAST_NOTIFY_FILE):
|
||||
with open(LAST_NOTIFY_FILE, 'r') as f:
|
||||
last_notify_ts = float(f.read().strip())
|
||||
|
||||
if (datetime.now().timestamp() - last_notify_ts) < 7 * 24 * 60 * 60:
|
||||
should_notify = False
|
||||
|
||||
if should_notify:
|
||||
print(f"[{datetime.now()}] ⚠️ Updates are stale (> 7 days). Sends notification.")
|
||||
send_notification("⚠️ No changes detected for over 7 days. Please check manually.")
|
||||
|
||||
with open(LAST_NOTIFY_FILE, 'w') as f:
|
||||
f.write(str(datetime.now().timestamp()))
|
||||
except Exception as e:
|
||||
print(f"[{datetime.now()}] Error checking staleness: {e}")
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -5,5 +5,5 @@ description = "App to verify if the Univesp pages changed"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"requests", "schedule"
|
||||
"requests", "schedule", "beautifulsoup4"
|
||||
]
|
||||
|
||||
33
uv.lock
generated
33
uv.lock
generated
@ -2,6 +2,19 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.14"
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.14.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "soupsieve" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.11.12"
|
||||
@ -69,17 +82,37 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/20/a7/84c96b61fd13205f2cafbe263cdb2745965974bdf3e0078f121dfeca5f02/schedule-1.2.2-py3-none-any.whl", hash = "sha256:5bef4a2a0183abf44046ae0d164cadcac21b1db011bdd8102e4a0c1e91e06a7d", size = 12220, upload-time = "2024-05-25T18:41:59.121Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "univesper"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "beautifulsoup4" },
|
||||
{ name = "requests" },
|
||||
{ name = "schedule" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "beautifulsoup4" },
|
||||
{ name = "requests" },
|
||||
{ name = "schedule" },
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user