File size: 4,969 Bytes
12ac8ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c5aea5
12ac8ff
2c5aea5
12ac8ff
afcd954
12ac8ff
 
 
 
 
 
2c5aea5
12ac8ff
 
 
 
 
 
2c5aea5
12ac8ff
 
 
 
2c5aea5
afcd954
 
12ac8ff
2c5aea5
12ac8ff
afcd954
 
 
 
 
 
12ac8ff
2c5aea5
ecf2c66
12ac8ff
ecf2c66
2c5aea5
12ac8ff
 
2c5aea5
 
 
 
 
 
 
ecf2c66
12ac8ff
2c5aea5
ec8c142
 
12ac8ff
ec8c142
 
12ac8ff
 
15e2717
 
ec8c142
15e2717
 
 
 
ec8c142
12ac8ff
15e2717
e2c8dd5
 
6440d27
afcd954
e2c8dd5
 
6440d27
e2c8dd5
 
2c5aea5
12ac8ff
 
 
 
 
 
afcd954
 
38cf376
2c5aea5
 
12ac8ff
afcd954
12ac8ff
afcd954
 
2f8f693
 
12ac8ff
afcd954
2c5aea5
ecf2c66
12ac8ff
afcd954
 
ecf2c66
12ac8ff
2c5aea5
afcd954
12ac8ff
2c5aea5
 
12ac8ff
 
 
afcd954
12ac8ff
 
 
2c5aea5
12ac8ff
 
 
2c5aea5
12ac8ff
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import os
import json
import hashlib
import time
import re
from pathlib import Path

import requests

PUBLISHED_FILE = "published_posts.json"
GH_PAGES_BASE = "https://kagvi13.github.io/HMP/"

HASHNODE_TOKEN = os.environ["HASHNODE_TOKEN"]
HASHNODE_PUBLICATION_ID = os.environ["HASHNODE_PUBLICATION_ID"]
API_URL = "https://gql.hashnode.com"


def convert_md_links(md_text: str) -> str:
    """Конвертирует относительные ссылки (*.md) в абсолютные ссылки на GitHub Pages."""
    def replacer(match):
        text, link = match.groups()
        if link.startswith("http://") or link.startswith("https://") or not link.endswith(".md"):
            return match.group(0)
        abs_link = GH_PAGES_BASE + link.replace(".md", "").lstrip("./")
        return f"[{text}]({abs_link})"
    return re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replacer, md_text)


def load_published():
    if Path(PUBLISHED_FILE).exists():
        with open(PUBLISHED_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}


def save_published(data):
    with open(PUBLISHED_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)


def file_hash(md_text: str):
    return hashlib.md5(md_text.encode("utf-8")).hexdigest()


def graphql_request(query, variables):
    headers = {"Authorization": f"Bearer {HASHNODE_TOKEN}", "Content-Type": "application/json"}
    resp = requests.post(API_URL, json={"query": query, "variables": variables}, headers=headers)
    data = resp.json()
    if "errors" in data:
        raise Exception(f"GraphQL errors: {data['errors']}")
    return data


def create_post(title, slug, markdown_content):
    query = """
    mutation CreateDraft($input: CreateDraftInput!) {
      createDraft(input: $input) { draft { id slug title } }
    }
    """
    variables = {
        "input": {
            "title": title,
            "contentMarkdown": markdown_content,
            "publicationId": HASHNODE_PUBLICATION_ID
        }
    }
    return graphql_request(query, variables)["data"]["createDraft"]["draft"]


def update_post(post_id, title, markdown_content):
    """Обновляем уже опубликованный пост."""
    query = """
    mutation UpdatePost($input: UpdatePostInput!) {
      updatePost(input: $input) { post { id slug title } }
    }
    """
    variables = {
        "input": {
            "id": post_id,
            "title": title,
            "contentMarkdown": markdown_content
        }
    }
    return graphql_request(query, variables)["data"]["updatePost"]["post"]


def publish_draft(draft_id):
    query = """
    mutation PublishDraft($input: PublishDraftInput!) {
      publishDraft(input: $input) { post { id slug url } }
    }
    """
    variables = {"input": {"draftId": draft_id}}
    return graphql_request(query, variables)["data"]["publishDraft"]["post"]


def main(force=False):
    published = load_published()
    md_files = list(Path("docs").rglob("*.md"))

    for md_file in md_files:
        name = md_file.stem
        title = name if len(name) >= 6 else name + "-HMP"
        slug = re.sub(r'[^a-z0-9-]', '-', title.lower()).strip('-')[:250]

        md_text = f"Источник: [ {md_file.name} ](https://github.com/kagvi13/HMP/blob/main/docs/{md_file.name})\n\n" \
                  + md_file.read_text(encoding="utf-8")
        md_text = convert_md_links(md_text)
        h = file_hash(md_text)

        if not force and name in published and published[name].get("hash") == h:
            print(f"✅ Пост '{name}' без изменений — пропускаем.")
            continue

        try:
            if name in published and "id" in published[name]:
                post = update_post(published[name]["id"], title, md_text)
                print(f"♻ Обновлён пост: https://hashnode.com/@yourusername/{post['slug']}")
            else:
                draft = create_post(title, slug, md_text)
                post = publish_draft(draft["id"])
                print(f"🆕 Пост опубликован: https://hashnode.com/@yourusername/{post['slug']}")

            # Обновляем локальный JSON после публикации/обновления
            published[name] = {"id": post["id"], "slug": post["slug"], "hash": h}
            save_published(published)

            print("⏱ Пауза 30 секунд перед следующим постом...")
            time.sleep(30)

        except Exception as e:
            print(f"❌ Ошибка при публикации {name}: {e}")
            save_published(published)
            break


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("--force", action="store_true", help="Обновить все посты, даже без изменений")
    args = parser.parse_args()
    main(force=args.force)