#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""StudioSoft: artikel baru — 7 pasal kontrak digital agency (pilar C retainer).

Jalur: python3 ss_artikel_kontrak_digital_agency.py            (dry run: hitung + cek)
       python3 ss_artikel_kontrak_digital_agency.py --draft    (buat DRAFT + meta Yoast)
       python3 ss_artikel_kontrak_digital_agency.py --jadwal   (set status=future 2026-10-19 08:00)
"""
import base64, json, re, sys, time, urllib.error, urllib.request

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
DOM = "studiosoft.id"
S = f"https://{DOM}"
AUTH = base64.b64encode(b"bambangbot:q26r RePo BVy6 pwU2 F3z7 ouyl").decode()

JUDUL = "Kontrak Digital Agency: 7 Pasal yang Harus Ada Sebelum Tanda Tangan"
SLUG = "kontrak-digital-agency-pasal-wajib"
FOKUS = "kontrak digital agency"
META_JUDUL = "7 Pasal Kontrak Digital Agency Wajib Dicek"
META_DESC = ("Kontrak digital agency perlu memuat tujuh pasal kunci, dari lingkup kerja sampai serah "
             "terima aset. Ini daftar yang bisa Anda cek sebelum tanda tangan.")
KATEGORI = [26]          # Digital Marketing
TANGGAL = "2026-10-19T08:00:00"   # zona situs (Asia/Jakarta)

WA = ("https://wa.me/6281935000624?text=Halo%20StudioSoft%2C%20saya%20ingin%20konsultasi%20"
      "digital%20marketing")
TOMBOL = ('<p style="text-align:center;margin:32px 0 8px"><a href="' + WA +
          '" target="_blank" rel="noopener" style="display:inline-block;background-color:#0f2557;'
          'color:#ffffff;padding:15px 34px;font-size:18px;font-weight:bold;text-decoration:none;'
          'border-radius:6px;">Konsultasi Gratis</a></p>')

ISI = """
<p>Kontrak digital agency biasanya ditandatangani di hari yang sibuk. Anggaran baru saja disetujui, tim
agensi sudah mulai bekerja, dan dokumennya dibaca sekilas karena semuanya terdengar masuk akal.
Pertanyaannya baru muncul beberapa bulan kemudian, saat Anda ingin tahu siapa pemilik akun iklan, atau
saat permintaan yang menurut Anda wajar ternyata dihitung sebagai pekerjaan tambahan.</p>

<p>Tujuh pasal di bawah ini adalah bagian yang paling sering memicu perdebatan. Anda tidak perlu jadi
ahli hukum untuk memeriksanya. Cukup pastikan setiap pasal menjawab satu pertanyaan sederhana, dan kalau
jawabannya masih kabur, minta ditulis ulang sebelum tanda tangan.</p>

<h2>Kenapa pasal kontrak baru terasa penting setelah masalah muncul</h2>
<p>Banyak kontrak agensi disusun dari template yang sudah dipakai berkali-kali. Isinya tidak salah,
hanya terlalu umum: pengelolaan SEO bulanan, pengelolaan media sosial, laporan berkala. Frasa seperti itu
nyaman bagi pihak agensi karena memberi ruang bergerak, dan merugikan Anda karena sulit ditagih hasilnya.
Kontrak digital agency yang isinya terlalu umum juga merepotkan saat Anda ingin menambah atau mengurangi
satu bagian pekerjaan di tengah periode.</p>

<p>Perselisihan hampir selalu mulai dari pertanyaan yang terdengar sepele. Siapa yang berhak memakai
materi yang sudah dibayar? Apakah menambah dua artikel masuk paket atau dihitung terpisah? Kalau kerja
sama berhenti, riwayat data ikut atau tidak? Semua pertanyaan itu punya jawaban yang jelas, tetapi harus
ditulis sejak awal, bukan dinegosiasikan saat hubungan sudah memanas.</p>

<h2>Tiga pasal pertama: lingkup kerja, ukuran hasil, ritme laporan</h2>
<p>Pasal lingkup kerja sebaiknya menyebut jumlah pekerjaan, bukan hanya jenisnya. Kalimat seperti empat
artikel per bulan dengan panjang 1.000 sampai 1.300 kata dan satu kali revisi jauh lebih mudah ditagih
daripada kata produksi konten. Kalau paketnya mencakup iklan, tulis berapa kampanye dan berapa anggaran
media yang dikelola.</p>

<p>Ukuran hasil perlu memakai angka yang bisa dibuka oleh kedua pihak dari alat yang sama. Misalnya
jumlah kontak masuk lewat WhatsApp dan formulir, jumlah halaman yang terindeks, atau biaya per calon
pembeli. Yang menentukan bukan besar kecilnya target, melainkan kesepakatan soal alat ukurnya. Ukuran yang
tertulis di kontrak digital agency sebaiknya juga menyebut cara membacanya, misalnya angka mana yang
dihitung per bulan dan mana yang dilihat per tiga bulan. Tanpa itu, setiap laporan bisa diperdebatkan dari
dua sudut yang berbeda.</p>

<p>Ritme laporan menentukan kapan Anda bisa menilai pekerjaan. Satu laporan ringkas setiap bulan dengan
tanggal tetap lebih berguna daripada dashboard yang tidak pernah dibuka siapa pun. Tulis juga nama orang
yang menyusun laporan tersebut, supaya ada pihak yang bisa ditanyai ketika angkanya tidak masuk akal.</p>

<h2>Dua pasal soal kepemilikan: akun dan karya</h2>
<p>Pasal kepemilikan akun sering dianggap remeh karena semuanya lancar selama kerja sama berjalan.
Mintalah domain, Google Analytics, Search Console, akun iklan, dan profil bisnis didaftarkan atas nama
bisnis Anda, sementara agensi diberi akses sebagai pengelola. Perbedaannya terasa saat Anda ingin
mengganti penyedia atau memakai jasa pihak lain untuk satu kanal tertentu.</p>

<p>Untuk karya, sepakati hak pakainya. Naskah, desain, foto, dan video yang Anda bayar sebaiknya boleh
dipakai ulang untuk kebutuhan lain, termasuk materi cetak dan presentasi penjualan, tanpa biaya
tambahan. Kalau agensi memakai komponen berlisensi pihak ketiga, minta daftarnya supaya Anda tahu bagian
mana yang tidak bisa dibawa saat pindah.</p>

<h2>Pasal biaya: angka tetap, pekerjaan tambahan, kenaikan harga</h2>
<p>Tulis nilai kerja sama bulanan, jadwal pembayaran, dan masa berlaku harganya. Kontrak yang mengunci
harga selama dua belas bulan memberi kepastian anggaran, tetapi tetap perlu klausul kenaikan harga:
berapa persen, kapan dihitung, dan berapa lama sebelumnya diberitahukan.</p>

<p>Pemicu perdebatan yang paling sering justru pekerjaan tambahan. Buat definisinya sempit. Sebutkan apa
yang sudah termasuk di dalam paket, apa yang dihitung terpisah, dan berapa tarifnya. Anda juga sebaiknya
berhak menghentikan pekerjaan yang belum disetujui tanpa biaya, asalkan aturan itu tertulis dan bukan
hanya disepakati lewat percakapan telepon.</p>

<h2>Pasal keluar: pemberitahuan, serah terima, dan akses data</h2>
<p>Kontrak yang sehat menyebut cara berhenti tanpa drama. Lazimnya cukup pemberitahuan tertulis tiga
puluh hari sebelum periode berikutnya dimulai, ditambah kewajiban mengembalikan akses dan berkas kerja.</p>

<p>Minta bagian serah terima yang rinci: daftar akun, arsip naskah mentah, berkas desain, kata sandi,
dan data historis analitik. Sebutkan juga tenggatnya, misalnya sepuluh hari kerja setelah pemberitahuan
diterima. Tanpa pasal ini, perpindahan penyedia bisa memakan waktu berbulan-bulan, dan pekerjaan yang
seharusnya berjalan justru berhenti di tengah jalan.</p>

<h2>Tujuh pasal itu dalam satu halaman ringkas</h2>
<p>Kalau kontrak digital agency Anda sedang disusun atau akan diperpanjang, bandingkan isinya dengan
daftar berikut. Bagian yang belum terjawab di sini biasanya yang akan ditanyakan pertama kali saat kerja
sama berjalan tidak sesuai harapan.</p>
<ul>
<li>Lingkup kerja dengan jumlah, format, dan batas revisi.</li>
<li>Ukuran hasil beserta alat ukur yang disepakati.</li>
<li>Jadwal laporan dan nama penanggung jawabnya.</li>
<li>Kepemilikan domain, akun analitik, dan akun iklan.</li>
<li>Hak pakai karya serta daftar komponen pihak ketiga.</li>
<li>Biaya bulanan, tarif pekerjaan tambahan, dan aturan kenaikan harga.</li>
<li>Cara berhenti, tenggat pemberitahuan, dan serah terima aset.</li>
</ul>

<h2>Pertanyaan yang sering diajukan</h2>

<h3>Apakah kontrak setahun selalu lebih menguntungkan?</h3>
<p>Belum tentu. Kontrak panjang masuk akal kalau lingkupnya sudah teruji dan hasilnya terukur. Untuk
kerja sama pertama, periode tiga bulan dengan evaluasi sebelum diperpanjang lebih aman bagi kedua pihak,
terutama untuk pekerjaan SEO yang hasilnya baru terlihat setelah beberapa bulan berjalan.</p>

<h3>Bagaimana kalau agensi menolak menyerahkan akses akun?</h3>
<p>Itu alasan yang cukup untuk menghentikan pembicaraan. Akun dan data adalah aset bisnis Anda, termasuk
riwayat kampanye yang sudah dibayar. Agensi yang bekerja dengan benar tidak keberatan, karena posisinya
adalah pengelola, bukan pemilik.</p>

<h3>Apa yang terjadi pada peringkat kalau saya pindah agensi?</h3>
<p>Peringkat bergantung pada isi situs, tautan, dan kecepatan teknis, bukan pada siapa yang mengelolanya.
Risiko terbesar saat pindah adalah kehilangan riwayat data dan jeda pekerjaan yang terlalu lama. Karena
itu, dua pasal soal kepemilikan dan serah terima di atas perlu ada sejak kontrak pertama.</p>

<h2>Langkah berikutnya</h2>
<p>Sebelum menandatangani kontrak digital agency, ambil satu jam untuk membaca ulang tujuh pasal di atas
dan tandai bagian yang masih ambigu. Kirimkan pertanyaan itu ke calon agensi secara tertulis adalah cara
paling cepat menilai cara mereka bekerja. Jawabannya sering lebih jujur daripada proposal yang tersusun
rapi.</p>

<p>Kalau Anda sedang membandingkan beberapa penyedia, lihat dulu
<a href="https://studiosoft.id/digital-marketing-agency-surabaya-cara-memilih-partner/">cara memilih
digital marketing agency di Surabaya</a> beserta hal yang biasanya jadi pembeda di antara mereka. Untuk
gambaran kerja sama bulanan, termasuk apa saja yang masuk paket, ada penjelasan di halaman
<a href="https://studiosoft.id/retainer-digital-marketing/">retainer digital marketing</a>. Daftar
penyedia di kota Anda kami rangkum di
<a href="https://studiosoft.id/digital-agency-terbaik-di-surabaya/">daftar digital agency terbaik di
Surabaya</a>.</p>

""" + TOMBOL + """
<p><em>Catatan: kami memakai daftar pasal ini untuk kontrak kami sendiri. Kalau ada bagian yang masih
terasa kabur di kerja sama Anda, sebutkan saja, itu lebih cepat ditangani daripada dibiarkan.</em></p>
"""

SCHEMA = """
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[
{"@type":"Question","name":"Apakah kontrak setahun selalu lebih menguntungkan?","acceptedAnswer":{"@type":"Answer","text":"Belum tentu. Kontrak panjang masuk akal kalau lingkupnya sudah teruji dan hasilnya terukur. Untuk kerja sama pertama, periode tiga bulan dengan evaluasi sebelum diperpanjang lebih aman bagi kedua pihak, terutama untuk pekerjaan SEO yang hasilnya baru terlihat setelah beberapa bulan berjalan."}},
{"@type":"Question","name":"Bagaimana kalau agensi menolak menyerahkan akses akun?","acceptedAnswer":{"@type":"Answer","text":"Itu alasan yang cukup untuk menghentikan pembicaraan. Akun dan data adalah aset bisnis Anda, termasuk riwayat kampanye yang sudah dibayar. Agensi yang bekerja dengan benar tidak keberatan, karena posisinya adalah pengelola, bukan pemilik."}},
{"@type":"Question","name":"Apa yang terjadi pada peringkat kalau saya pindah agensi?","acceptedAnswer":{"@type":"Answer","text":"Peringkat bergantung pada isi situs, tautan, dan kecepatan teknis, bukan pada siapa yang mengelolanya. Risiko terbesar saat pindah adalah kehilangan riwayat data dan jeda pekerjaan yang terlalu lama. Karena itu, dua pasal soal kepemilikan dan serah terima di atas perlu ada sejak kontrak pertama."}}]}
</script>
"""
ISI_FULL = ISI + SCHEMA

UA = {"User-Agent": "Mozilla/5.0"}


def rest(p, method="GET", data=None):
    h = {"Authorization": f"Basic {AUTH}", **UA}
    body = None
    if data is not None:
        body = json.dumps(data).encode()
        h["Content-Type"] = "application/json"
    r = urllib.request.Request(S + p, data=body, headers=h, method=method)
    for _ in range(3):
        try:
            with urllib.request.urlopen(r, timeout=180) as x:
                return x.status, json.loads(x.read())
        except urllib.error.HTTPError as e:
            return e.code, e.read().decode()[:300]
        except Exception as e:
            terakhir = str(e)[:120]
            time.sleep(4)
    return 0, terakhir


def polos(html):
    t = re.sub(r"<script.*?</script>", " ", html, flags=re.S | re.I)
    t = re.sub(r"<[^>]+>", " ", t)
    return re.sub(r"\s+", " ", t).strip()


if __name__ == "__main__":
    teks = polos(ISI_FULL)
    kata = len(teks.split())
    internal = re.findall(r'href="(https://studiosoft\.id/[^"]+)"', ISI_FULL)
    print(f"judul    : {JUDUL} ({len(JUDUL)} char)")
    print(f"slug     : {SLUG}")
    print(f"meta jdl : {META_JUDUL} ({len(META_JUDUL)} char)")
    print(f"meta desc: {len(META_DESC)} char · mulai dgn fokus: {META_DESC.lower().startswith(FOKUS)}")
    print(f"panjang  : {kata} kata (baku 1.000-1.300)")
    print(f"h2       : {ISI_FULL.count('<h2>')} · h3: {ISI_FULL.count('<h3>')}")
    print(f"internal : {len(internal)} {internal}")
    print(f"tombol WA: {'ada' if 'Konsultasi Gratis' in ISI_FULL and 'wa.me/6281935000624' in ISI_FULL else 'TIDAK ADA'}")
    print(f"FAQPage  : {'ada' if 'FAQPage' in ISI_FULL else 'TIDAK ADA'}")
    print(f"fokus kw di ISI: {ISI_FULL.lower().count(FOKUS)}x")

    if "--draft" not in sys.argv and "--jadwal" not in sys.argv:
        print("\n(dry run)")
        sys.exit(0)

    st, cek = rest(f"/wp-json/wp/v2/posts?slug={SLUG}&status=any&_fields=id,status")
    if isinstance(cek, list) and cek:
        pid = cek[0]["id"]
        print(f"\n   sudah ada #{pid} status={cek[0]['status']} → lanjut pakai id itu")
    else:
        st, d = rest("/wp-json/wp/v2/posts", "POST", {
            "title": JUDUL, "slug": SLUG, "content": ISI_FULL, "status": "draft",
            "comment_status": "closed", "categories": KATEGORI, "excerpt": META_DESC})
        if not isinstance(d, dict) or "id" not in d:
            print(f"   ❌ gagal buat draft: http={st} {str(d)[:300]}")
            sys.exit(1)
        pid = d["id"]
        print(f"\n   ✅ DRAFT #{pid} → {d.get('link')} status={d.get('status')}")

    st, d2 = rest(f"/wp-json/wp/v2/posts/{pid}", "POST", {"meta": {
        "_yoast_wpseo_title": META_JUDUL, "_yoast_wpseo_metadesc": META_DESC,
        "_yoast_wpseo_focuskw": FOKUS}})
    print(f"   meta: http={st}")

    # baca balik
    st, v = rest(f"/wp-json/wp/v2/posts/{pid}?context=edit")
    if isinstance(v, dict):
        m = v.get("meta") or {}
        isi_raw = v["content"]["raw"]
        print(f"   baca balik: status={v['status']} tanggal={v['date']} slug={v['slug']}")
        print(f"   meta tersimpan: fokus='{m.get('_yoast_wpseo_focuskw')}' "
              f"judul='{m.get('_yoast_wpseo_title')}' desc={len(m.get('_yoast_wpseo_metadesc') or '')} char")
        print(f"   isi: {len(isi_raw)} char · {len(polos(isi_raw).split())} kata · "
              f"wa.me={'ya' if 'wa.me/6281935000624' in isi_raw else 'TIDAK'} · "
              f"FAQPage={'ya' if 'FAQPage' in isi_raw else 'TIDAK'}")
    else:
        print(f"   ❌ baca balik gagal: {v}")

    if "--jadwal" in sys.argv:
        st, d3 = rest(f"/wp-json/wp/v2/posts/{pid}", "POST", {"status": "future", "date": TANGGAL})
        print(f"\n   set future: http={st} status={(d3 or {}).get('status') if isinstance(d3, dict) else d3}")
        st, v2 = rest(f"/wp-json/wp/v2/posts/{pid}?context=edit&_fields=id,status,date,date_gmt,slug,link")
        print(f"   baca balik: {json.dumps(v2, ensure_ascii=False)}")
        json.dump({"id": pid, "slug": SLUG, "status": (v2 or {}).get("status"),
                   "date": (v2 or {}).get("date"), "kata": kata, "internal": internal},
                  open(f"{WS}/ss_artikel_kontrak_hasil.json", "w"), ensure_ascii=False, indent=1)
        print(f"   -> ss_artikel_kontrak_hasil.json")
