git ssb

0+

Grey the earthling / scuttleblog



Tree: 1338c020316a3eb9679397cf649ef5278a25fe1c

Files: 1338c020316a3eb9679397cf649ef5278a25fe1c / scuttleblog.py

6128 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
33scuttleblog_dir = os.path.dirname(__file__)
34
35def run_sbot():
36 for attempt in range(100):
37 try:
38 subprocess.check_output(['sbot', 'status'])
39 except:
40 pid = subprocess.Popen(['sbot', 'server']).pid
41 time.sleep(1)
42 else:
43 break
44
45def get_user_posts(ssb_userid):
46 user_posts_args = ['sbot', 'createUserStream', '--id', ssb_userid]
47 json_posts_args = ['json', '--group', \
48 '-c', 'this.value.content.type == "post"', \
49 '-c', 'this.value.content.text != null', \
50 '-c', 'this.value.content.text != ""', \
51 '-c', 'this.value.content.root == null']
52 user_posts_stream = subprocess.Popen(user_posts_args,
53 stdout = subprocess.PIPE)
54 user_posts_json = subprocess.check_output(json_posts_args,
55 stdin = user_posts_stream.stdout)
56 user_posts = json.loads(user_posts_json)
57 return user_posts
58
59def define_post_text(text):
60 text = text.strip()
61 title = text.splitlines()[0]
62 if re.match('#', text):
63 # The first line is a heading.
64 # Use it as the title and strip it from the body.
65 if len(text.splitlines()) > 1:
66 body = ''.join(text.splitlines(keepends=True)[1:]).strip()
67 else:
68 body = ''
69 else:
70 # Truncate the first line and use it as the title.
71 # Keep everything in the body.
72 maxlength = 100
73 ellipsis = '...'
74 if len(title) > maxlength:
75 title = title[:maxlength].rsplit(' ', 1)[0] + ellipsis
76 body = text
77 title = re.sub('^#+\s+', '', title)
78 return {'title': title, 'body': body}
79
80def build_post_structure(p):
81 post = {}
82 post['frontmatter'] = {}
83 post['frontmatter']['key'] = p['key']
84 post['frontmatter']['title'] = define_post_text(
85 p['value']['content']['text'])['title']
86 time = datetime.datetime.fromtimestamp(int(p['value']['timestamp']
87 / 1000),
88 datetime.timezone.utc)
89 post['frontmatter']['date'] = time.strftime('%Y-%m-%dT%H:%M:%SZ')
90 post['frontmatter']['sequence'] = p['value']['sequence']
91 post['body'] = define_post_text(p['value']['content']['text'])['body']
92 return (post)
93
94def format_post_file(post):
95 content = ''
96 content += str(json.dumps(post['frontmatter'], indent = 4))
97 content += '\n\n'
98 content += str(post['body'])
99 return content
100
101def define_post_filename(post):
102 folder = 'hugo/content/posts'
103 slug = str(post['frontmatter']['key'])
104 filetype = 'md'
105 return folder + '/' + slug + '.' + filetype
106
107def write_post_file(post):
108 os.makedirs(os.path.dirname(define_post_filename(post)), exist_ok=True)
109 with open(define_post_filename(post), 'w') as f:
110 f.write (format_post_file(post))
111
112def write_posts_from_user(ssb_userid):
113 posts = get_user_posts(ssb_userid)
114 for post in posts:
115 write_post_file(build_post_structure(post))
116
117
118def get_user_metadata(ssb_userid):
119 user_metadata_args = ['sbot', 'links',
120 '--source', ssb_userid,
121 '--dest', ssb_userid,
122 '--rel', 'about',
123 '--values']
124 json_metadata_args = ['json', '--deep-merge',
125 '-c', 'this.value.content.type == "about"']
126 user_metadata_stream = subprocess.Popen(user_metadata_args,
127 stdout = subprocess.PIPE)
128 user_metadata_json = subprocess.check_output(json_metadata_args,
129 stdin = user_metadata_stream.stdout)
130 user_metadata = json.loads(user_metadata_json)
131 return user_metadata
132
133def check_hugo_theme(theme, theme_clone_url):
134 hugo_theme_dir = os.path.join(scuttleblog_dir, 'hugo', 'themes', theme)
135 if not os.path.isdir(hugo_theme_dir):
136 subprocess.run(['git', 'submodule', 'add', '-f',
137 theme_clone_url, hugo_theme_dir])
138
139def build_hugo_config(m):
140 hugo_config = {}
141 hugo_config['baseurl'] = conf.hugo_baseurl
142 hugo_config['theme'] = conf.hugo_theme
143 hugo_config['permalinks'] = {}
144 hugo_config['permalinks']['post'] = '/:filename/'
145 if 'name' in m['value']['content']:
146 hugo_config['title'] = m['value']['content']['name']
147 hugo_config['params'] = {}
148 if 'description' in m['value']['content']:
149 description = m['value']['content']['description']
150 hugo_config['params']['subtitle'] = description.replace('\n', ' ')
151 return (hugo_config)
152
153def write_hugo_config_from_user(ssb_userid):
154 metadata = get_user_metadata(ssb_userid)
155 for file in ['hugo/config.toml', 'hugo/config.yaml']:
156 if os.path.exists(file):
157 os.remove(file)
158 with open('hugo/config.json', 'w') as f:
159 f.write (json.dumps(build_hugo_config(metadata), indent = 4))
160
161def run_hugo():
162 subprocess.run(['hugo', '-s', 'hugo'])
163
164run_sbot()
165write_posts_from_user(conf.ssb_userid)
166write_hugo_config_from_user(conf.ssb_userid)
167check_hugo_theme(conf.hugo_theme, conf.hugo_theme_clone_url)
168run_hugo()
169
170

Built with git-ssb-web