#!/usr/bin/python3 __copyright__ = """ Scuttleblog Copyright (C) 2017 Greg K Nicholson This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . """ import datetime import json import os import re import time import subprocess import conf scuttleblog_dir = os.path.dirname(__file__) def run_sbot(): for attempt in range(100): try: subprocess.check_output(['sbot', 'status']) except: pid = subprocess.Popen(['sbot', 'server']).pid time.sleep(1) else: break def get_user_posts(ssb_userid): user_posts_args = ['sbot', 'createUserStream', '--id', ssb_userid] json_posts_args = ['json', '--group', \ '-c', 'this.value.content.type == "post"', \ '-c', 'this.value.content.text != null', \ '-c', 'this.value.content.text != ""', \ '-c', 'this.value.content.root == null'] user_posts_stream = subprocess.Popen(user_posts_args, stdout = subprocess.PIPE) user_posts_json = subprocess.check_output(json_posts_args, stdin = user_posts_stream.stdout) user_posts = json.loads(user_posts_json) return user_posts def define_post_text(text): text = text.strip() title = text.splitlines()[0] if re.match('#', text): # The first line is a heading. # Use it as the title and strip it from the body. if len(text.splitlines()) > 1: body = ''.join(text.splitlines(keepends=True)[1:]).strip() else: body = '' else: # Truncate the first line and use it as the title. # Keep everything in the body. maxlength = 100 ellipsis = '...' if len(title) > maxlength: title = title[:maxlength].rsplit(' ', 1)[0] + ellipsis body = text title = re.sub('^#+\s+', '', title) return {'title': title, 'body': body} def build_post_structure(p): post = {} post['frontmatter'] = {} post['frontmatter']['key'] = p['key'] post['frontmatter']['title'] = define_post_text( p['value']['content']['text'])['title'] time = datetime.datetime.fromtimestamp(int(p['value']['timestamp'] / 1000), datetime.timezone.utc) post['frontmatter']['date'] = time.strftime('%Y-%m-%dT%H:%M:%SZ') post['frontmatter']['sequence'] = p['value']['sequence'] post['body'] = define_post_text(p['value']['content']['text'])['body'] return (post) def format_post_file(post): content = '' content += str(json.dumps(post['frontmatter'], indent = 4)) content += '\n\n' content += str(post['body']) return content def define_post_filename(post): folder = 'hugo/content/posts' slug = str(post['frontmatter']['key']) filetype = 'md' return folder + '/' + slug + '.' + filetype def write_post_file(post): os.makedirs(os.path.dirname(define_post_filename(post)), exist_ok=True) with open(define_post_filename(post), 'w') as f: f.write (format_post_file(post)) def write_posts_from_user(ssb_userid): posts = get_user_posts(ssb_userid) for post in posts: write_post_file(build_post_structure(post)) def get_user_metadata(ssb_userid): user_metadata_args = ['sbot', 'links', '--source', ssb_userid, '--dest', ssb_userid, '--rel', 'about', '--values'] json_metadata_args = ['json', '--deep-merge', '-c', 'this.value.content.type == "about"'] user_metadata_stream = subprocess.Popen(user_metadata_args, stdout = subprocess.PIPE) user_metadata_json = subprocess.check_output(json_metadata_args, stdin = user_metadata_stream.stdout) user_metadata = json.loads(user_metadata_json) return user_metadata def check_hugo_theme(theme, theme_clone_url): hugo_theme_dir = os.path.join(scuttleblog_dir, 'hugo', 'themes', theme) if not os.path.isdir(hugo_theme_dir): subprocess.run(['git', 'submodule', 'add', '-f', theme_clone_url, hugo_theme_dir]) def build_hugo_config(m): hugo_config = {} hugo_config['baseurl'] = conf.hugo_baseurl hugo_config['theme'] = conf.hugo_theme hugo_config['permalinks'] = {} hugo_config['permalinks']['post'] = '/:filename/' if 'name' in m['value']['content']: hugo_config['title'] = m['value']['content']['name'] hugo_config['params'] = {} if 'description' in m['value']['content']: description = m['value']['content']['description'] hugo_config['params']['subtitle'] = description.replace('\n', ' ') return (hugo_config) def write_hugo_config_from_user(ssb_userid): metadata = get_user_metadata(ssb_userid) for file in ['hugo/config.toml', 'hugo/config.yaml']: if os.path.exists(file): os.remove(file) with open('hugo/config.json', 'w') as f: f.write (json.dumps(build_hugo_config(metadata), indent = 4)) def run_hugo(): subprocess.run(['hugo', '-s', 'hugo']) run_sbot() write_posts_from_user(conf.ssb_userid) write_hugo_config_from_user(conf.ssb_userid) check_hugo_theme(conf.hugo_theme, conf.hugo_theme_clone_url) run_hugo()