add scrapbook

This commit is contained in:
Mathias DUPEUX 2025-04-15 14:01:52 +02:00
parent cbe04a86d1
commit 402dbfc006
8 changed files with 158 additions and 7 deletions

View file

@ -1 +1,2 @@
URL="http://127.0.0.1:5000" # URL for rss/atom feed
URL="http://127.0.0.1:5000" # URL for rss/atom feed
CHANNEL_LIST_TOKEN="xoxb-" # Used in channels.py

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
channels.json
templates/articles/
!templates/articles/loremipsum.html

46
channels.py Normal file
View file

@ -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)

View file

@ -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)

View file

@ -1,3 +1,4 @@
flask
feedgen
requests
python-dotenv

57
routes/scrapbook.py Normal file
View file

@ -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'<a href="https://hackclub.slack.com/archives/{channel_id}">#{channel_name}</a>'
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)

View file

@ -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;
}

26
templates/scrapbook.html Normal file
View file

@ -0,0 +1,26 @@
{% extends 'base.html' %}
{% block head %}
<title>Scrapbook</title>
{% endblock %}
{% block content %}
<h1>Scrapook posts</h1>
<i><h6>
<a href="https://scrapbook.hackclub.com/mathias" style="color: #005B96;">See on scrapbook</a>
</h6></i>
<br>
{% for post in posts%}
{% if loop.first %}
<details id="{{ post.id }}" open>
{% else%}
<details id="{{ post.id }}">
{% endif %}
<summary>{{ post.postedAt | formatDate }}</summary>
<div>{{ post.text | formatContent |safe }}</div>
</details>
{% endfor %}
{% endblock %}