mirror of
https://github.com/MathiasDPX/blog.git
synced 2025-05-10 07:33:09 +00:00
add scrapbook
This commit is contained in:
parent
cbe04a86d1
commit
402dbfc006
8 changed files with 158 additions and 7 deletions
|
@ -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
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
channels.json
|
||||||
templates/articles/
|
templates/articles/
|
||||||
!templates/articles/loremipsum.html
|
!templates/articles/loremipsum.html
|
||||||
|
|
||||||
|
|
46
channels.py
Normal file
46
channels.py
Normal 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)
|
6
main.py
6
main.py
|
@ -5,17 +5,20 @@ from flask import Flask, render_template, request, send_file, make_response
|
||||||
from feedgen.feed import FeedGenerator
|
from feedgen.feed import FeedGenerator
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from routes.editor import editor_routes
|
from routes.editor import editor_routes
|
||||||
|
from routes.scrapbook import sp_routes
|
||||||
from classes import *
|
from classes import *
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.register_blueprint(editor_routes)
|
app.register_blueprint(editor_routes)
|
||||||
|
app.register_blueprint(sp_routes)
|
||||||
|
|
||||||
with open("articles.json", "r", encoding="utf-8") as f:
|
with open("articles.json", "r", encoding="utf-8") as f:
|
||||||
articles_data = json.load(f)
|
articles_data = json.load(f)
|
||||||
categories = {} # Category name:str -> [Article]
|
categories = {} # Category name:str -> [Article]
|
||||||
articles = {} # ID:str = Article
|
articles = {} # ID:str = Article
|
||||||
|
|
||||||
|
# RSS Feed
|
||||||
fg = FeedGenerator()
|
fg = FeedGenerator()
|
||||||
fg.title("Mathias")
|
fg.title("Mathias")
|
||||||
fg.id("Mathias")
|
fg.id("Mathias")
|
||||||
|
@ -24,6 +27,7 @@ fg.language("en")
|
||||||
fg.link(href=os.getenv("URL"))
|
fg.link(href=os.getenv("URL"))
|
||||||
fg.description("Blog RSS feed")
|
fg.description("Blog RSS feed")
|
||||||
|
|
||||||
|
# Register articles
|
||||||
for article_data in articles_data:
|
for article_data in articles_data:
|
||||||
category_name = article_data.get("category", "Uncategorized")
|
category_name = article_data.get("category", "Uncategorized")
|
||||||
category_list = categories.get(category_name, [])
|
category_list = categories.get(category_name, [])
|
||||||
|
@ -86,4 +90,4 @@ def article(article_id:str):
|
||||||
return render_template(f"articles/{article.template}.html")
|
return render_template(f"articles/{article.template}.html")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run(debug=True)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
flask
|
flask
|
||||||
feedgen
|
feedgen
|
||||||
|
requests
|
||||||
python-dotenv
|
python-dotenv
|
57
routes/scrapbook.py
Normal file
57
routes/scrapbook.py
Normal 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)
|
|
@ -19,11 +19,11 @@ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Heading Hover Effects */
|
/* Heading Hover Effects */
|
||||||
h2 a:hover::before,
|
h2.chapter a:hover::before,
|
||||||
h3 a:hover::before,
|
h3.chapter a:hover::before,
|
||||||
h4 a:hover::before,
|
h4.chapter a:hover::before,
|
||||||
h5 a:hover::before,
|
h5.chapter a:hover::before,
|
||||||
h6 a:hover::before {
|
h6.chapter a:hover::before {
|
||||||
content: "#";
|
content: "#";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -0.75em;
|
left: -0.75em;
|
||||||
|
@ -42,6 +42,21 @@ time {
|
||||||
font-size: 75%;
|
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 {
|
hr {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
26
templates/scrapbook.html
Normal file
26
templates/scrapbook.html
Normal 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 %}
|
Loading…
Add table
Add a link
Reference in a new issue