git ssb

0+

Grey the earthling / scuttleblog



Tree: 3e4cd5cd53d0b8d6f4964b7960386bb0b6763182

Files: 3e4cd5cd53d0b8d6f4964b7960386bb0b6763182 / scuttleblog.py

4734 bytesRaw
1#!/usr/bin/python3
2
3__copyright__ = """
4
5 Scuttleblog
6
7 Copyright (C) 2017 Greg K Nicholson
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Affero General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Affero General Public License for more details.
18
19 You should have received a copy of the GNU Affero General Public License
20 along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22"""
23
24import datetime
25import json
26import os
27import re
28import time
29import subprocess
30
31import conf
32
33def run_sbot():
34 for attempt in range(100):
35 try:
36 subprocess.check_output(['sbot', 'status'])
37 except:
38 pid = subprocess.Popen(['sbot', 'server']).pid
39 time.sleep(1)
40 else:
41 break
42
43def get_user_posts(ssb_userid):
44 user_posts_args = ['sbot', 'createUserStream', '--id', ssb_userid]
45 json_posts_args = ['json', '--group', '-c', 'this.value.content.type == "post"', '-c', 'this.value.content.root == null']
46 user_posts_stream = subprocess.Popen(user_posts_args, stdout = subprocess.PIPE)
47 user_posts_json = subprocess.check_output(json_posts_args, stdin = user_posts_stream.stdout)
48 user_posts = json.loads(user_posts_json)
49 return user_posts
50
51def define_post_text(text):
52 text = text.strip()
53 title = text.splitlines()[0]
54 if re.match('#', text):
55 # The first line is a heading. Use it as the title and strip it from the body.
56 body = ''.join(text.splitlines(keepends=True)[1:]).strip() \
57 if len(text.splitlines()) > 1 \
58 else ''
59 else:
60 # Truncate the first line and use it as the title. Keep everything in the body.
61 maxlength = 100
62 ellipsis = '...'
63 title = title if len(title) <= maxlength else title[:maxlength].rsplit(' ', 1)[0] + ellipsis
64 body = text
65 title = re.sub('^#+\s+', '', title)
66 # FIXME: This doesn't gracefully handle markdown near the start of a post.
67 return {'title': title, 'body': body}
68
69def build_post_structure(p):
70 post = {}
71 post['frontmatter'] = {}
72 post['frontmatter']['key'] = p['key']
73 post['frontmatter']['title'] = define_post_text(p['value']['content']['text'])['title']
74 post['frontmatter']['date'] = datetime.datetime.fromtimestamp(int(p['value']['timestamp'] / 1000), datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
75 post['frontmatter']['sequence'] = p['value']['sequence']
76 post['body'] = define_post_text(p['value']['content']['text'])['body']
77 return (post)
78
79def format_post_file(post):
80 content = ''
81 content += str(json.dumps(post['frontmatter'], indent = 4))
82 content += '\n\n'
83 content += str(post['body'])
84 return content
85
86def define_post_filename(post):
87 folder = 'hugo/content/posts'
88 #slug = post['key'].replace('+','-').replace('/','_').replace('=.sha256','')
89 slug = str(post['frontmatter']['sequence'])
90 filetype = 'md'
91 return folder + '/' + slug + '.' + filetype
92
93def write_post_file(post):
94 os.makedirs(os.path.dirname(define_post_filename(post)), exist_ok=True)
95 with open(define_post_filename(post), 'w') as f:
96 f.write (format_post_file(post))
97
98def write_posts_from_user(ssb_userid):
99 posts = get_user_posts(ssb_userid)
100 for post in posts:
101 write_post_file(build_post_structure(post))
102
103
104def get_user_metadata(ssb_userid):
105 user_metadata_args = ['sbot', 'links', '--source', ssb_userid, '--dest', ssb_userid, '--rel', 'about', '--values']
106 json_metadata_args = ['json', '--deep-merge', '-c', 'this.value.content.type == "about"']
107 user_metadata_stream = subprocess.Popen(user_metadata_args, stdout = subprocess.PIPE)
108 user_metadata_json = subprocess.check_output(json_metadata_args, stdin = user_metadata_stream.stdout)
109 user_metadata = json.loads(user_metadata_json)
110 return user_metadata
111
112def build_hugo_config(m):
113 hugo_config = {}
114 hugo_config['baseurl'] = conf.hugo_baseurl
115 hugo_config['theme'] = conf.hugo_theme
116 if 'name' in m['value']['content']:
117 hugo_config['title'] = m['value']['content']['name']
118 hugo_config['params'] = {}
119 if 'description' in m['value']['content']:
120 hugo_config['params']['subtitle'] = m['value']['content']['description'].replace('\n', ' ')
121 return (hugo_config)
122
123def write_hugo_config_from_user(ssb_userid):
124 metadata = get_user_metadata(ssb_userid)
125 with open('hugo/config.json', 'w') as f:
126 f.write (json.dumps(build_hugo_config(metadata), indent = 4))
127 # TODO: add the user's image in the right place
128
129def run_hugo():
130 subprocess.run(['hugo', '-s', 'hugo'])
131
132run_sbot()
133write_posts_from_user(conf.ssb_userid)
134write_hugo_config_from_user(conf.ssb_userid)
135run_hugo()
136
137

Built with git-ssb-web