Compare commits
4 Commits
89dc3afa12
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f4f109f75d | |||
| 7f402cd69b | |||
| ed880bdd57 | |||
| 08463e18d0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.env
|
.env
|
||||||
output/*.md
|
output/*.md
|
||||||
|
output/*.txt
|
||||||
@@ -1,22 +1,32 @@
|
|||||||
You are writing a public-facing blog post on behalf of our development team. The post is based strictly on the content of our internal meeting notes — do not add any information, claims, or details that are not present in the source material.
|
# Task
|
||||||
|
|
||||||
## Tone and style
|
- Write a public-facing blog post on behalf of a development team.
|
||||||
|
- Rely strictly on internal meeting notes.
|
||||||
|
- Do not add any outside information or unverified claims.
|
||||||
|
|
||||||
Write in a warm, positive, and conversational tone. The audience is people who are interested in our project but are not necessarily technical experts. Avoid jargon where possible; when technical terms are necessary, briefly explain them in plain language.
|
# Style
|
||||||
|
|
||||||
The post should feel like an honest and enthusiastic update from a team that is proud of what they are building — not a press release or a sales pitch.
|
- Warm and enthusiastic tone.
|
||||||
|
- Concise, filler-free phrasing.
|
||||||
|
- Meaningful information in every sentence.
|
||||||
|
- Target audience is technical; use industry jargon.
|
||||||
|
- Do not explain basic concepts.
|
||||||
|
|
||||||
## Structure
|
# Content
|
||||||
|
|
||||||
- Start with a short, engaging opening paragraph that sets the context and draws the reader in.
|
- Address every point in the source material.
|
||||||
- The body should walk through what the team has been working on, what progress has been made, and what challenges or decisions came up — all based on the meeting notes.
|
- Guessing or speculating is strictly prohibited.
|
||||||
- End with a forward-looking closing paragraph that hints at what is coming next, based only on what the notes mention.
|
- If information is missing or unclear, do not invent details.
|
||||||
|
- Professional pride, not a marketing pitch or press release.
|
||||||
|
- Short, punchy opening to set the context.
|
||||||
|
- Body: completed work, progress, decisions, and challenges.
|
||||||
|
- Closing: future steps based only on the source material.
|
||||||
|
|
||||||
## Formatting
|
# Formatting
|
||||||
|
|
||||||
- Use Markdown.
|
- Use Markdown.
|
||||||
- Use a single `#` heading for the title. Make the title specific and descriptive — not generic like "Project Update".
|
- Single `#` for the title (specific and technical).
|
||||||
- Use `##` subheadings to break up longer posts if needed.
|
- Use `##` for subheadings.
|
||||||
- Keep paragraphs short and readable — 3 to 5 sentences each.
|
- Short paragraphs (3-5 sentences each).
|
||||||
- Do not use bullet points or numbered lists in the body. Write in flowing prose.
|
- No bullet points or lists in the body text; write in flowing prose.
|
||||||
- Aim for 300 to 500 words in total.
|
- Total length: 300-500 words.
|
||||||
|
|||||||
26
Makefile
26
Makefile
@@ -1,23 +1,27 @@
|
|||||||
ENV = export $(shell cat .env | grep -v '^\#' | grep -v '^$$' | xargs)
|
ENV = export $(shell cat .env | grep -v '^\#' | grep -v '^$$' | xargs)
|
||||||
|
|
||||||
.PHONY: fetch write translate upload all
|
.PHONY: fetch write translate upload clean all
|
||||||
|
|
||||||
## Letölt egy wiki oldalt SOURCE.md-be
|
## Downloads a wiki page into SOURCE.md
|
||||||
## Használat: make fetch URL=/path/to/page
|
## Usage: make fetch URL=/path/to/page
|
||||||
fetch:
|
fetch:
|
||||||
$(ENV) && python3 generator.py fetch $(URL)
|
@$(ENV) && python3 generator.py fetch $(URL)
|
||||||
|
|
||||||
## Blogposztot ír SOURCE.md-ből → BLOGPOST.md
|
## Writes a blog post from SOURCE.md → BLOGPOST.md
|
||||||
write:
|
write:
|
||||||
$(ENV) && python3 generator.py write
|
@$(ENV) && python3 generator.py write
|
||||||
|
|
||||||
## Lefordítja BLOGPOST.md → TRANSLATED_BLOGPOST.md
|
## Translates BLOGPOST.md → TRANSLATED_BLOGPOST.md
|
||||||
translate:
|
translate:
|
||||||
$(ENV) && python3 generator.py translate
|
@$(ENV) && python3 generator.py translate
|
||||||
|
|
||||||
## Feltölti TRANSLATED_BLOGPOST.md-t a wikire
|
## Uploads TRANSLATED_BLOGPOST.md to the wiki
|
||||||
upload:
|
upload:
|
||||||
$(ENV) && python3 generator.py upload
|
@$(ENV) && python3 generator.py upload
|
||||||
|
|
||||||
## Teljes pipeline: write → translate → upload
|
## Deletes .md files from the output directory
|
||||||
|
clean:
|
||||||
|
@$(ENV) && python3 generator.py clean
|
||||||
|
|
||||||
|
## Full pipeline: write → translate → upload
|
||||||
all: write translate upload
|
all: write translate upload
|
||||||
49
generator.py
49
generator.py
@@ -30,6 +30,7 @@ import urllib.error
|
|||||||
|
|
||||||
OUTPUT_DIR = "output"
|
OUTPUT_DIR = "output"
|
||||||
SOURCE_FILE = os.path.join(OUTPUT_DIR, "SOURCE.md")
|
SOURCE_FILE = os.path.join(OUTPUT_DIR, "SOURCE.md")
|
||||||
|
SOURCE_TITLE_FILE = os.path.join(OUTPUT_DIR, "SOURCE_TITLE.txt")
|
||||||
BLOGPOST_FILE = os.path.join(OUTPUT_DIR, "BLOGPOST.md")
|
BLOGPOST_FILE = os.path.join(OUTPUT_DIR, "BLOGPOST.md")
|
||||||
TRANSLATED_FILE = os.path.join(OUTPUT_DIR, "TRANSLATED_BLOGPOST.md")
|
TRANSLATED_FILE = os.path.join(OUTPUT_DIR, "TRANSLATED_BLOGPOST.md")
|
||||||
INSTRUCTIONS_FILE = "INSTRUCTIONS.md"
|
INSTRUCTIONS_FILE = "INSTRUCTIONS.md"
|
||||||
@@ -83,9 +84,9 @@ query ($path: String!) {
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
MUTATION_UPDATE_PAGE = """
|
MUTATION_UPDATE_PAGE = """
|
||||||
mutation ($id: Int!, $content: String!) {
|
mutation ($id: Int!, $content: String!, $description: String!) {
|
||||||
pages {
|
pages {
|
||||||
update(id: $id, content: $content, tags: ["blog"]) {
|
update(id: $id, content: $content, description: $description, tags: ["blog"]) {
|
||||||
responseResult { succeeded message }
|
responseResult { succeeded message }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +94,7 @@ mutation ($id: Int!, $content: String!) {
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
MUTATION_CREATE_PAGE = """
|
MUTATION_CREATE_PAGE = """
|
||||||
mutation ($path: String!, $title: String!, $content: String!) {
|
mutation ($path: String!, $title: String!, $content: String!, $description: String!) {
|
||||||
pages {
|
pages {
|
||||||
create(
|
create(
|
||||||
path: $path
|
path: $path
|
||||||
@@ -104,7 +105,7 @@ mutation ($path: String!, $title: String!, $content: String!) {
|
|||||||
isPublished: true
|
isPublished: true
|
||||||
isPrivate: false
|
isPrivate: false
|
||||||
tags: ["blog"]
|
tags: ["blog"]
|
||||||
description: ""
|
description: $description
|
||||||
) {
|
) {
|
||||||
responseResult { succeeded message }
|
responseResult { succeeded message }
|
||||||
page { id }
|
page { id }
|
||||||
@@ -191,13 +192,13 @@ class WikiJS:
|
|||||||
page = resp.get("data", {}).get("pages", {}).get("singleByPath")
|
page = resp.get("data", {}).get("pages", {}).get("singleByPath")
|
||||||
return page.get("id") if page else None
|
return page.get("id") if page else None
|
||||||
|
|
||||||
def update_page(self, page_id: int, content: str):
|
def update_page(self, page_id: int, content: str, description: str):
|
||||||
variables = {"id": page_id, "content": content}
|
variables = {"id": page_id, "content": content, "description": description}
|
||||||
resp = self.graphql(MUTATION_UPDATE_PAGE, variables)
|
resp = self.graphql(MUTATION_UPDATE_PAGE, variables)
|
||||||
return resp.get("data", {}).get("pages", {}).get("update", {}).get("responseResult", {}), resp
|
return resp.get("data", {}).get("pages", {}).get("update", {}).get("responseResult", {}), resp
|
||||||
|
|
||||||
def create_page(self, path: str, title: str, content: str):
|
def create_page(self, path: str, title: str, content: str, description: str):
|
||||||
variables = {"path": path, "title": title, "content": content}
|
variables = {"path": path, "title": title, "content": content, "description": description}
|
||||||
resp = self.graphql(MUTATION_CREATE_PAGE, variables)
|
resp = self.graphql(MUTATION_CREATE_PAGE, variables)
|
||||||
return resp.get("data", {}).get("pages", {}).get("create", {}).get("responseResult", {}), resp
|
return resp.get("data", {}).get("pages", {}).get("create", {}).get("responseResult", {}), resp
|
||||||
|
|
||||||
@@ -242,6 +243,7 @@ class BlogWriter:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
write_file(SOURCE_FILE, page["content"])
|
write_file(SOURCE_FILE, page["content"])
|
||||||
|
write_file(SOURCE_TITLE_FILE, page["title"])
|
||||||
|
|
||||||
def write(self):
|
def write(self):
|
||||||
original_lang = require_env("ORIGINAL_LANG", "Hungarian")
|
original_lang = require_env("ORIGINAL_LANG", "Hungarian")
|
||||||
@@ -275,6 +277,7 @@ class BlogWriter:
|
|||||||
|
|
||||||
def upload(self):
|
def upload(self):
|
||||||
content = read_file(TRANSLATED_FILE)
|
content = read_file(TRANSLATED_FILE)
|
||||||
|
description = read_file(SOURCE_TITLE_FILE).strip()
|
||||||
|
|
||||||
# Extract H1 title
|
# Extract H1 title
|
||||||
match = re.search(r"^#\s+(.+)", content, re.MULTILINE)
|
match = re.search(r"^#\s+(.+)", content, re.MULTILINE)
|
||||||
@@ -290,15 +293,16 @@ class BlogWriter:
|
|||||||
print(f"→ Uploading to Wiki.js")
|
print(f"→ Uploading to Wiki.js")
|
||||||
print(f" Title : {title}")
|
print(f" Title : {title}")
|
||||||
print(f" Path : /{page_path}")
|
print(f" Path : /{page_path}")
|
||||||
|
print(f" Description: {description}")
|
||||||
|
|
||||||
existing_id = self.wiki.find_page_id(page_path)
|
existing_id = self.wiki.find_page_id(page_path)
|
||||||
|
|
||||||
if existing_id:
|
if existing_id:
|
||||||
print(f" Found existing page id={existing_id}, updating...")
|
print(f" Found existing page id={existing_id}, updating...")
|
||||||
result, resp = self.wiki.update_page(existing_id, content)
|
result, resp = self.wiki.update_page(existing_id, content, description)
|
||||||
else:
|
else:
|
||||||
print(" Page not found, creating new...")
|
print(" Page not found, creating new...")
|
||||||
result, resp = self.wiki.create_page(page_path, title, content)
|
result, resp = self.wiki.create_page(page_path, title, content, description)
|
||||||
|
|
||||||
errors = resp.get("errors")
|
errors = resp.get("errors")
|
||||||
if errors:
|
if errors:
|
||||||
@@ -311,6 +315,20 @@ class BlogWriter:
|
|||||||
|
|
||||||
print(f"✓ Successfully uploaded to {self.wiki.base_domain}/{page_path}")
|
print(f"✓ Successfully uploaded to {self.wiki.base_domain}/{page_path}")
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Delete all .md files in the output directory."""
|
||||||
|
if not os.path.exists(OUTPUT_DIR):
|
||||||
|
print(f"→ Output directory '{OUTPUT_DIR}' does not exist. Nothing to clean.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"→ Cleaning {OUTPUT_DIR}/...")
|
||||||
|
count = 0
|
||||||
|
for filename in os.listdir(OUTPUT_DIR):
|
||||||
|
if filename.endswith(".md") or filename.endswith(".txt"):
|
||||||
|
os.remove(os.path.join(OUTPUT_DIR, filename))
|
||||||
|
count += 1
|
||||||
|
print(f"✓ Removed {count} Markdown files.")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Entry point
|
# Entry point
|
||||||
@@ -329,13 +347,16 @@ def main():
|
|||||||
p_fetch.add_argument("url", help="Page path or full URL, e.g. /my-page or https://wiki.example.com/my-page")
|
p_fetch.add_argument("url", help="Page path or full URL, e.g. /my-page or https://wiki.example.com/my-page")
|
||||||
|
|
||||||
# write
|
# write
|
||||||
subparsers.add_parser("write", help=f"Generate blog post from {SOURCE_FILE} using Gemini")
|
subparsers.add_parser("write", help=f"Generate blog post using Gemini")
|
||||||
|
|
||||||
# translate
|
# translate
|
||||||
subparsers.add_parser("translate", help=f"Translate {BLOGPOST_FILE} using Gemini")
|
subparsers.add_parser("translate", help=f"Translate generated blog post using Gemini")
|
||||||
|
|
||||||
# upload
|
# upload
|
||||||
subparsers.add_parser("upload", help=f"Upload {TRANSLATED_FILE} to Wiki.js")
|
subparsers.add_parser("upload", help=f"Upload translated blog post to Wiki.js")
|
||||||
|
|
||||||
|
# clean
|
||||||
|
subparsers.add_parser("clean", help=f"Delete all .md files in the {OUTPUT_DIR} directory")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
writer = BlogWriter()
|
writer = BlogWriter()
|
||||||
@@ -348,6 +369,8 @@ def main():
|
|||||||
writer.translate()
|
writer.translate()
|
||||||
elif args.command == "upload":
|
elif args.command == "upload":
|
||||||
writer.upload()
|
writer.upload()
|
||||||
|
elif args.command == "clean":
|
||||||
|
writer.clean()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user