#!/usr/bin/env python3
"""StudioSoft: artikel harian 12 Okt 2026 (pilar E lokal) — kata kunci lokal yang benar-benar dibeli.
Pola mengikuti ss_artikel_agensi_4.py. Tahap: draf dulu (--apply), baru dijadwalkan (--jadwalkan TANGGAL).
"""
import base64, json, re, sys, time, urllib.error, urllib.request

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

JUDUL = "Kata Kunci Lokal: Cara Menemukan yang Benar-Benar Dibeli Orang"
SLUG = "kata-kunci-lokal"
FOKUS = "kata kunci lokal"
META_JUDUL = "Kata Kunci Lokal: Cara Menemukan yang Dibeli"
META_DESC = ("Kata kunci lokal yang benar-benar dibeli bisa dilacak dari Search Console. Ini cara memilah "
             "kueri berniat beli dan menentukan wilayah yang layak digarap.")

KAT = [26]  # Digital Marketing

WA = "https://wa.me/6281935000624?text=Halo%20StudioSoft%2C%20saya%20ingin%20konsultasi%20digital%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>')

L1 = "https://studiosoft.id/7-cara-menggunakan-ai-untuk-riset-kata-kunci-yang-lebih-cepat-dan-akurat/"
L2 = "https://studiosoft.id/apa-itu-seo-pengertian-cara-kerja-manfaat/"
L3 = "https://studiosoft.id/jasa-seo-surabaya-paket-harga/"

FAQ = [
    ("Apakah perlu alat berbayar untuk menemukan kata kunci lokal?",
     "Tidak selalu. Search Console sudah cukup untuk memulai karena datanya berasal dari pencarian nyata "
     "yang memunculkan situs Anda. Alat berbayar berguna saat Anda perlu membandingkan permintaan antar "
     "wilayah atau melihat kueri yang belum pernah muncul."),
    ("Berapa banyak halaman kota yang wajar dibuat?",
     "Sebanyak wilayah yang memang muncul di data kueri Anda. Kalau hanya dua wilayah yang menghasilkan "
     "tayangan, dua halaman itu cukup. Menambah wilayah tanpa data hanya menambah halaman tipis."),
    ("Kalau kueri lokal saya muncul di posisi 15, apa yang perlu diperbaiki?",
     "Lihat halaman yang mengungguli Anda. Kalau semuanya direktori atau artikel daftar, perbaiki "
     "kejelasan halaman Anda: cakupan layanan, wilayah, harga, dan cara menghubungi. Kalau yang unggul "
     "penyedia lokal lain, periksa kedalaman isi dan tautan internal."),
    ("Apakah nama kota perlu diulang banyak di dalam halaman?",
     "Tidak. Sebutkan di judul, satu sub-judul, dan beberapa kalimat yang memang membahas wilayah itu. "
     "Pengulangan yang tidak menambah informasi tidak membantu pembaca maupun peringkat."),
]

ISI = """
<p>Banyak pemilik bisnis menebak kata kunci lokal dengan satu cara: tempel nama kota di belakang nama
layanan, lalu berharap halaman itu menang. Hasilnya banyak halaman yang isinya berulang, tetapi jarang
mendatangkan pembeli. Persoalannya ada di pertanyaan yang layak dijawab: kueri mana yang dipakai orang
sebelum menghubungi penyedia?</p>

<p>Artikel ini menunjukkan cara menemukan kata kunci lokal dari data yang sudah Anda miliki sendiri, yaitu
laporan Search Console. Yang perlu dikuasai adalah memisahkan kueri yang datang karena penasaran dari
kueri yang muncul tepat sebelum orang membeli.</p>

<h2>Mulai dari data yang sudah Anda miliki</h2>
<p>Buka Search Console, pilih properti situs Anda, lalu masuk ke menu Performance. Di bagian Queries, Anda
melihat daftar kueri yang memunculkan situs Anda beserta jumlah tayangan, klik, dan posisi rata-ratanya.
Data ini gratis dan berasal dari pencarian nyata, bukan perkiraan alat pihak ketiga.</p>

<p>Susun daftarnya: ambil semua kueri yang punya tayangan selama 28 hari terakhir, lalu urutkan dari
tayangan terbesar. Tandai kueri yang memuat nama kota, kabupaten, atau kecamatan. Kueri bertanda wilayah
inilah bahan mentah kata kunci lokal Anda, dan semuanya sudah terbukti dipakai orang.</p>

<p>Kalau datanya masih tipis, ambil kueri yang Anda bidik di judul halaman lalu cek apakah kueri itu
muncul. Halaman yang tidak pernah muncul biasanya bermasalah pada judul, isi, atau status pengindeksan,
dan itu perlu dibereskan sebelum menambah halaman baru.</p>

<h2>Membedakan kueri yang ramai dari kueri yang dibeli</h2>
<p>Dua kueri bisa terlihat mirip, tetapi niat orang yang mengetiknya jauh berbeda. Kueri seperti "apa itu
seo" mendatangkan banyak tayangan dan hampir tidak menghasilkan kontak, karena yang mencarinya sedang
belajar. Kueri seperti "jasa seo surabaya paket bulanan" datang dari orang yang sudah tahu kebutuhannya dan
membandingkan penyedia.</p>

<p>Cara cepat memisahkannya: baca kueri itu seolah Anda mengucapkannya sambil memegang dompet. Kalau orang
masih butuh berminggu-minggu sebelum siap menghubungi, kueri tersebut cocok jadi bahan edukasi, bukan
target utama halaman layanan.</p>

<p>Perhatikan juga posisi rata-ratanya. Kueri dengan 300 tayangan di posisi 12 biasanya lebih menarik
daripada kueri dengan 30 tayangan di posisi 1, karena permintaannya sudah terbukti dan masih ada ruang
untuk naik.</p>

<h2>Penanda kata yang menunjukkan niat beli</h2>
<p>Kata kunci lokal yang layak digarap biasanya memuat penanda berikut.</p>
<ul>
<li>Kata biaya: harga, biaya, tarif, berapa, atau paket.</li>
<li>Nama wilayah yang spesifik: kota, kabupaten, kecamatan, atau nama jalan tertentu.</li>
<li>Kata tindakan: servis, pasang, sewa, pesan, buka, atau panggil.</li>
<li>Nama kebutuhan yang sudah jelas, misalnya "laser jerawat" atau "website company profile".</li>
</ul>
<p>Sebaliknya, kueri yang memuat kata seperti contoh, pengertian, jenis, atau cara membuat berada di tahap
awal. Kueri itu lebih tepat dipakai di artikel pendukung yang menautkan pembacanya ke halaman layanan.</p>

<h2>Seberapa jauh nama kota dan kecamatan perlu disebut</h2>
<p>Di bagian ini banyak pekerjaan SEO lokal berlebihan. Membuat satu halaman untuk setiap kecamatan di kota
besar terlihat rapi, tetapi isinya sama dengan halaman kota induknya, hanya nama wilayahnya yang ditukar.
Google cenderung menampilkan salah satu saja.</p>

<p>Ukur dulu apakah permintaannya ada. Cari nama kecamatan atau wilayah tertentu di daftar kueri Anda.
Kalau nama itu muncul, berarti ada orang di sana yang mencari, dan satu halaman khusus masuk akal. Kalau
tidak pernah muncul, tahan dulu.</p>

<p>Untuk bisnis yang melayani dari satu lokasi, memperkuat satu halaman kota biasanya lebih menguntungkan.
Isi dengan bukti setempat: foto pekerjaan, nama proyek, dan keterangan cara pelayanan dijalankan.</p>

<h2>Siapa yang sebenarnya mengisi hasil teratas</h2>
<p>Setelah daftar kueri tersusun, periksa halaman yang sekarang menempati hasil teratas untuk setiap kueri
tersebut. Hasilnya sering tidak seperti dugaan awal: yang menang bukan selalu penyedia jasa di kota itu
sendiri.</p>

<p>Saat kami memeriksa hasil pencarian untuk kueri bergaya lokal, misalnya pencarian bengkel di kota besar,
halaman teratas justru berisi artikel daftar dan halaman direktori, bukan beranda bengkel. Halaman informasi
yang mengumpulkan banyak penyedia ikut bersaing di kueri seperti itu, di samping penyedia lokal.</p>

<p>Konsekuensinya praktis: kalau hasil teratas diisi direktori dan artikel daftar, halaman layanan yang
generik cenderung kalah. Peluangnya ada di halaman yang menjawab kebutuhan spesifik, dengan cakupan
layanan, wilayah, dan harga yang tertulis jelas.</p>

<h2>Uji kecil sebelum menulis banyak halaman</h2>
<p>Godaan terbesar dalam SEO lokal adalah membuat banyak halaman sekaligus. Mulai dari tiga halaman:
layanan kota utama, layanan paling spesifik, dan artikel pendukung yang menjawab pertanyaan sebelum orang
membeli.</p>

<p>Setelah empat sampai enam pekan, bandingkan datanya. Yang diperhatikan bukan peringkat saja, tetapi
jumlah kueri baru yang mulai memunculkan halaman itu dan apakah ada kontak masuk.</p>

<p>Halaman baru butuh waktu sebelum datanya berarti. Perubahan pertama yang biasanya terlihat adalah
bertambahnya kueri yang memunculkan situs Anda. Kalau setelah enam pekan belum muncul untuk satu kueri
pun, penyebabnya biasanya isi yang tipis atau judul yang tidak sesuai.</p>

<h2>Kesalahan yang membuat halaman lokal tidak jalan</h2>
<ul>
<li>Halaman kota yang isinya hanya menukar nama wilayah dari halaman lain.</li>
<li>Memilih kueri dari alat riset tanpa memeriksa apakah kueri itu muncul di Search Console.</li>
<li>Judul halaman tidak memuat kueri utama, sehingga Google tidak punya alasan menampilkannya.</li>
<li>Halaman layanan tidak memuat cara menghubungi yang jelas dan bisa diklik dari ponsel.</li>
<li>Menilai keberhasilan hanya dari peringkat satu kata kunci, tanpa melihat kueri yang mulai tumbuh.</li>
</ul>
<p>Kalau Anda ingin memperdalam sisi riset kata kunci sebelum menentukan target, langkahnya kami bahas di
<a href=\"""" + L1 + """\">cara menggunakan AI untuk riset kata kunci</a>. Untuk dasar-dasar yang perlu
dipahami lebih dulu, mulai dari <a href=\"""" + L2 + """\">pengertian SEO dan cara kerjanya</a>.</p>

<h2>Pertanyaan yang sering diajukan</h2>
<h3>""" + FAQ[0][0] + """</h3>
<p>""" + FAQ[0][1] + """</p>
<h3>""" + FAQ[1][0] + """</h3>
<p>""" + FAQ[1][1] + """</p>
<h3>""" + FAQ[2][0] + """</h3>
<p>""" + FAQ[2][1] + """</p>
<h3>""" + FAQ[3][0] + """</h3>
<p>""" + FAQ[3][1] + """</p>

<p>Menemukan kata kunci lokal yang benar-benar dibeli bergantung pada dua hal: membaca kueri yang sudah ada
di data Anda dan menguji tiga halaman sebelum memperbanyak. Kalau Anda lebih suka menyerahkan bagian ini
kepada tim, kami biasa mulai dari pemeriksaan data Search Console dan penentuan target yang bisa diukur.
Cakupan kerja dan kisaran biayanya ada di <a href=\"""" + L3 + """\">halaman jasa SEO Surabaya</a>.</p>

""" + TOMBOL + """
<p><em>Catatan: contoh pemeriksaan hasil pencarian di atas kami ambil dari pengecekan langsung halaman hasil
Google, bukan dari perkiraan.</em></p>
"""

LD = {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": [{"@type": "Question", "name": q,
                    "acceptedAnswer": {"@type": "Answer", "text": a}} for q, a in FAQ],
}
ISI = ISI + '\n<script type="application/ld+json">' + json.dumps(LD, ensure_ascii=False) + "</script>\n"

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(f"https://{DOM}{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:
            time.sleep(4)
    return 0, "gagal"


tanpa_script = re.sub(r"<script.*?</script>", " ", ISI, flags=re.S)
kata_visible = len(re.sub(r"<[^>]+>", " ", tanpa_script).split())
kata_hitung = len(re.sub(r"<[^>]+>", " ", ISI).split())
print("=== artikel baru:", JUDUL)
print(f"   panjang naskah: {kata_visible} kata terlihat · {kata_hitung} kata versi cek_pola_ai")
print(f"   meta judul: {len(META_JUDUL)} char (maks 60)")
print(f"   meta desc : {len(META_DESC)} char (140-158) | fokus di awal: {META_DESC.lower().startswith(FOKUS)}")
print(f"   tombol WA : {'ada' if WA in ISI else 'TIDAK ADA'}")
print(f"   tautan internal: {len(re.findall(r'href=.https://studiosoft.id/', ISI))}")
print(f"   h2: {len(re.findall('<h2', ISI))} | FAQPage: {'FAQPage' in ISI}")

if "--apply" not in sys.argv and "--jadwalkan" not in sys.argv:
    print("\n(dry run — pakai --apply untuk bikin draf, --jadwalkan YYYY-MM-DD untuk menjadwalkan)")
    sys.exit(0)

if "--apply" in sys.argv:
    st, d = rest(f"/wp-json/wp/v2/posts?slug={SLUG}&_fields=id,slug,status")
    if isinstance(d, list):
        for p in d:
            print(f"   ⚠ slug {SLUG} sudah dipakai: #{p['id']} [{p['status']}] — hentikan, ganti slug")
        if d:
            sys.exit(1)
    st, d = rest("/wp-json/wp/v2/posts", "POST", {
        "title": JUDUL, "slug": SLUG, "content": ISI, "status": "draft",
        "categories": KAT, "comment_status": "closed",
        "meta": {"_yoast_wpseo_title": META_JUDUL, "_yoast_wpseo_metadesc": META_DESC,
                 "_yoast_wpseo_focuskw": FOKUS}})
    if not isinstance(d, dict) or "id" not in d:
        print("   ❌ gagal bikin draf:", st, str(d)[:300])
        sys.exit(1)
    pid = d["id"]
    print(f"\n   ✅ draf #{pid} → {d.get('link')}")
    st2, d2 = rest(f"/wp-json/wp/v2/posts/{pid}?context=edit")
    raw = (d2.get("content") or {}).get("raw", "")
    meta = (d2.get("meta") or {})
    print(f"   baca balik: status={d2.get('status')} slug={d2.get('slug')} kata="
          f"{len(re.sub(r'<[^>]+>', ' ', raw).split())}")
    print("   meta: " + json.dumps({k: v for k, v in meta.items() if "yoast" in k}, ensure_ascii=False)[:400])
    simpan = {"id": pid, "judul": JUDUL, "slug": SLUG, "tautan": d.get("link")}
    json.dump(simpan, open(f"{WS}/drafts/ss_artikel_kata_kunci_lokal.json", "w"), ensure_ascii=False, indent=1)
    print("   → id disimpan di drafts/ss_artikel_kata_kunci_lokal.json")

if "--jadwalkan" in sys.argv:
    tgl = sys.argv[sys.argv.index("--jadwalkan") + 1]
    pid = json.load(open(f"{WS}/drafts/ss_artikel_kata_kunci_lokal.json"))["id"]
    st, d = rest(f"/wp-json/wp/v2/posts/{pid}", "POST",
                 {"status": "future", "date": f"{tgl}T08:00:00"})
    if not isinstance(d, dict) or d.get("status") != "future":
        print("   ❌ gagal menjadwalkan:", st, str(d)[:300])
        sys.exit(1)
    st2, d2 = rest(f"/wp-json/wp/v2/posts/{pid}?context=edit&_fields=id,status,date,slug,content,meta")
    raw = (d2.get("content") or {}).get("raw", "")
    print(f"   ✅ dijadwalkan #{pid}: status={d2.get('status')} date={d2.get('date')} slug={d2.get('slug')}")
    print(f"   isi: {len(re.sub(r'<[^>]+>', ' ', raw).split())} kata | tombol WA: "
          f"{'ada' if 'wa.me/6281935000624' in raw else 'TIDAK ADA'} | tautan internal: "
          f"{len(re.findall(r'href=.https://studiosoft.id/', raw))}")
