4 Commits

Author SHA1 Message Date
50596fc4ba feat: Add pubdate to rss feed
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 1m25s
Build Docker / BuildImage (push) Successful in 2m25s
2026-01-28 22:01:30 +07:00
83436352e7 fix: RSS page now is available on /now.rss again
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 1m11s
Build Docker / BuildImage (push) Successful in 1m19s
2026-01-28 21:56:06 +07:00
779f895ef8 fix: Add last played spotify song when none currently playing
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 2m39s
Build Docker / BuildImage (push) Successful in 4m23s
2026-01-28 21:50:15 +07:00
d53c48ba85 feat: Remove Revolut and Wise from donate page
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 2m41s
Build Docker / BuildImage (push) Successful in 3m59s
2025-12-17 17:27:09 +11:00
9 changed files with 56 additions and 18 deletions

View File

@@ -6,6 +6,7 @@
"BTC": "Bitcoin (BTC)", "BTC": "Bitcoin (BTC)",
"CRO": "Crypto.com (CRO)", "CRO": "Crypto.com (CRO)",
"DOT": "Polkadot (DOT)", "DOT": "Polkadot (DOT)",
"ERG": "ERGO (ERG)",
"ETH": "Ethereum (ETH)", "ETH": "Ethereum (ETH)",
"HNS": "Handshake (HNS)", "HNS": "Handshake (HNS)",
"MATIC": "Polygon (MATIC)", "MATIC": "Polygon (MATIC)",

1
.well-known/wallets/ERG Normal file
View File

@@ -0,0 +1 @@
9gfxbmYdeVGr4PPRbsBHhsJJSHyacJRNr61LhxudHc8PtQPCUsM

Binary file not shown.

View File

@@ -7,7 +7,7 @@ from mail import sendEmail
from tools import getClientIP, getGitCommit, json_response, parse_date, get_tools_data from tools import getClientIP, getGitCommit, json_response, parse_date, get_tools_data
from blueprints import sol from blueprints import sol
from dateutil import parser as date_parser from dateutil import parser as date_parser
from blueprints.spotify import get_spotify_track from blueprints.spotify import get_playing_spotify_track
from cache_helper import get_nc_config, get_git_latest_activity from cache_helper import get_nc_config, get_git_latest_activity
# Constants # Constants
@@ -176,7 +176,7 @@ def tools():
@app.route("/playing") @app.route("/playing")
def playing(): def playing():
"""Get the currently playing Spotify track.""" """Get the currently playing Spotify track."""
track_info = get_spotify_track() track_info = get_playing_spotify_track()
if "error" in track_info: if "error" in track_info:
return json_response(request, track_info, HTTP_OK) return json_response(request, track_info, HTTP_OK)
return json_response(request, {"spotify": track_info}, HTTP_OK) return json_response(request, {"spotify": track_info}, HTTP_OK)

View File

@@ -8,7 +8,7 @@ from bs4 import BeautifulSoup
import re import re
# Create blueprint # Create blueprint
app = Blueprint("now", __name__, url_prefix="/now") app = Blueprint("now", __name__, url_prefix="/")
@lru_cache(maxsize=16) @lru_cache(maxsize=16)
@@ -136,14 +136,14 @@ def render_curl(date=None):
) )
@app.route("/", strict_slashes=False) @app.route("/now", strict_slashes=False)
def index(): def index():
if isCLI(request): if isCLI(request):
return render_curl() return render_curl()
return render_latest(handshake_scripts=getHandshakeScript(request.host)) return render_latest(handshake_scripts=getHandshakeScript(request.host))
@app.route("/<path:path>") @app.route("/now/<path:path>")
def path(path): def path(path):
if isCLI(request): if isCLI(request):
return render_curl(path) return render_curl(path)
@@ -151,6 +151,7 @@ def path(path):
return render(path, handshake_scripts=getHandshakeScript(request.host)) return render(path, handshake_scripts=getHandshakeScript(request.host))
@app.route("/now/old", strict_slashes=False)
@app.route("/old", strict_slashes=False) @app.route("/old", strict_slashes=False)
def old(): def old():
now_dates = list_dates()[1:] now_dates = list_dates()[1:]
@@ -187,16 +188,26 @@ def old():
@app.route("/rss.xml") @app.route("/rss.xml")
def rss(): def rss():
host = "https://" + request.host host = "https://" + request.host
path = request.path
if ":" in request.host: if ":" in request.host:
host = "http://" + request.host host = "http://" + request.host
# Generate RSS feed # Generate RSS feed
now_pages = list_page_files() now_pages = list_page_files()
rss = f'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nathan.Woodburn/</title><link>{host}</link><description>See what I\'ve been up to</description><language>en-us</language><lastBuildDate>{datetime.datetime.now(tz=datetime.timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")}</lastBuildDate><atom:link href="{host}/now.rss" rel="self" type="application/rss+xml" />' rss = f'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nathan.Woodburn/</title><link>{host}</link><description>See what I\'ve been up to</description><language>en-us</language><lastBuildDate>{datetime.datetime.now(tz=datetime.timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")}</lastBuildDate><atom:link href="{host}{path}" rel="self" type="application/rss+xml" />'
for page in now_pages: for page in now_pages:
link = page.strip(".html") link = page.strip(".html")
date = datetime.datetime.strptime(link, "%y_%m_%d") date = datetime.datetime.strptime(link, "%y_%m_%d")
pubdate = date.strftime("%a, %d %b %Y 00:00:00 +0000")
date = date.strftime("%A, %B %d, %Y") date = date.strftime("%A, %B %d, %Y")
rss += f"<item><title>What's Happening {date}</title><link>{host}/now/{link}</link><description>Latest updates for {date}</description><guid>{host}/now/{link}</guid></item>" rss += f"""
<item>
<title>What's Happening {date}</title>
<link>{host}/now/{link}</link>
<description>Latest updates for {date}</description>
<pubDate>{pubdate}</pubDate>
<guid isPermaLink="true">{host}/now/{link}</guid>
</item>
"""
rss += "</channel></rss>" rss += "</channel></rss>"
return make_response(rss, 200, {"Content-Type": "application/rss+xml"}) return make_response(rss, 200, {"Content-Type": "application/rss+xml"})

View File

@@ -15,7 +15,8 @@ SPOTIFY_AUTH_URL = "https://accounts.spotify.com/authorize"
SPOTIFY_TOKEN_URL = "https://accounts.spotify.com/api/token" SPOTIFY_TOKEN_URL = "https://accounts.spotify.com/api/token"
SPOTIFY_CURRENTLY_PLAYING_URL = "https://api.spotify.com/v1/me/player/currently-playing" SPOTIFY_CURRENTLY_PLAYING_URL = "https://api.spotify.com/v1/me/player/currently-playing"
SCOPE = "user-read-currently-playing user-read-playback-state"
SCOPE = "user-read-currently-playing user-read-playback-state user-read-recently-played"
ACCESS_TOKEN = None ACCESS_TOKEN = None
REFRESH_TOKEN = os.getenv("SPOTIFY_REFRESH_TOKEN") REFRESH_TOKEN = os.getenv("SPOTIFY_REFRESH_TOKEN")
@@ -103,11 +104,11 @@ def callback():
@app.route("/playing") @app.route("/playing")
def currently_playing(): def currently_playing():
"""Public endpoint showing your current track.""" """Public endpoint showing your current track."""
track = get_spotify_track() track = get_playing_spotify_track()
return json_response(request, {"spotify": track}, 200) return json_response(request, {"spotify": track}, 200)
def get_spotify_track(): def get_playing_spotify_track():
"""Internal function to get current playing track without HTTP context.""" """Internal function to get current playing track without HTTP context."""
token = refresh_access_token() token = refresh_access_token()
if not token: if not token:
@@ -115,9 +116,9 @@ def get_spotify_track():
headers = {"Authorization": f"Bearer {token}"} headers = {"Authorization": f"Bearer {token}"}
response = requests.get(SPOTIFY_CURRENTLY_PLAYING_URL, headers=headers) response = requests.get(SPOTIFY_CURRENTLY_PLAYING_URL, headers=headers)
if response.status_code == 204: if response.status_code == 204:
return {"error": "Nothing is currently playing."} # return {"error": "Nothing is currently playing."}
return get_last_spotify_track()
elif response.status_code != 200: elif response.status_code != 200:
return {"error": "Spotify API error", "status": response.status_code} return {"error": "Spotify API error", "status": response.status_code}
@@ -135,3 +136,30 @@ def get_spotify_track():
"duration_ms": data["item"].get("duration_ms", 1), "duration_ms": data["item"].get("duration_ms", 1),
} }
return track return track
def get_last_spotify_track():
"""Internal function to get last played track without HTTP context."""
token = refresh_access_token()
if not token:
return {"error": "Failed to refresh access token"}
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(
"https://api.spotify.com/v1/me/player/recently-played", headers=headers
)
if response.status_code != 200:
print("Spotify API error:", response.text)
return {"error": "Spotify API error", "status": response.status_code}
data = response.json()
if not data.get("items"):
return {"error": "No recently played tracks found."}
last_track_info = data["items"][0]["track"]
track = {
"song_name": last_track_info["name"],
"artist": ", ".join([artist["name"] for artist in last_track_info["artists"]]),
"album_name": last_track_info["album"]["name"],
"album_art": last_track_info["album"]["images"][0]["url"],
"played_at": data["items"][0]["played_at"],
}
return track

View File

@@ -2,7 +2,7 @@ from flask import render_template
from tools import getAddress, get_tools_data, getClientIP from tools import getAddress, get_tools_data, getClientIP
import os import os
from functools import lru_cache from functools import lru_cache
from blueprints.spotify import get_spotify_track from blueprints.spotify import get_playing_spotify_track
from cache_helper import get_git_latest_activity, get_projects as get_projects_cached from cache_helper import get_git_latest_activity, get_projects as get_projects_cached
@@ -61,7 +61,7 @@ def curl_response(request):
"index.ascii", "index.ascii",
repo=get_current_project(), repo=get_current_project(),
ip=getClientIP(request), ip=getClientIP(request),
spotify=get_spotify_track(), spotify=get_playing_spotify_track(),
), ),
200, 200,
{"Content-Type": "text/plain; charset=utf-8"}, {"Content-Type": "text/plain; charset=utf-8"},

View File

@@ -65,7 +65,6 @@ RATE_LIMIT_WINDOW = 3600 # 1 hour in seconds
RESTRICTED_ROUTES = ["ascii"] RESTRICTED_ROUTES = ["ascii"]
REDIRECT_ROUTES = { REDIRECT_ROUTES = {
"contact": "/#contact", "contact": "/#contact",
"old": "/now/old",
"/meet": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr", "/meet": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
"/meeting": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr", "/meeting": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
"/appointment": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr", "/appointment": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",

View File

@@ -70,8 +70,6 @@
<li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://paypal.me/nathanwoodburn" target="_blank" style="width: auto;"><i class="fab fa-paypal fa-fw"></i><span class="network-name">&nbsp; Paypal</span></a></li> <li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://paypal.me/nathanwoodburn" target="_blank" style="width: auto;"><i class="fab fa-paypal fa-fw"></i><span class="network-name">&nbsp; Paypal</span></a></li>
<li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://github.com/sponsors/Nathanwoodburn" target="_blank" style="width: auto;"><i class="fab fa-github fa-fw"></i><span class="network-name">&nbsp; Github</span></a></li> <li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://github.com/sponsors/Nathanwoodburn" target="_blank" style="width: auto;"><i class="fab fa-github fa-fw"></i><span class="network-name">&nbsp; Github</span></a></li>
<li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://donate.stripe.com/8wM6pv0VD08Xe408ww" target="_blank" style="width: auto;"><i class="fab fa-stripe-s fa-fw"></i><span class="network-name">&nbsp;Stripe</span></a></li> <li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://donate.stripe.com/8wM6pv0VD08Xe408ww" target="_blank" style="width: auto;"><i class="fab fa-stripe-s fa-fw"></i><span class="network-name">&nbsp;Stripe</span></a></li>
<li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://wise.com/pay/me/nathanjoelw6" target="_blank" style="width: auto;"><span class="network-name">&nbsp;Wise</span></a></li>
<li class="list-inline-item" style="margin-top: 15px;">&nbsp;<a class="btn btn-primary btn-lg btn-default" role="button" href="https://revolut.me/nwoodburn" target="_blank" style="width: auto;"><span class="network-name">&nbsp;Revolut</span></a></li>
</ul> </ul>
<h4>Crypto Options</h4><div class="dropdown"> <h4>Crypto Options</h4><div class="dropdown">
<button class="btn btn-primary btn-lg btn-default" aria-expanded="false" data-bs-toggle="dropdown" type="button" id="dropdownButton">Select Crypto</button> <button class="btn btn-primary btn-lg btn-default" aria-expanded="false" data-bs-toggle="dropdown" type="button" id="dropdownButton">Select Crypto</button>