git ssb

0+

Grey the earthling / scuttleblog



Tree: 36dcea55c65ac21eda7d833b2616ab35da5cc6ad

Files: 36dcea55c65ac21eda7d833b2616ab35da5cc6ad / scuttleblog.py

6153 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:
10 you can redistribute it and/or modify it
11 under the terms of the GNU Affero General Public License
12 as published by the Free Software Foundation,
13 either version 3 of the License,
14 or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY;
18 without even the implied warranty
19 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU Affero General Public License for more details.
21
22 You should have received a copy
23 of the GNU Affero General Public License
24 along with this program.
25 If not, see <https://www.gnu.org/licenses/>.
26
27"""
28
29import datetime
30import json
31import os
32import re
33import time
34import subprocess
35
36import conf
37
38scuttleblog_dir = os.path.dirname(__file__)
39
40def run_sbot():
41 for attempt in range(100):
42 try:
43 subprocess.check_output(['sbot', 'status'])
44 except:
45 pid = subprocess.Popen(['sbot', 'server']).pid
46 time.sleep(1)
47 else:
48 break
49
50def get_user_posts(ssb_userid):
51 user_posts_args = ['sbot', 'createUserStream', '--id', ssb_userid]
52 json_posts_args = ['json', '--group', \
53 '-c', 'this.value.content.type == "post"', \
54 '-c', 'this.value.content.text != null', \
55 '-c', 'this.value.content.text != ""', \
56 '-c', 'this.value.content.root == null']
57 user_posts_stream = subprocess.Popen(user_posts_args,
58 stdout = subprocess.PIPE)
59 user_posts_json = subprocess.check_output(json_posts_args,
60 stdin = user_posts_stream.stdout)
61 user_posts = json.loads(user_posts_json)
62 return user_posts
63
64def define_post_text(text):
65 text = text.strip()
66 title = text.splitlines()[0]
67 if re.match('#', text):
68 # The first line is a heading.
69 # Use it as the title and strip it from the body.
70 if len(text.splitlines()) > 1:
71 body = ''.join(text.splitlines(keepends=True)[1:]).strip()
72 else:
73 body = ''
74 else:
75 # Truncate the first line and use it as the title.
76 # Keep everything in the body.
77 maxlength = 100
78 ellipsis = '...'
79 if len(title) > maxlength:
80 title = title[:maxlength].rsplit(' ', 1)[0] + ellipsis
81 body = text
82 title = re.sub('^#+\s+', '', title)
83 return {'title': title, 'body': body}
84
85def build_post_structure(p):
86 post = {}
87 post['frontmatter'] = {}
88 post['frontmatter']['key'] = p['key']
89 post['frontmatter']['title'] = define_post_text(
90 p['value']['content']['text'])['title']
91 time = datetime.datetime.fromtimestamp(int(p['value']['timestamp']
92 / 1000),
93 datetime.timezone.utc)
94 post['frontmatter']['date'] = time.strftime('%Y-%m-%dT%H:%M:%SZ')
95 post['frontmatter']['sequence'] = p['value']['sequence']
96 post['body'] = define_post_text(p['value']['content']['text'])['body']
97 return (post)
98
99def format_post_file(post):
100 content = ''
101 content += str(json.dumps(post['frontmatter'], indent = 4))
102 content += '\n\n'
103 content += str(post['body'])
104 return content
105
106def define_post_filename(post):
107 folder = 'hugo/content/posts'
108 slug = str(post['frontmatter']['sequence'])
109 filetype = 'md'
110 return folder + '/' + slug + '.' + filetype
111
112def write_post_file(post):
113 os.makedirs(os.path.dirname(define_post_filename(post)), exist_ok=True)
114 with open(define_post_filename(post), 'w') as f:
115 f.write (format_post_file(post))
116
117def write_posts_from_user(ssb_userid):
118 posts = get_user_posts(ssb_userid)
119 for post in posts:
120 write_post_file(build_post_structure(post))
121
122
123def get_user_metadata(ssb_userid):
124 user_metadata_args = ['sbot', 'links',
125 '--source', ssb_userid,
126 '--dest', ssb_userid,
127 '--rel', 'about',
128 '--values']
129 json_metadata_args = ['json', '--deep-merge',
130 '-c', 'this.value.content.type == "about"']
131 user_metadata_stream = subprocess.Popen(user_metadata_args,
132 stdout = subprocess.PIPE)
133 user_metadata_json = subprocess.check_output(json_metadata_args,
134 stdin = user_metadata_stream.stdout)
135 user_metadata = json.loads(user_metadata_json)
136 return user_metadata
137
138def check_hugo_theme(theme, theme_clone_url):
139 hugo_theme_dir = os.path.join(scuttleblog_dir, 'hugo', 'themes', theme)
140 if not os.path.isdir(hugo_theme_dir):
141 subprocess.run(['git', 'submodule', 'add', '-f',
142 theme_clone_url, hugo_theme_dir])
143
144def build_hugo_config(m):
145 hugo_config = {}
146 hugo_config['baseurl'] = conf.hugo_baseurl
147 hugo_config['theme'] = conf.hugo_theme
148 hugo_config['permalinks'] = {}
149 hugo_config['permalinks']['post'] = '/:filename/'
150 if 'name' in m['value']['content']:
151 hugo_config['title'] = m['value']['content']['name']
152 hugo_config['params'] = {}
153 if 'description' in m['value']['content']:
154 description = m['value']['content']['description']
155 hugo_config['params']['subtitle'] = description.replace('\n', ' ')
156 return (hugo_config)
157
158def write_hugo_config_from_user(ssb_userid):
159 metadata = get_user_metadata(ssb_userid)
160 for file in ['hugo/config.toml', 'hugo/config.yaml']:
161 if os.path.exists(file):
162 os.remove(file)
163 with open('hugo/config.json', 'w') as f:
164 f.write (json.dumps(build_hugo_config(metadata), indent = 4))
165
166def run_hugo():
167 subprocess.run(['hugo', '-s', 'hugo'])
168
169run_sbot()
170write_posts_from_user(conf.ssb_userid)
171write_hugo_config_from_user(conf.ssb_userid)
172check_hugo_theme(conf.hugo_theme, conf.hugo_theme_clone_url)
173run_hugo()
174
175

Built with git-ssb-web