GitHub Action
commited on
Commit
·
2c5aea5
1
Parent(s):
1595318
Sync from GitHub with Git LFS
Browse files- scripts/publish_to_hashnode.py +28 -11
scripts/publish_to_hashnode.py
CHANGED
@@ -14,7 +14,9 @@ HASHNODE_TOKEN = os.environ["HASHNODE_TOKEN"]
|
|
14 |
HASHNODE_PUBLICATION_ID = os.environ["HASHNODE_PUBLICATION_ID"]
|
15 |
API_URL = "https://gql.hashnode.com"
|
16 |
|
|
|
17 |
def convert_md_links(md_text: str) -> str:
|
|
|
18 |
def replacer(match):
|
19 |
text, link = match.groups()
|
20 |
if link.startswith("http://") or link.startswith("https://") or not link.endswith(".md"):
|
@@ -23,19 +25,23 @@ def convert_md_links(md_text: str) -> str:
|
|
23 |
return f"[{text}]({abs_link})"
|
24 |
return re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replacer, md_text)
|
25 |
|
|
|
26 |
def load_published():
|
27 |
if Path(PUBLISHED_FILE).exists():
|
28 |
with open(PUBLISHED_FILE, "r", encoding="utf-8") as f:
|
29 |
return json.load(f)
|
30 |
return {}
|
31 |
|
|
|
32 |
def save_published(data):
|
33 |
with open(PUBLISHED_FILE, "w", encoding="utf-8") as f:
|
34 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
35 |
|
|
|
36 |
def file_hash(md_text: str):
|
37 |
return hashlib.md5(md_text.encode("utf-8")).hexdigest()
|
38 |
|
|
|
39 |
def graphql_request(query, variables):
|
40 |
headers = {"Authorization": f"Bearer {HASHNODE_TOKEN}", "Content-Type": "application/json"}
|
41 |
resp = requests.post(API_URL, json={"query": query, "variables": variables}, headers=headers)
|
@@ -44,24 +50,29 @@ def graphql_request(query, variables):
|
|
44 |
raise Exception(f"GraphQL errors: {data['errors']}")
|
45 |
return data
|
46 |
|
|
|
47 |
def create_post(title, slug, markdown_content):
|
48 |
query = """
|
49 |
mutation CreateDraft($input: CreateDraftInput!) {
|
50 |
-
createDraft(input: $input) {
|
51 |
-
draft { id slug title }
|
52 |
-
}
|
53 |
}
|
54 |
"""
|
55 |
-
variables = {
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
return graphql_request(query, variables)["data"]["createDraft"]["draft"]
|
58 |
|
|
|
59 |
def update_post(draft_id, title, markdown_content):
|
|
|
60 |
query = """
|
61 |
mutation UpdateDraft($input: UpdateDraftInput!) {
|
62 |
-
updateDraft(input: $input) {
|
63 |
-
draft { id slug title }
|
64 |
-
}
|
65 |
}
|
66 |
"""
|
67 |
variables = {
|
@@ -83,6 +94,7 @@ def publish_draft(draft_id):
|
|
83 |
variables = {"input": {"draftId": draft_id}}
|
84 |
return graphql_request(query, variables)["data"]["publishDraft"]["post"]
|
85 |
|
|
|
86 |
def main(force=False):
|
87 |
published = load_published()
|
88 |
md_files = list(Path("docs").rglob("*.md"))
|
@@ -92,7 +104,8 @@ def main(force=False):
|
|
92 |
title = name if len(name) >= 6 else name + "-HMP"
|
93 |
slug = re.sub(r'[^a-z0-9-]', '-', title.lower()).strip('-')[:250]
|
94 |
|
95 |
-
md_text = f"Источник: [ {md_file.name} ](https://github.com/kagvi13/HMP/blob/main/docs/{md_file.name})\n\n"
|
|
|
96 |
md_text = convert_md_links(md_text)
|
97 |
h = file_hash(md_text)
|
98 |
|
@@ -102,15 +115,18 @@ def main(force=False):
|
|
102 |
|
103 |
try:
|
104 |
if name in published and "id" in published[name]:
|
105 |
-
update_post(published[name]["id"], title, md_text)
|
106 |
print(f"♻ Обновлён пост: https://hashnode.com/@yourusername/{post['slug']}")
|
107 |
else:
|
108 |
draft = create_post(title, slug, md_text)
|
109 |
post = publish_draft(draft["id"])
|
110 |
print(f"🆕 Пост опубликован: https://hashnode.com/@yourusername/{post['slug']}")
|
111 |
|
|
|
112 |
published[name] = {"id": post["id"], "slug": post["slug"], "hash": h}
|
113 |
save_published(published)
|
|
|
|
|
114 |
time.sleep(30)
|
115 |
|
116 |
except Exception as e:
|
@@ -118,9 +134,10 @@ def main(force=False):
|
|
118 |
save_published(published)
|
119 |
break
|
120 |
|
|
|
121 |
if __name__ == "__main__":
|
122 |
import argparse
|
123 |
parser = argparse.ArgumentParser()
|
124 |
-
parser.add_argument("--force", action="store_true")
|
125 |
args = parser.parse_args()
|
126 |
main(force=args.force)
|
|
|
14 |
HASHNODE_PUBLICATION_ID = os.environ["HASHNODE_PUBLICATION_ID"]
|
15 |
API_URL = "https://gql.hashnode.com"
|
16 |
|
17 |
+
|
18 |
def convert_md_links(md_text: str) -> str:
|
19 |
+
"""Конвертирует относительные ссылки (*.md) в абсолютные ссылки на GitHub Pages."""
|
20 |
def replacer(match):
|
21 |
text, link = match.groups()
|
22 |
if link.startswith("http://") or link.startswith("https://") or not link.endswith(".md"):
|
|
|
25 |
return f"[{text}]({abs_link})"
|
26 |
return re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replacer, md_text)
|
27 |
|
28 |
+
|
29 |
def load_published():
|
30 |
if Path(PUBLISHED_FILE).exists():
|
31 |
with open(PUBLISHED_FILE, "r", encoding="utf-8") as f:
|
32 |
return json.load(f)
|
33 |
return {}
|
34 |
|
35 |
+
|
36 |
def save_published(data):
|
37 |
with open(PUBLISHED_FILE, "w", encoding="utf-8") as f:
|
38 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
39 |
|
40 |
+
|
41 |
def file_hash(md_text: str):
|
42 |
return hashlib.md5(md_text.encode("utf-8")).hexdigest()
|
43 |
|
44 |
+
|
45 |
def graphql_request(query, variables):
|
46 |
headers = {"Authorization": f"Bearer {HASHNODE_TOKEN}", "Content-Type": "application/json"}
|
47 |
resp = requests.post(API_URL, json={"query": query, "variables": variables}, headers=headers)
|
|
|
50 |
raise Exception(f"GraphQL errors: {data['errors']}")
|
51 |
return data
|
52 |
|
53 |
+
|
54 |
def create_post(title, slug, markdown_content):
|
55 |
query = """
|
56 |
mutation CreateDraft($input: CreateDraftInput!) {
|
57 |
+
createDraft(input: $input) { draft { id slug title } }
|
|
|
|
|
58 |
}
|
59 |
"""
|
60 |
+
variables = {
|
61 |
+
"input": {
|
62 |
+
"title": title,
|
63 |
+
"contentMarkdown": markdown_content,
|
64 |
+
"slug": slug,
|
65 |
+
"publicationId": HASHNODE_PUBLICATION_ID
|
66 |
+
}
|
67 |
+
}
|
68 |
return graphql_request(query, variables)["data"]["createDraft"]["draft"]
|
69 |
|
70 |
+
|
71 |
def update_post(draft_id, title, markdown_content):
|
72 |
+
"""Используем draftId для обновления, чтобы соответствовать текущему API Hashnode."""
|
73 |
query = """
|
74 |
mutation UpdateDraft($input: UpdateDraftInput!) {
|
75 |
+
updateDraft(input: $input) { draft { id slug title } }
|
|
|
|
|
76 |
}
|
77 |
"""
|
78 |
variables = {
|
|
|
94 |
variables = {"input": {"draftId": draft_id}}
|
95 |
return graphql_request(query, variables)["data"]["publishDraft"]["post"]
|
96 |
|
97 |
+
|
98 |
def main(force=False):
|
99 |
published = load_published()
|
100 |
md_files = list(Path("docs").rglob("*.md"))
|
|
|
104 |
title = name if len(name) >= 6 else name + "-HMP"
|
105 |
slug = re.sub(r'[^a-z0-9-]', '-', title.lower()).strip('-')[:250]
|
106 |
|
107 |
+
md_text = f"Источник: [ {md_file.name} ](https://github.com/kagvi13/HMP/blob/main/docs/{md_file.name})\n\n" \
|
108 |
+
+ md_file.read_text(encoding="utf-8")
|
109 |
md_text = convert_md_links(md_text)
|
110 |
h = file_hash(md_text)
|
111 |
|
|
|
115 |
|
116 |
try:
|
117 |
if name in published and "id" in published[name]:
|
118 |
+
post = update_post(published[name]["id"], title, md_text)
|
119 |
print(f"♻ Обновлён пост: https://hashnode.com/@yourusername/{post['slug']}")
|
120 |
else:
|
121 |
draft = create_post(title, slug, md_text)
|
122 |
post = publish_draft(draft["id"])
|
123 |
print(f"🆕 Пост опубликован: https://hashnode.com/@yourusername/{post['slug']}")
|
124 |
|
125 |
+
# Обновляем локальный JSON после публикации/обновления
|
126 |
published[name] = {"id": post["id"], "slug": post["slug"], "hash": h}
|
127 |
save_published(published)
|
128 |
+
|
129 |
+
print("⏱ Пауза 30 секунд перед следующим постом...")
|
130 |
time.sleep(30)
|
131 |
|
132 |
except Exception as e:
|
|
|
134 |
save_published(published)
|
135 |
break
|
136 |
|
137 |
+
|
138 |
if __name__ == "__main__":
|
139 |
import argparse
|
140 |
parser = argparse.ArgumentParser()
|
141 |
+
parser.add_argument("--force", action="store_true", help="Обновить все посты, даже без изменений")
|
142 |
args = parser.parse_args()
|
143 |
main(force=args.force)
|