#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""StudioSoft — halaman kanonik "Jasa SEO Surabaya" (pengganti 8 halaman yang saling berebut).

Pakai: python3 ss_halaman_jasa_seo_surabaya.py            (uji)
       python3 ss_halaman_jasa_seo_surabaya.py --terbit   (buat + terbitkan)
"""
import base64, json, os, re, sys, time, urllib.request

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
S = "https://studiosoft.id"
H = {"Authorization": "Basic " + base64.b64encode(b"bambangbot:q26r RePo BVy6 pwU2 F3z7 ouyl").decode(),
     "User-Agent": "Mozilla/5.0"}
HJ = {**H, "Content-Type": "application/json"}

JUDUL = "Jasa SEO Surabaya: Yang Dikerjakan, Cara Kerja, dan Cara Menilainya"
SLUG = "jasa-seo-surabaya"
FOKUS = "jasa seo surabaya"
META_JUDUL = "Jasa SEO Surabaya: Yang Dikerjakan dan Cara Menilai"
META_DESC = ("Jasa SEO Surabaya yang bekerja terbuka: apa saja yang dikerjakan tiap bulan, cara "
             "mengukur hasilnya, dan pertanyaan yang perlu Anda ajukan sebelum memilih.")
WA = ("https://wa.me/6281935000624?text=Halo%20StudioSoft%2C%20saya%20ingin%20konsultasi%20jasa%20SEO%20"
      "untuk%20bisnis%20saya")
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>')


def l(url, teks):
    return f'<a href="{url}">{teks}</a>'


ISI = """
<p>Mencari jasa SEO di Surabaya biasanya berakhir dengan daftar panjang nama agensi. Yang jarang
dijelaskan justru hal yang paling menentukan: apa saja yang benar-benar dikerjakan setiap bulan,
bagaimana hasilnya diukur, dan kapan sebaiknya Anda berhenti bekerja sama. Halaman ini menjawab tiga
hal itu tanpa janji berlebihan.</p>

<h2>Jasa SEO itu pekerjaan apa sebenarnya</h2>
<p>SEO bukan satu tindakan tunggal. Ia gabungan pekerjaan teknis, penulisan, dan perbaikan struktur
situs yang tujuannya satu: membuat situs muncul pada pencarian yang benar-benar dipakai calon pembeli,
lalu membuat pengunjung itu menghubungi Anda.</p>

<p>Karena itu, hasilnya tidak pernah datang dari satu hal saja. Menambah artikel tanpa memperbaiki
kecepatan halaman menghasilkan kunjungan yang berhenti di tengah. Memperbaiki teknis tanpa isi
menghasilkan situs rapi yang tidak ditemukan siapa pun.</p>

<h2>Yang dikerjakan setiap bulan</h2>
<p>Pekerjaan yang sehat punya tiga lapis, dan ketiganya berjalan bersamaan:</p>
<ul>
<li><strong>Lapis teknis.</strong> Kecepatan halaman, struktur alamat, pemetaan situs, data terstruktur,
dan memastikan halaman yang sudah ditulis benar-benar terbaca mesin pencari.</li>
<li><strong>Lapis isi.</strong> Halaman layanan yang menjelaskan apa yang Anda jual, dan artikel yang
menjawab pertanyaan calon pembeli sebelum membeli.</li>
<li><strong>Lapis jangkauan.</strong> Tautan internal yang menghubungkan halaman terkait, profil bisnis
untuk pencarian wilayah, dan perbaikan judul serta deskripsi agar orang mau mengklik.</li>
</ul>

<p>Kalau ada agensi yang menyebut angka peringkat tanpa menjelaskan tiga lapis ini, pertanyaannya
wajar: pekerjaan mana yang sebenarnya dibayar.</p>

<h2>Cara kerja yang bisa Anda pantau</h2>
<p>Pekerjaan SEO yang terbuka selalu meninggalkan jejak yang bisa Anda periksa sendiri:</p>
<ul>
<li>Daftar kueri yang sedang digarap, beserta posisi awalnya.</li>
<li>Halaman yang baru ditulis atau diperbarui pada bulan itu.</li>
<li>Catatan perubahan teknis: apa yang diubah dan kapan.</li>
<li>Laporan bulanan yang membandingkan angka dengan bulan sebelumnya, bukan hanya menampilkan angka
besar.</li>
</ul>
<p>Kalau Anda tidak bisa melihat salah satu dari empat hal itu, sulit membedakan pekerjaan sungguhan
dari laporan yang tampak sibuk.</p>

<h2>Kesalahan yang paling sering terjadi di Surabaya</h2>
<p>Dari pengalaman menggarap situs bisnis lokal, empat masalah ini paling sering muncul:</p>
<ul>
<li><strong>Banyak halaman untuk satu topik.</strong> Delapan halaman mengejar kueri yang sama membuat
sinyalnya pecah dan tidak ada yang menang. Yang dibutuhkan satu halaman yang benar-benar tuntas.</li>
<li><strong>Isi tentang perusahaan, bukan tentang pembeli.</strong> Halaman yang bercerita soal
pengalaman dan penghargaan tidak menjawab pertanyaan orang yang sedang mencari harga, jadwal, atau
lokasi.</li>
<li><strong>Halaman layanan kosong.</strong> Situs punya banyak artikel, tetapi halaman yang menjelaskan
jasa utamanya justru hanya satu paragraf.</li>
<li><strong>Tidak ada pengukuran kontak.</strong> Tanpa mencatat klik WhatsApp dan telepon, tidak
seorang pun tahu halaman mana yang mendatangkan calon pembeli.</li>
</ul>

""" + TOMBOL + """

<h2>Apa yang membedakan SEO lokal</h2>
<p>Untuk bisnis yang melayani satu kota, pekerjaan SEO berbeda dari biasanya. Orang tidak mencari
"jasa" secara umum, tetapi menyebut wilayah: nama layanan ditambah nama kota atau kecamatan. Kueri
seperti ini jumlahnya lebih kecil, tetapi niatnya jauh lebih dekat ke pembelian.</p>

<p>Konsekuensinya, halaman yang menang bukan halaman yang paling panjang, melainkan halaman yang
paling jelas menjawab: melayani wilayah mana, apa yang dikerjakan, bagaimana cara memulai, dan ke mana
menghubungi. Profil bisnis di Google juga ikut menentukan, karena sebagian pencarian wilayah dijawab
langsung dengan peta.</p>

<h2>Bulan pertama biasanya diisi apa</h2>
<p>Supaya tidak ada harapan keliru soal irama pekerjaan, ini gambaran bulan pertama yang wajar:</p>
<ul>
<li><strong>Pekan pertama.</strong> Pemeriksaan menyeluruh: kecepatan, status indeks, halaman yang
bertumpang tindih, dan kueri yang sudah ada di posisi 11 sampai 30.</li>
<li><strong>Pekan kedua.</strong> Perbaikan teknis dan penetapan satu halaman kanonik untuk kueri utama,
supaya tidak ada lagi halaman yang saling berebut.</li>
<li><strong>Pekan ketiga.</strong> Penulisan atau penulisan ulang halaman layanan, termasuk judul dan
deskripsi yang menjawab niat pencari.</li>
<li><strong>Pekan keempat.</strong> Pemasangan pengukuran kontak, lalu laporan awal sebagai titik
banding bulan berikutnya.</li>
</ul>
<p>Kenaikan biasanya belum terlihat di bulan pertama. Yang bisa diperiksa justru pekerjaannya: apa yang
diubah, dan di halaman mana.</p>

<h2>Cara menilai hasil, bukan janji</h2>
<p>Empat angka ini cukup untuk menilai pekerjaan SEO dalam tiga bulan:</p>
<ul>
<li><strong>Jumlah kueri yang bertayangan.</strong> Bertambah berarti situs makin sering muncul.</li>
<li><strong>Posisi rata-rata.</strong> Membaik berarti naik, walaupun belum tentu menambah klik.</li>
<li><strong>Klik dari pencarian.</strong> Ini yang menentukan trafik.</li>
<li><strong>Kontak masuk.</strong> Klik WhatsApp dan telepon — inilah yang paling dekat dengan uang.</li>
</ul>
<p>Angka pertama dan kedua bisa naik tanpa angka ketiga. Kalau itu terjadi, biasanya judul dan deskripsi
belum menjawab apa yang dicari orang, atau halaman yang muncul justru bukan halaman yang menjual.</p>

<h2>Kapan sebaiknya berhenti</h2>
<p>Saran yang jarang diberikan: berhenti kalau setelah tiga sampai empat bulan tidak ada satu pun angka
yang bergerak, dan tidak ada penjelasan yang bisa dipertanggungjawabkan. Sebaliknya, lanjutkan kalau
angkanya naik walau lambat, karena pekerjaan SEO bersifat menumpuk. Halaman yang sudah baik tetap
bekerja bertahun-tahun tanpa biaya tambahan.</p>

<h2>Pertanyaan yang perlu diajukan sebelum memilih</h2>
<ul>
<li>Pekerjaan apa saja yang masuk lingkup bulanan, dan apa yang dihitung tambahan?</li>
<li>Siapa yang menulis isinya, dan siapa yang memeriksanya?</li>
<li>Bagaimana cara Anda melaporkan hasil, dan seberapa sering?</li>
<li>Kalau kerja sama berhenti, apa yang tetap menjadi milik saya — artikel, halaman, dan akses?</li>
<li>Bisakah Anda menunjukkan situs klien yang boleh saya hubungi?</li>
</ul>

<h2>Pertanyaan yang sering muncul</h2>

<h3>Berapa lama hasil SEO mulai terlihat?</h3>
<p>Perbaikan teknis dan judul biasanya terasa dalam tiga sampai enam pekan. Penambahan halaman baru
memerlukan dua sampai empat bulan untuk benar-benar bekerja.</p>

<h3>Apakah harus membuat banyak artikel?</h3>
<p>Tidak selalu. Memperbaiki halaman layanan yang sudah ada dan merapikan topik yang tumpang tindih
sering memberi hasil lebih cepat daripada menambah artikel baru.</p>

<h3>Apakah jasa SEO bisa menjamin peringkat satu?</h3>
<p>Tidak ada yang bisa menjamin. Yang bisa dijanjikan adalah pekerjaan yang terukur: kueri yang digarap,
halaman yang dikerjakan, dan laporan yang bisa Anda periksa.</p>

<h3>Apa yang harus saya siapkan?</h3>
<p>Akses situs, target pasar, dan layanan yang ingin ditonjolkan. Kalau ada data penjualan atau daftar
pertanyaan pelanggan, itu sangat membantu menentukan prioritas.</p>

<h3>Bagaimana kalau situs saya pernah kena masalah keamanan?</h3>
<p>Itu dikerjakan lebih dulu. Situs yang masih menyimpan konten sisipan atau tautan tak terlihat sulit
naik, karena kepercayaan yang jadi dasarnya sudah terganggu.</p>

<p>Satu hal yang sering terlewat: pekerjaan SEO yang baik juga menyiapkan pemiliknya untuk mandiri.
Dokumentasi perubahan, daftar kueri, dan akses yang tetap milik Anda membuat kerja sama tidak
menjadi ketergantungan. Kalau nanti Anda memutuskan mengerjakannya sendiri, semua bahan dan catatan
sudah ada.</p>

<h2>Baca juga</h2>
<p>Untuk melihat harga dan paket, mulai dari halaman """ + l("/jasa-seo-professional/", "jasa SEO profesional") + """.
Kalau yang dicari agensi lengkap, ada """ + l("/digital-agency-terbaik-surabaya/", "digital marketing agency Surabaya") + """.
Sedangkan panduan dasar memilih penyedia bisa dibaca di """ + l("/7-jasa-seo-terbaik-di-surabaya-untuk-naikkan-ranking-website-ke-halaman-satu-google/", "rekomendasi jasa SEO Surabaya") + """.</p>

""" + TOMBOL + """
"""

TAG = [86, 110]

def rest(p, method="GET", data=None):
    body = json.dumps(data).encode() if data is not None else None
    r = urllib.request.Request(S + p, data=body, headers=HJ, method=method)
    with urllib.request.urlopen(r, timeout=180) as x:
        return json.loads(x.read())


def rapikan(isi):
    TAG_T = ("p", "li", "h2", "h3")
    pola = re.compile(r"(<(" + "|".join(TAG_T) + r")(?:\s[^>]*)?>)(.*?)</\2>", re.S | re.I)

    def g(mo):
        d = mo.group(3)
        if "<" in d or "\n" not in d:
            return mo.group(0)
        rapi = re.sub(r"\s{2,}", " ", re.sub(r"\s*\n\s*", " ", d)).strip()
        return f"{mo.group(1)}{rapi}</{mo.group(2)}>"
    return pola.sub(g, isi)


def skema_faq(isi):
    pasangan = []
    for mo in re.finditer(r"<h3>(.*?)</h3>\s*<p>(.*?)</p>", isi, re.S):
        q = re.sub(r"\s+", " ", re.sub("<[^>]+>", "", mo.group(1))).strip()
        j = re.sub(r"\s+", " ", re.sub("<[^>]+>", "", mo.group(2))).strip()
        if q and j:
            pasangan.append({"@type": "Question", "name": q,
                             "acceptedAnswer": {"@type": "Answer", "text": j}})
    if not pasangan:
        return ""
    return ('\n<script type="application/ld+json">'
            + json.dumps({"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": pasangan},
                         ensure_ascii=False) + "</script>\n")


def buat_gambar(berkas):
    from PIL import Image, ImageDraw, ImageFont
    F = os.path.expanduser("~/.fonts")
    f = lambda n, s: ImageFont.truetype(os.path.join(F, n), s)
    W, Hh, PAD = 1000, 567, 62
    img = Image.new("RGB", (W, Hh), (255, 255, 255))
    dr = ImageDraw.Draw(img)
    dr.rectangle([0, 0, W, 12], fill=(37, 99, 235))
    dr.rectangle([W - 300, Hh - 300, W, Hh], fill=(240, 245, 255))
    for i in range(3):
        dr.ellipse([W - 230 + i * 26, Hh - 210 + i * 26, W - 60, Hh - 40], outline=(37, 99, 235), width=2)
    dr.text((PAD, 62), "STUDIOSOFT", font=f("Poppins-SemiBold.ttf", 24), fill=(37, 99, 235))
    dr.text((PAD, 96), "Digital marketing Surabaya", font=f("Poppins-Regular.ttf", 20), fill=(100, 116, 139))
    dr.text((PAD, 180), "Jasa SEO", font=f("Poppins-Bold.ttf", 64), fill=(11, 31, 58))
    dr.text((PAD, 258), "Surabaya", font=f("Poppins-Bold.ttf", 64), fill=(11, 31, 58))
    dr.text((PAD, 360), "Yang dikerjakan, cara mengukur, dan cara menilai", font=f("Poppins-Regular.ttf", 24),
            fill=(100, 116, 139))
    dr.rectangle([PAD, Hh - 96, PAD + 74, Hh - 92], fill=(37, 99, 235))
    dr.text((PAD, Hh - 78), "studiosoft.id", font=f("Poppins-SemiBold.ttf", 23), fill=(11, 31, 58))
    img.save(berkas, "WEBP", quality=92, method=6)
    return img.size


isi = rapikan(ISI)
teks = re.sub(r"\s+", " ", re.sub("<[^>]+>", " ", isi)).strip()
print(f"judul: {JUDUL}\nslug: /{SLUG}/")
print(f"kata: {len(teks.split())} · h2 {isi.count('<h2')} · faq {isi.count('<h3>')} · "
      f"WA {'ada' if 'wa.me/6281935000624' in isi else 'TIDAK'} · "
      f"meta {len(META_JUDUL)}/{len(META_DESC)} char")

if "--terbit" not in sys.argv:
    print("\n(uji — pakai --terbit)")
    sys.exit(0)

ada = rest(f"/wp-json/wp/v2/posts?slug={SLUG}&status=any&_fields=id,status")
if isinstance(ada, list) and ada:
    print(f"\nsudah ada #{ada[0]['id']} status={ada[0]['status']} — dilewati")
    sys.exit(0)

d = rest("/wp-json/wp/v2/posts", "POST", {
    "title": JUDUL, "slug": SLUG, "content": isi + skema_faq(isi), "status": "publish",
    "comment_status": "closed", "categories": [330, 26], "tags": TAG, "excerpt": META_DESC})
pid = d["id"]
print(f"\nTERBIT #{pid} · {d.get('link')}")
rest(f"/wp-json/wp/v2/posts/{pid}", "POST", {"meta": {
    "_yoast_wpseo_title": META_JUDUL, "_yoast_wpseo_metadesc": META_DESC, "_yoast_wpseo_focuskw": FOKUS}})
berkas = f"{WS}/ss_feat_jasa-seo-surabaya.webp"
uk = buat_gambar(berkas)
data = open(berkas, "rb").read()
hh = {**H, "Content-Type": "image/webp", "Content-Disposition": 'attachment; filename="ss_feat_jasa-seo-surabaya.webp"'}
r = urllib.request.Request(f"{S}/wp-json/wp/v2/media", data=data, headers=hh, method="POST")
with urllib.request.urlopen(r, timeout=300) as x:
    med = json.loads(x.read())
rest(f"/wp-json/wp/v2/media/{med['id']}", "POST", {"alt_text": "Jasa SEO Surabaya StudioSoft", "title": JUDUL[:90]})
rest(f"/wp-json/wp/v2/posts/{pid}", "POST", {"featured_media": med["id"]})
time.sleep(2)
v = rest(f"/wp-json/wp/v2/posts/{pid}?context=edit&_fields=status,link,featured_media,meta,categories")
print(f"gambar: media #{med['id']} {uk[0]}x{uk[1]} webp · status {v['status']} · kategori {v['categories']}")
print(f"meta fokus: {(v.get('meta') or {}).get('_yoast_wpseo_focuskw')}")
print(f"alamat: {v['link']}")
