Backup dos posts da rede Hive e Steemit para markdown

Disclaimer for those who don’t speak Portuguese: I will soon post a more complete Portuguese/English post discussing ways to use the script.

É vamos ver se o Spamminator não vai atrapalhar o alcance do post já que sempre é bom ler as dicas e críticas de outras pessoas sobre um código aberto. Apesar, de, no fim, o código estar em GPL, portanto, é só irem ao Github que vai conter todas estas ferramentas e mandar o bom e velho PR.

Mas, como eu falei a uns dias atrás eu havia voltado a estudar um pouco a API aqui da rede Hive para retornar os backups dos meus posts já que eu pretendo voltar a fazer o backup/mirror do meu conteúdo no meu blog.

Sei que é muito difícil a rede aqui cair mas … aquilo né. Sempre é bom manter o conteúdo na nossa mão caso o projeto deixe de existir.

E para isto eu criei um script que faz diversas coisas interessantes. Ele baixa os posts aqui da rede Hive e também as imagens nestes posts para o disco local e cria um post pronto para ser usado no Hugo ( com o tempo pretendo colocar também a função para o Jekyll ).

Ele no momento é uma linha de comando e facilita muito a inseri-lo em um cronjob na sua máquina ou VPS. No meu caso ele roda aqui em um servidor na minha rede, baixa o conteúdo e outro job cria o post e o PULL REQUEST no repositório do meu blog no Github.

#!/usr/bin/python
# -*- coding: utf-8 -*-

from beem import Hive
from beem.account import Account
import os
import io
import argparse
import requests
import uuid
from urllib.parse import urlparse
import re
from datetime import datetime, timedelta

def download_image(image_url, path):
    try:
        # Fazer o download da imagem
        response = requests.get(image_url)
        if response.status_code == 200:
            # Extrair a extensão do arquivo
            parsed_url = urlparse(image_url)
            _, ext = os.path.splitext(parsed_url.path)

            # Gerar um nome de arquivo único com UUID
            unique_filename = f"{uuid.uuid4()}{ext}"
            file_path = os.path.join(path, unique_filename)

            # Salvar a imagem no disco
            with open(file_path, 'wb') as f:
                f.write(response.content)

            print(f"Imagem baixada e salva como: {file_path}")
            return unique_filename
        else:
            print(f"Erro ao baixar a imagem: {image_url} (Status Code: {response.status_code})")
            return None
    except Exception as e:
        print(f"Erro ao processar a imagem {image_url}: {e}")
        return None

def extract_images_from_markdown(markdown_content):
    # Procurar por imagens no formato ![alt](image_url)
    image_urls = re.findall(r'!\[.*?\]\((.*?)\)', markdown_content)
    return image_urls

def main(author, path, last=False, include_actifit=False, all_posts=False, today=False, platform="hive"):
    # Escolher a blockchain com base na plataforma
    if platform == "hive":
        node_url = "https://api.hive.blog"
    else:  # steemit
        node_url = "https://api.steemit.com"

    # Conectar à blockchain Hive ou Steemit
    hive = Hive(node=node_url)
    account = Account(author, blockchain_instance=hive)
    
    # Data de ontem e hoje
    yesterday = (datetime.utcnow() - timedelta(days=1)).date()
    today_date = datetime.utcnow().date()

    # Obter os posts da conta
    posts = account.get_blog(limit=500)  # Ajuste o limite conforme necessário
    
    if last:
        # Pega o último post apenas
        posts = [posts[0]] if posts else []
    
    # Processar cada post
    for post in posts:
        if post["author"] != author:
            continue
        
        # Verificar se a tag 'actifit' está no post
        if 'actifit' in post.get('json_metadata', {}).get('tags', []):
            if not include_actifit:
                print(f"Post ignorado devido à tag 'actifit': {post['title']}")
                continue
        
        # Usar o campo created diretamente como datetime
        post_date = post["created"].date()
        
        # Condicionais para --all, --today e posts de ontem
        if not all_posts:
            if today:
                if post_date != today_date:
                    continue
            else:
                if post_date != yesterday:
                    continue
        
        markdown_content = post['body']
        title = post['title']
        permlink = post['permlink']
        link_for_post = f'https://{platform}.blog/@{author}/{permlink}'
        
        # Baixar imagens e substituir os links no markdown
        images = post.get('json_metadata', {}).get('image', [])
        
        if images:
            print(f"Imagens encontradas no post (json_metadata): {images}")
        
        # Extrair imagens do markdown
        markdown_images = extract_images_from_markdown(markdown_content)
        
        if markdown_images:
            print(f"Imagens encontradas no markdown: {markdown_images}")

        # Baixar todas as imagens encontradas no json_metadata e no markdown
        all_images = images + markdown_images
        for image_url in all_images:
            downloaded_image_name = download_image(image_url, path)
            if downloaded_image_name:
                markdown_content = markdown_content.replace(image_url, downloaded_image_name)
        
        post_final = f'---\n<br />**Postado originalmente na rede {platform.capitalize()}: [{link_for_post}]({link_for_post})** <br />\n----'
        yaml_prefix = '---\n'
        TitleYaml = title.replace(':', '').replace('\'', '').replace('#', '').replace('(', '').replace(')', '')
        
        # Construir o prefixo YAML
        yaml_prefix += f'title: {TitleYaml}\n'
        yaml_prefix += f'date: {post["created"]}\n'
        yaml_prefix += f'permlink: /{platform}/{permlink}\n'
        yaml_prefix += 'type: posts\n'
        yaml_prefix += f'categories: ["{platform.capitalize()}"]\n'
        yaml_prefix += f'author: {author}\n---\n'
        
        # Nome do arquivo
        filename = os.path.join(path, f"{post_date}_{permlink}.md")
       
        # Salvar o conteúdo em um arquivo Markdown
        with io.open(filename, "w", encoding="utf-8") as f:
            f.write(yaml_prefix + markdown_content + post_final)
        
        print(f"Post salvo: {filename}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("author", help="Nome da conta na Hive ou Steemit")
    parser.add_argument("path", help="Caminho onde os arquivos Markdown serão salvos")
    parser.add_argument("--last", action="store_true", help="Pega o último post somente")
    parser.add_argument("--actifit", action="store_true", help="Inclui posts com a tag 'actifit'")
    parser.add_argument("--all", action="store_true", help="Pega todos os posts, ignorando o filtro de data")
    parser.add_argument("--today", action="store_true", help="Pega apenas os posts de hoje")
    parser.add_argument("--steemit", action="store_true", help="Usar a rede Steemit em vez da Hive")
    
    args = parser.parse_args()
    
    # Definir a plataforma (Hive ou Steemit)
    platform = "steemit" if args.steemit else "hive"
    
    main(args.author, args.path, args.last, args.actifit, args.all, args.today, platform)

A idéia disto surgiu porque por mais que a rede esteja distribuída ainda dependemos de pontos centrais para que tenhamos acesso ao conteúdo. E assim, dependendo do que ocorrer principalmente em um país como o nosso em que alguns juízes se acham semideuses.

O processo é bem simples. O script vai na API da rede Hive, busca todos os posts de um usuário específicio e manda o conteúdo markdown dos nossos posts aqui para um diretório.

Como vira e mexe também fazemos a inserção de imagens, este script pega a imagem, efetua o download da mesma para o disco local e reescreve o endereço da imagem no markdown para apontar para o disco local.

Isto é bom pois o meu script anterior não fazia isto e, este processo eu tinha que efetuar manualmente. E imagine isto para diversos posts em uma semana. Como eu era muito mais ativo aqui isto atrapalhava um pouco.

Mas espero que esta primeira versão já ajude. Tenho algumas idéias para o próximo passo, que seria ele baixar o post e também inserir as tags no cabeçalho do post do Hugo. E lógico, em breve, também criar o esquema para que ele consiga gerar arquivos para o Jekyll ( apesar de, caso necessário, a pessoa possa usar os scripts do próprio Jekyll para migrar o post para o formato que ele utiliza ).

Uso do script

O uso do script é muito fácil. Aqui deixo inclusive alguns exemplos de como podemos utilizá-lo no dia a dia.

Temos alguns parâmetros que foram inseridos no script:

  • --last - este parâmetro pega somente o último post
  • --actifit. - pega todos os posts incluindo aqueles que tenham a tag actifit. Por padrão o script não pega estes posts e inclui no backup.
  • --all pega todo os posts. Por padrão o script pega somente os posts de ontem
  • --steemit quando este parâmetro é inserido ele passa a baixar os posts da rede Steemit ( breve pretendo colocar as outras redes irmãs também )

Exemplos de uso:

Para pegar todos os posts:

venv/bin/python hive_posts_yesterday_to_md.py ataliba hive --all

Para pegar apenas os posts de hoje:

venv/bin/python hive_posts_yesterday_to_md.py ataliba hive --today

Para pegar todos os posts, incluindo os com actifit:

venv/bin/python hive_posts_yesterday_to_md.py ataliba hive --all --actifit

Para pegar somente o último post de hoje:

venv/bin/python hive_posts_yesterday_to_md.py ataliba hive --last --today

Imagem direto do Pixabay


Postado originalmente na rede Hive: https://hive.blog/@ataliba/backup-dos-posts-da-rede