From 402dbfc006f5de37b314804435d1f764638acff4 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:01:52 +0200 Subject: [PATCH 01/15] add scrapbook --- .env.example | 3 ++- .gitignore | 1 + channels.py | 46 ++++++++++++++++++++++++++++++++ main.py | 6 ++++- requirements.txt | 1 + routes/scrapbook.py | 57 ++++++++++++++++++++++++++++++++++++++++ static/typography.css | 25 ++++++++++++++---- templates/scrapbook.html | 26 ++++++++++++++++++ 8 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 channels.py create mode 100644 routes/scrapbook.py create mode 100644 templates/scrapbook.html diff --git a/.env.example b/.env.example index e05042e..ff42a12 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -URL="http://127.0.0.1:5000" # URL for rss/atom feed \ No newline at end of file +URL="http://127.0.0.1:5000" # URL for rss/atom feed +CHANNEL_LIST_TOKEN="xoxb-" # Used in channels.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index 399446e..57e3136 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +channels.json templates/articles/ !templates/articles/loremipsum.html diff --git a/channels.py b/channels.py new file mode 100644 index 0000000..bf7341a --- /dev/null +++ b/channels.py @@ -0,0 +1,46 @@ +""" +Create a dict of channel_id: channel_name for scrapbook +""" + +from dotenv import load_dotenv +import requests +import json +import os + +load_dotenv() + +ACCESS_TOKEN = os.getenv("CHANNEL_LIST_TOKEN") + +headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "Content-Type": "application/json" +} + +params = { + "types": "public_channel", + "limit": 1000 +} + +channels_map = {} + +while True: + response = requests.get("https://slack.com/api/conversations.list", headers=headers, params=params) + data = response.json() + + if not data.get("ok"): + print("Error:", data.get("error")) + break + + for channel in data.get("channels", []): + channels_map[channel["id"]] = channel["name"] + + cursor = data.get("response_metadata", {}).get("next_cursor") + if cursor: + params["cursor"] = cursor + else: + break + +print(f"{len(channels_map)} public channels found") + +with open("channels.json", "w+") as f: + json.dump(channels_map, f) \ No newline at end of file diff --git a/main.py b/main.py index 31bd9d1..08f4478 100644 --- a/main.py +++ b/main.py @@ -5,17 +5,20 @@ from flask import Flask, render_template, request, send_file, make_response from feedgen.feed import FeedGenerator from dotenv import load_dotenv from routes.editor import editor_routes +from routes.scrapbook import sp_routes from classes import * load_dotenv() app = Flask(__name__) app.register_blueprint(editor_routes) +app.register_blueprint(sp_routes) with open("articles.json", "r", encoding="utf-8") as f: articles_data = json.load(f) categories = {} # Category name:str -> [Article] articles = {} # ID:str = Article +# RSS Feed fg = FeedGenerator() fg.title("Mathias") fg.id("Mathias") @@ -24,6 +27,7 @@ fg.language("en") fg.link(href=os.getenv("URL")) fg.description("Blog RSS feed") +# Register articles for article_data in articles_data: category_name = article_data.get("category", "Uncategorized") category_list = categories.get(category_name, []) @@ -86,4 +90,4 @@ def article(article_id:str): return render_template(f"articles/{article.template}.html") if __name__ == "__main__": - app.run() + app.run(debug=True) diff --git a/requirements.txt b/requirements.txt index 32f5093..87bd431 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ flask feedgen +requests python-dotenv \ No newline at end of file diff --git a/routes/scrapbook.py b/routes/scrapbook.py new file mode 100644 index 0000000..92a8694 --- /dev/null +++ b/routes/scrapbook.py @@ -0,0 +1,57 @@ +""" +Display my hackclub scrapbook +see https://scrapbook.hackclub.com/about +""" + +from flask import Blueprint, render_template +from datetime import datetime +import requests +import time +import json +import re + +sp_routes = Blueprint('scrapbook', __name__, template_folder='templates') + +lastScrapbookUpdate = 0 +scrapbookPosts = [] + +try: + channels_maps = json.load(open('channels.json', 'r', encoding="utf-8")) +except: + print("channels.json not found") + channels_maps = {} + +@sp_routes.app_template_filter('formatDate') +def format_date(date_str): + date_obj = datetime.fromisoformat(date_str.replace('Z', '+00:00')) + return date_obj.strftime('%d/%m/%Y @ %Hh%M') + +@sp_routes.app_template_filter('formatContent') +def convert_slack_references(text): + # Convert channel references + channel_pattern = r'<#(C[A-Z0-9]+)\|>' + + def channel_replacement(match): + channel_id = match.group(1) + channel_name = channels_maps.get(channel_id, channel_id) + return f'#{channel_name}' + + result = re.sub(channel_pattern, channel_replacement, text) + + return result + +@sp_routes.route("/scrapbook") +def scrapbook(): + """Page linking to my scrapbook articles""" + global lastScrapbookUpdate, scrapbookPosts + if lastScrapbookUpdate+3600 < time.time(): + try: + r = requests.get("https://scrapbook.hackclub.com/api/users/mathias") + data = r.json() + scrapbookPosts = data["posts"] + scrapbookPosts.extend(scrapbookPosts) + scrapbookPosts.extend(scrapbookPosts) + lastScrapbookUpdate = time.time() + except: pass + + return render_template("scrapbook.html", posts=scrapbookPosts) \ No newline at end of file diff --git a/static/typography.css b/static/typography.css index e93b18b..66d8d7d 100644 --- a/static/typography.css +++ b/static/typography.css @@ -19,11 +19,11 @@ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { } /* Heading Hover Effects */ -h2 a:hover::before, -h3 a:hover::before, -h4 a:hover::before, -h5 a:hover::before, -h6 a:hover::before { +h2.chapter a:hover::before, +h3.chapter a:hover::before, +h4.chapter a:hover::before, +h5.chapter a:hover::before, +h6.chapter a:hover::before { content: "#"; position: absolute; left: -0.75em; @@ -42,6 +42,21 @@ time { font-size: 75%; } +#main details div { + padding-left: 1em; + line-height: 115%; +} + +#main details:open { + padding-bottom: 1em; +} + +#main details summary { + font-family: "Montserrat", sans-serif; + font-weight: 500; + padding-bottom: 0.25em; +} + hr { margin-bottom: 1em; } diff --git a/templates/scrapbook.html b/templates/scrapbook.html new file mode 100644 index 0000000..5669820 --- /dev/null +++ b/templates/scrapbook.html @@ -0,0 +1,26 @@ +{% extends 'base.html' %} + +{% block head %} +Scrapbook +{% endblock %} + + +{% block content %} +

Scrapook posts

+
+ See on scrapbook +
+
+ +{% for post in posts%} +{% if loop.first %} +
+{% else%} +
+{% endif %} + {{ post.postedAt | formatDate }} +
{{ post.text | formatContent |safe }}
+
+{% endfor %} + +{% endblock %} \ No newline at end of file From 5b2a3158fb30cfc9bb0083ceac245bd32f338a67 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:06:12 +0200 Subject: [PATCH 02/15] remove test .extend --- routes/scrapbook.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/routes/scrapbook.py b/routes/scrapbook.py index 92a8694..6a6e6c8 100644 --- a/routes/scrapbook.py +++ b/routes/scrapbook.py @@ -49,8 +49,6 @@ def scrapbook(): r = requests.get("https://scrapbook.hackclub.com/api/users/mathias") data = r.json() scrapbookPosts = data["posts"] - scrapbookPosts.extend(scrapbookPosts) - scrapbookPosts.extend(scrapbookPosts) lastScrapbookUpdate = time.time() except: pass From 7b21eed1463d3ad8dc7c0b77812464975dd6c092 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:15:04 +0200 Subject: [PATCH 03/15] Add scrapbook link --- templates/home.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/templates/home.html b/templates/home.html index cf475c2..edc7faa 100644 --- a/templates/home.html +++ b/templates/home.html @@ -13,18 +13,15 @@ li { h3 { padding-bottom: 0.1em; } -a { - color: inherit; -} {% endblock %} {% block content %} -👋, Welcome here, you can see it's a mess +👋, Welcome here, you can see it's a mess. +My scrapbook posts are available at /scrapbook

- {% for category_name, articles in categories %}

{{ category_name }}

    From 753bb1ec8552639add0f54f537664ba5b836bf04 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:15:32 +0200 Subject: [PATCH 04/15] typo: scrapook > scrapbook --- templates/scrapbook.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/scrapbook.html b/templates/scrapbook.html index 5669820..5c4b9eb 100644 --- a/templates/scrapbook.html +++ b/templates/scrapbook.html @@ -6,7 +6,7 @@ {% block content %} -

    Scrapook posts

    +

    Scrapbook posts

    See on scrapbook
    From f36033d84141e44dbc606ae940305259f3d88497 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:18:20 +0200 Subject: [PATCH 05/15] channel link open on a new page --- routes/scrapbook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/scrapbook.py b/routes/scrapbook.py index 6a6e6c8..739b01f 100644 --- a/routes/scrapbook.py +++ b/routes/scrapbook.py @@ -34,7 +34,7 @@ def convert_slack_references(text): def channel_replacement(match): channel_id = match.group(1) channel_name = channels_maps.get(channel_id, channel_id) - return f'#{channel_name}' + return f'#{channel_name}' result = re.sub(channel_pattern, channel_replacement, text) From 1bc6d0983506bc02d5936102903bbfb8d8200c29 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:32:09 +0200 Subject: [PATCH 06/15] add url to formatContent --- routes/scrapbook.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/routes/scrapbook.py b/routes/scrapbook.py index 739b01f..50e39ff 100644 --- a/routes/scrapbook.py +++ b/routes/scrapbook.py @@ -34,10 +34,18 @@ def convert_slack_references(text): def channel_replacement(match): channel_id = match.group(1) channel_name = channels_maps.get(channel_id, channel_id) - return f'#{channel_name}' + return f'#{channel_name}' result = re.sub(channel_pattern, channel_replacement, text) - + + url_pattern = r'<(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))>' + + def url_replacement(match): + url = match.group(1) + return f'{url}' + + result = re.sub(url_pattern, url_replacement, text) + return result @sp_routes.route("/scrapbook") From 15515f1bc20c66f5c3de17082b8dcb5688f37dff Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:39:07 +0200 Subject: [PATCH 07/15] remove url replacement --- routes/scrapbook.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/routes/scrapbook.py b/routes/scrapbook.py index 50e39ff..a47c411 100644 --- a/routes/scrapbook.py +++ b/routes/scrapbook.py @@ -38,14 +38,6 @@ def convert_slack_references(text): result = re.sub(channel_pattern, channel_replacement, text) - url_pattern = r'<(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))>' - - def url_replacement(match): - url = match.group(1) - return f'{url}' - - result = re.sub(url_pattern, url_replacement, text) - return result @sp_routes.route("/scrapbook") From 2366bd318935257d2bce82f6be6eea157bff196b Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 20:10:34 +0200 Subject: [PATCH 08/15] adjusts /scrapbook time to the user's time --- main.py | 3 +++ requirements.txt | 1 + static/typography.css | 4 ++++ templates/base.html | 1 + templates/scrapbook.html | 2 +- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 08f4478..3df9f04 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ import re import os from flask import Flask, render_template, request, send_file, make_response from feedgen.feed import FeedGenerator +from flask_moment import Moment from dotenv import load_dotenv from routes.editor import editor_routes from routes.scrapbook import sp_routes @@ -10,6 +11,8 @@ from classes import * load_dotenv() app = Flask(__name__) +moment = Moment(app) + app.register_blueprint(editor_routes) app.register_blueprint(sp_routes) diff --git a/requirements.txt b/requirements.txt index 87bd431..a75be84 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask feedgen requests +Flask-Moment python-dotenv \ No newline at end of file diff --git a/static/typography.css b/static/typography.css index 66d8d7d..dfa9116 100644 --- a/static/typography.css +++ b/static/typography.css @@ -36,6 +36,10 @@ h6.chapter a:hover::before { margin-bottom: 0.75em; } +summary > small { + font-size: 0.5em; +} + time { font-family: "Montserrat", sans-serif; font-weight: bold; diff --git a/templates/base.html b/templates/base.html index ce673df..40e333b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,6 +6,7 @@ {% block head %}{% endblock %} + {{ moment.include_moment() }}
    diff --git a/templates/scrapbook.html b/templates/scrapbook.html index 5c4b9eb..26bb95d 100644 --- a/templates/scrapbook.html +++ b/templates/scrapbook.html @@ -18,7 +18,7 @@ {% else%}
    {% endif %} - {{ post.postedAt | formatDate }} + {{ moment(post.postedAt).format('DD/MM/YYYY @ HH[h]mm') }}
    {{ post.text | formatContent |safe }}
    {% endfor %} From 74b6aac9225a72321b152522f02af4148c5396c3 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 20:30:02 +0200 Subject: [PATCH 09/15] add chapter to demo --- static/typography.css | 1 + templates/articles/loremipsum.html | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/static/typography.css b/static/typography.css index dfa9116..a1f3935 100644 --- a/static/typography.css +++ b/static/typography.css @@ -19,6 +19,7 @@ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { } /* Heading Hover Effects */ +h1.chapter a:hover::before, h2.chapter a:hover::before, h3.chapter a:hover::before, h4.chapter a:hover::before, diff --git a/templates/articles/loremipsum.html b/templates/articles/loremipsum.html index 7a2b118..8374a8b 100644 --- a/templates/articles/loremipsum.html +++ b/templates/articles/loremipsum.html @@ -5,11 +5,12 @@ {% endblock %} {% block content %} -

    Lorem Ipsum

    +

    Lorem Ipsum


    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla mattis, est et laoreet efficitur, nisi leo volutpat nibh, et rutrum erat urna vel nisl. Aenean sodales lorem quis dolor sodales, at convallis purus convallis. Nam interdum tincidunt nunc, sed molestie lectus. Integer a rhoncus enim, eu cursus lacus.

    Donec dignissim consequat augue mattis ornare. Vivamus feugiat odio in sagittis consectetur. Proin nec suscipit dolor, ut consectetur nisi. Aliquam ut ex dapibus, volutpat justo sit amet, dignissim tellus. Curabitur placerat tempor neque congue sollicitudin. Praesent elementum in ligula id semper.

    + {% endblock %} \ No newline at end of file From 4a8e87142bf7b1036127a9042401dabe71797855 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Thu, 17 Apr 2025 18:20:31 +0200 Subject: [PATCH 10/15] Fix link interpreted as tag --- routes/scrapbook.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/routes/scrapbook.py b/routes/scrapbook.py index a47c411..df1d531 100644 --- a/routes/scrapbook.py +++ b/routes/scrapbook.py @@ -36,8 +36,14 @@ def convert_slack_references(text): channel_name = channels_maps.get(channel_id, channel_id) return f'#{channel_name}' - result = re.sub(channel_pattern, channel_replacement, text) + url_pattern = r'<(http(?:|s):\/\/[a-zA-Z0-9\.\/_-]+)>' + def url_replacement(match): + url = match.group(1) + return f'{url}' + result = re.sub(url_pattern, url_replacement, text) + result = re.sub(channel_pattern, channel_replacement, result) + return result @sp_routes.route("/scrapbook") From 789a7fa2bbdf50cd7c4d895cee20e445a45b57b6 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Thu, 17 Apr 2025 18:34:04 +0200 Subject: [PATCH 11/15] Add attachments to scrapbook --- templates/scrapbook.html | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/templates/scrapbook.html b/templates/scrapbook.html index 26bb95d..25c4b7a 100644 --- a/templates/scrapbook.html +++ b/templates/scrapbook.html @@ -2,6 +2,15 @@ {% block head %} Scrapbook + + {% endblock %} @@ -20,6 +29,19 @@ {% endif %} {{ moment(post.postedAt).format('DD/MM/YYYY @ HH[h]mm') }}
    {{ post.text | formatContent |safe }}
    + {% for attachment in post.attachments %} +
    + {% if attachment.endswith(('.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp')) %} + Image + {% elif attachment.endswith(('.mp4', '.webm', '.ogg', '.mov', '.avi')) %} + + {% else %} +

    URL does not appear to be an image or video: {{ attachment }}

    + {% endif %} + {% endfor %}
{% endfor %} From 2771bd96120357c265138c24f458da7f77f16626 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Thu, 17 Apr 2025 18:51:17 +0200 Subject: [PATCH 12/15] change summary cursor to pointer --- static/typography.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/typography.css b/static/typography.css index a1f3935..7effbb1 100644 --- a/static/typography.css +++ b/static/typography.css @@ -57,6 +57,7 @@ time { } #main details summary { + cursor: pointer; font-family: "Montserrat", sans-serif; font-weight: 500; padding-bottom: 0.25em; From a3f34217607c53abaabd37f9728e04f47ccfcb54 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Fri, 18 Apr 2025 20:47:32 +0200 Subject: [PATCH 13/15] underline link on hover --- static/typography.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static/typography.css b/static/typography.css index 7effbb1..c8b9c5c 100644 --- a/static/typography.css +++ b/static/typography.css @@ -68,6 +68,10 @@ hr { } /* Links */ +a:hover { + text-decoration: underline; +} + a { text-decoration: none; color: #005B96; From 8dfe4dce9fcb98c56f041aedbccc7aa7819b83e6 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Sat, 19 Apr 2025 18:56:42 +0200 Subject: [PATCH 14/15] better split base.css / typography.css --- static/base.css | 52 +++++++++++++++++++++++++++++++++++++++++-- static/typography.css | 45 ------------------------------------- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/static/base.css b/static/base.css index 7171eda..dd7157f 100644 --- a/static/base.css +++ b/static/base.css @@ -1,6 +1,5 @@ @import url("typography.css"); - /* Reset */ * { margin: 0; @@ -52,6 +51,13 @@ header h1 { background-color: #03396C; padding: 5px 3em 5px 0; text-align: right; + min-width: 100%; + color: #ffffff; +} + +#navbar a + a::before { + content: "// "; + padding: 0 0.1em; } /* Main Content */ @@ -60,25 +66,67 @@ header h1 { flex: 1; } +#main details div { + padding-left: 1em; + line-height: 115%; +} + +#main details:open { + padding-bottom: 1em; +} + +#main details summary { + cursor: pointer; + padding-bottom: 0.25em; +} + /* Footer */ #footer { margin-top: auto; + text-align: center; } + #footer-title { width: 100%; + min-width: 100%; background-color: #03396C; + color: #ffffff; text-align: center; padding: 0.5em 0; } + +#footer p { + padding-top: 0.5em; + padding-bottom: 0.5em; + line-height: 1.25; +} + +hr { + margin-bottom: 1em; +} + +/* Links */ +a:hover { + text-decoration: underline; +} + +a { + text-decoration: none; + color: #005B96; +} + +/* Media Queries */ @media (max-width: 768px) { html, body { min-height: 100dvh; } + #wrap { width: 100%; max-width: none; } - footer p{ + + footer p { margin-bottom: 0.25em; } diff --git a/static/typography.css b/static/typography.css index c8b9c5c..7409202 100644 --- a/static/typography.css +++ b/static/typography.css @@ -47,40 +47,13 @@ time { font-size: 75%; } -#main details div { - padding-left: 1em; - line-height: 115%; -} - -#main details:open { - padding-bottom: 1em; -} - #main details summary { - cursor: pointer; font-family: "Montserrat", sans-serif; font-weight: 500; - padding-bottom: 0.25em; -} - -hr { - margin-bottom: 1em; -} - -/* Links */ -a:hover { - text-decoration: underline; -} - -a { - text-decoration: none; - color: #005B96; } /* Navigation */ #navbar { - min-width: 100%; - color: #ffffff; text-transform: uppercase; font-family: "Montserrat", sans-serif; } @@ -89,25 +62,7 @@ a { color: inherit; } -#navbar a + a::before { - content: "// "; - padding: 0 0.1em; -} - /* Footer */ #footer-title { - padding: 5px 0; - min-width: 100%; - color: #ffffff; font-family: "Montserrat", sans-serif; -} - -#footer { - text-align: center; -} - -#footer p { - padding-top: 0.5em; - padding-bottom: 0.5em; - line-height: 1.25; } \ No newline at end of file From 865535d3ba47793b7642475d6aea086ac69e4b3b Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Sat, 19 Apr 2025 23:14:47 +0200 Subject: [PATCH 15/15] Remove underline for navbar --- static/base.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static/base.css b/static/base.css index dd7157f..dd32a1d 100644 --- a/static/base.css +++ b/static/base.css @@ -110,6 +110,10 @@ a:hover { text-decoration: underline; } +nav a:hover { + text-decoration: none; +} + a { text-decoration: none; color: #005B96;