| name | tistory-publish |
| description | Automate Tistory blog publishing via browser automation (Playwright). Use when publishing posts to Tistory blogs โ handles TinyMCE editor manipulation, OG card insertion, banner upload, tag registration, category setting, and representative image selection. Works around Tistory's isTrusted event filtering. Requires OpenClaw with Playwright browser profile. |
Tistory Publish
ํฐ์คํ ๋ฆฌ ๋ธ๋ก๊ทธ ์๋ ๋ฐํ ์คํฌ. Tistory Open API ์ข
๋ฃ(2024.02) ์ดํ ์ ์ผํ ์๋ํ ๊ฒฝ๋ก์ธ ๋ธ๋ผ์ฐ์ ์๋ํ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ ์ ์กฐ๊ฑด
- OpenClaw + Playwright ๋ธ๋ผ์ฐ์ ํ๋กํ (
profile="openclaw")
- ํฐ์คํ ๋ฆฌ ๋ธ๋ก๊ทธ (Kakao ๊ณ์ ๋ก๊ทธ์ธ ์๋ฃ)
- Node.js 18+ (
@napi-rs/canvas โ ๋ฐฐ๋ ์์ฑ์ฉ)
ํต์ฌ ํ์ผ
| ํ์ผ | ์ฉ๋ |
|---|
scripts/tistory-publish.js | ์๋ํฐ ์กฐ์ ํจ์ ๋ชจ์ (browser evaluate๋ก ๋ก๋) |
scripts/mk-banner.js | ๋ฐฐ๋ ์ด๋ฏธ์ง ์์ฑ (1150ร630) |
blog-drafts/MK-PUBLISH-RUNBOOK.md | ๋ฐํ ์์ ๋ฐ๋ถ (์ ์ฒด ์ํฌํ๋ก์ฐ) |
blog-drafts/TEMPLATE-mk-review.md | ์๊ณ ์์ฑ ํ
ํ๋ฆฟ |
๋ฐํ ์ํฌํ๋ก์ฐ (์์ฝ)
์ ์ฒด ์์๋ MK-PUBLISH-RUNBOOK.md ์ฐธ์กฐ.
-1. ์๊ณ ์์ฑ (๊ธฐ์ฌ ์ ์ โ ์์ฝ โ ์ฝ๋ฉํธ)
0. ๋ฐฐ๋ ์์ฑ: node scripts/mk-banner.js
1. ์ ๊ธ ํ์ด์ง: browser navigate โ /manage/newpost
2. ์นดํ
๊ณ ๋ฆฌ + ์ ๋ชฉ: setCategory() + setTitle()
3. ๋ณธ๋ฌธ ์ฝ์
: buildBlogHTML() โ insertContent()
4. OG ์นด๋: prepareOGPlaceholder() โ Playwright Enter โ verifyOGCard()
5. ๋ฐฐ๋ ์
๋ก๋: openBannerUploadInput() โ browser upload
6. ๋ํ์ด๋ฏธ์ง: setRepresentImageFromEditor()
7. ํ๊ทธ: setTags([...])
8. ์ฒดํฌ๋ฆฌ์คํธ ํ์ธ (ํ์: OGร4, ๋ฐฐ๋ / ๊ถ์ฅ: ๋ํ์ด๋ฏธ์ง, ํ๊ทธ)
9. ๋ฐํ (A: ๊ณต๊ฐ / B: ๋น๊ณต๊ฐ+๊ฒ์ ์์ฒญ)
์ฃผ์ ํจ์ (tistory-publish.js)
๋ชจ๋ ํจ์๋ browser evaluate๋ก ์คํ. ์คํฌ๋ฆฝํธ๋ฅผ ๋จผ์ ํ์ด์ง์ ๋ก๋ํ ๋ค ํธ์ถ.
์ฝํ
์ธ ์ฝ์
buildBlogHTML({intro, articles}) โ HTML ์์ฑ (OG placeholder ํฌํจ)
insertContent(html) โ TinyMCE์ HTML ์ฝ์
(CodeMirror ๋๊ธฐํ)
registerSchema() โ data-ke-* ์์ฑ ํ์ฉ ๋ฑ๋ก (insertContent ์ ํธ์ถ)
OG ์นด๋ (isTrusted ์ฐํ ํ์)
getOGPlaceholders() โ placeholder URL ๋ชฉ๋ก ๋ฐํ
prepareOGPlaceholder(url) โ placeholder๋ฅผ URL ํ
์คํธ๋ก ๊ต์ฒด + ์ปค์ ์์น
verifyOGCard(url) โ OG ์นด๋ ๋ ๋๋ง ํ์ธ + URL ์์ฌ๋ฌผ ์ ๋ฆฌ
cleanupOGResiduals() โ ๋จ์ URL ํ
์คํธ ์ ์ฒด ์ ๋ฆฌ
โ ๏ธ OG ์นด๋๋ JS ๋จ๋
๋ถ๊ฐ. prepareOGPlaceholder() ํ ๋ฐ๋์ browser press Enter (Playwright)๋ก isTrusted=true ์ด๋ฒคํธ๋ฅผ ๋ณด๋ด์ผ Tistory OG ํ์๊ฐ ํธ๋ฆฌ๊ฑฐ๋จ.
๋ฐฐ๋
openBannerUploadInput() โ ์ปค์๋ฅผ ์๋ํฐ ์๋จ์ผ๋ก ์ด๋ ํ ์ฒจ๋ถ ๋ฉ๋ด ํ์ฑํ
verifyBannerUpload() โ ์
๋ก๋๋ ์ด๋ฏธ์ง๊ฐ ์๋ฒ URL์ธ์ง ํ์ธ
๋ฉํ๋ฐ์ดํฐ
setCategory(name) โ ์นดํ
๊ณ ๋ฆฌ ์
๋ ํธ ๋ฐ์ค์์ ์ ํ
setTitle(title) โ ์ ๋ชฉ ์
๋ ฅ
setTags(tags[]) โ ํ๊ทธ ๋ฑ๋ก (nativeSetter + InputEvent + KeyboardEvent)
clearTags() โ ๊ธฐ์กด ํ๊ทธ ์ ์ฒด ์ญ์
๋ํ์ด๋ฏธ์ง
setRepresentImageFromEditor() โ ์๋ํฐ ๋ด ์ฒซ ๋ฒ์งธ ์๋ฒ ์ด๋ฏธ์ง๋ฅผ ๋ํ์ด๋ฏธ์ง๋ก ์ค์
๋ฐํ
clickPublish() โ ๋ฐํ ๋ฒํผ ํด๋ฆญ
์ค์ ์ปค์คํฐ๋ง์ด์ง
์ด ์คํฌ์ ๋งค๊ฒฝ ๋ฆฌ๋ทฐ๋ฅผ ์์๋ก ํฌํจํ๊ณ ์์ง๋ง, ๋ค๋ฅธ ์ฉ๋๋ก๋ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค:
blog-drafts/TEMPLATE-mk-review.md๋ฅผ ๋ณต์ฌํด์ ์์ ์ ํ
ํ๋ฆฟ ์์ฑ
buildBlogHTML()์ ์ ๋ฌํ๋ {intro, articles} ๊ตฌ์กฐ๋ง ๋ง์ถ๋ฉด ์ด๋ค ์ฝํ
์ธ ๋ ๋ฐํ ๊ฐ๋ฅ
setCategory(), setTitle(), setTags()๋ก ๋ฉํ๋ฐ์ดํฐ ์ค์
- ๋ฐฐ๋๋
mk-banner.js๋ฅผ ์์ ํ๊ฑฐ๋ ์ง์ ์ด๋ฏธ์ง ํ์ผ ์ค๋น
์๋ ค์ง ์ ์ฝ
- Tistory ์๋ํฐ๊ฐ
isTrusted=false ์ด๋ฒคํธ๋ฅผ ๋ฌด์ โ OG/ํ๊ทธ์ ์ฐํ ๋ก์ง ํ์
- ๋ฐฐ๋ ํ์ผ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ JS๋ก ์ ์ด ๋ถ๊ฐ โ Playwright upload ์ก์
์ฌ์ฉ
- ๋ํ์ด๋ฏธ์ง ์
๋ ํฐ๊ฐ Tistory ์
๋ฐ์ดํธ๋ง๋ค ๋ณ๊ฒฝ๋ ์ ์์ (v2์์ 10๊ฐ์ง ๋ณํ ๋์)