From 402dbfc006f5de37b314804435d1f764638acff4 Mon Sep 17 00:00:00 2001 From: MathiasDPX Date: Tue, 15 Apr 2025 14:01:52 +0200 Subject: [PATCH] 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