ProPeler
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
www
/
html
/
wp-content
/
themes
/
bayisihat-jurnal
/
inc
/
Filename :
drip.php
back
Copy
<?php /** * Bayi Sihat — Enjin Drip E-mel (Resend) * Hantar siri aluan 7 e-mel ikut timeline (Hari 0/2/4/7/10/14/18) dari tarikh daftar. * Subscriber = CPT bs_subscriber (post_title = emel, meta bs_sub_date = tarikh daftar). * * SELAMAT SECARA DEFAULT: takkan hantar apa-apa melainkan option `bs_drip_active` = 1. * Dijalankan oleh system cron: wp eval-file .../drip-cron.php */ if (!defined('ABSPATH')) exit; /* ---------- Konfigurasi ---------- */ function bs_drip_from() { return 'Bayi Sihat <hello@bayisihat.com>'; } function bs_drip_reply() { return 'hello@bayisihat.com'; } function bs_drip_active(){ return get_option('bs_drip_active') === '1'; } /** API key Resend: dari konstan wp-config (utama) atau option DB (fallback). */ function bs_resend_key() { if (defined('BS_RESEND_KEY') && BS_RESEND_KEY) return BS_RESEND_KEY; return (string) get_option('bs_resend_key', ''); } /* ---------- Personalisasi ---------- */ /** Nama pertama subscriber, atau '' jika tiada. */ function bs_sub_firstname($id) { $n = trim((string) get_post_meta($id, 'bs_sub_name', true)); if ($n === '') return ''; $parts = preg_split('/\s+/', $n); return $parts[0]; } /** Sapaan pembuka: "Hai Aina!" atau "Hai!" bila tiada nama. */ function bs_greet($fn) { return $fn !== '' ? 'Hai ' . esc_html($fn) . '!' : 'Hai!'; } /** Ganti {name} di tengah ayat dengan nama atau 'anda'. */ function bs_nm($fn) { return $fn !== '' ? esc_html($fn) : 'anda'; } /* ---------- Unsubscribe (satu-klik, ditandatangani) ---------- */ function bs_unsub_token($id) { return substr(hash_hmac('sha256', $id . '|bs-unsub', wp_salt('auth')), 0, 24); } function bs_unsub_url($id) { return home_url('/?bs_unsub=' . $id . '-' . bs_unsub_token($id)); } add_action('init', 'bs_handle_unsub'); function bs_handle_unsub() { if (empty($_GET['bs_unsub'])) return; $raw = sanitize_text_field(wp_unslash($_GET['bs_unsub'])); $bad = '<!doctype html><meta charset="utf-8"><div style="font:16px/1.6 system-ui;max-width:520px;margin:12vh auto;padding:0 24px;color:#22201B;text-align:center"><p style="font-size:2rem">🌱</p><h1 style="color:#2F5D50">Pautan tidak sah</h1><p style="color:#726D60">Pautan berhenti langgan ini tidak sah atau telah luput. Kalau anda masih nak berhenti, balas mana-mana e-mel kami dan kami uruskan.</p></div>'; if (!preg_match('/^(\d+)-([a-f0-9]{24})$/', $raw, $m)) { wp_die(wp_kses_post($bad), 'Pautan tidak sah', array('response' => 400)); } $id = (int) $m[1]; if (get_post_type($id) !== 'bs_subscriber' || !hash_equals(bs_unsub_token($id), $m[2])) { wp_die(wp_kses_post($bad), 'Pautan tidak sah', array('response' => 400)); } if (!get_post_meta($id, 'bs_unsub', true)) { update_post_meta($id, 'bs_unsub', current_time('mysql')); } $ok = '<!doctype html><meta charset="utf-8"><title>Berhenti langgan</title><div style="font:16px/1.6 system-ui,-apple-system,Segoe UI,Roboto;max-width:520px;margin:12vh auto;padding:0 24px;color:#22201B;text-align:center"><p style="font-size:2rem">🌱</p><h1 style="color:#2F5D50;margin:.2rem 0">Anda telah berhenti langgan</h1><p style="color:#726D60">Kami dah keluarkan e-mel anda dari senarai. Maaf melihat anda pergi. Pintu kami sentiasa terbuka di <a href="https://bayisihat.com" style="color:#A05C36">bayisihat.com</a> bila-bila anda perlukan kami.</p></div>'; wp_die(wp_kses_post($ok), 'Berhenti langgan', array('response' => 200)); } /* ---------- Templat HTML e-mel (inline CSS untuk keserasian klien) ---------- */ function bs_email_wrap($inner, $preview, $unsub_url) { $pre = esc_html($preview); $u = esc_url($unsub_url); return '<!doctype html><html lang="ms"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light"></head>' . '<body style="margin:0;padding:0;background:#F7F3EA;">' . '<span style="display:none!important;visibility:hidden;opacity:0;color:transparent;height:0;width:0;overflow:hidden;mso-hide:all">' . $pre . '</span>' . '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#F7F3EA;padding:24px 12px"><tr><td align="center">' . '<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:14px;overflow:hidden;border:1px solid #E4DECF">' // header . '<tr><td style="background:#264A40;padding:20px 32px"><span style="font:italic 700 13px Georgia,serif;letter-spacing:.14em;color:#EFC98B">BAYI SIHAT</span></td></tr>' // body . '<tr><td style="padding:32px 32px 8px 32px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:16px;line-height:1.65;color:#22201B">' . $inner . '</td></tr>' // sign-off spacing . '<tr><td style="padding:0 32px 28px 32px"></td></tr>' // footer . '<tr><td style="background:#F7F3EA;padding:20px 32px;font-family:-apple-system,Segoe UI,Roboto,Arial,sans-serif;font-size:12px;line-height:1.6;color:#726D60;border-top:1px solid #E4DECF">' . 'Bayi Sihat — panduan percuma untuk ibu bapa Malaysia.<br>' . 'Anda terima e-mel ini sebab anda melanggan di <a href="https://bayisihat.com" style="color:#A05C36">bayisihat.com</a>.<br>' . '<a href="' . $u . '" style="color:#726D60;text-decoration:underline">Berhenti langgan</a>' . '</td></tr>' . '</table></td></tr></table></body></html>'; } /** Butang CTA. */ function bs_btn($url, $label) { return '<table role="presentation" cellpadding="0" cellspacing="0" style="margin:6px 0 18px"><tr><td style="background:#2F5D50;border-radius:9px">' . '<a href="' . esc_url($url) . '" style="display:inline-block;padding:12px 22px;font-family:-apple-system,Segoe UI,Roboto,Arial,sans-serif;font-weight:700;font-size:15px;color:#F7F3EA;text-decoration:none">' . esc_html($label) . '</a>' . '</td></tr></table>'; } /** Perenggan. */ function bs_p($html) { return '<p style="margin:0 0 16px">' . $html . '</p>'; } function bs_sign() { return bs_p('Kakak-kakak di sebalik Bayi Sihat 🌱'); } /* ---------- Definisi 7 langkah (offset hari dari tarikh daftar) ---------- */ function bs_drip_steps() { return array( array('key' => 'w1', 'day' => 0, 'subject' => 'Selamat datang ke Bayi Sihat — ebook & PDF anda di dalam', 'preview' => 'Ebook Panduan Kehamilan + 2 PDF percuma anda, siap untuk dimuat turun.', 'body' => 'bs_body_w1'), array('key' => 'w2', 'day' => 2, 'subject' => 'Tak payah kira sendiri — 3 alat percuma untuk ibu', 'preview' => 'Penjejak kehamilan minggu 1-40, kalkulator EDD & jadual imunisasi.', 'body' => 'bs_body_w2'), array('key' => 'w3', 'day' => 4, 'subject' => 'Mula dari sini kalau anda tak tahu nak baca apa dulu', 'preview' => 'Dua panduan besar kami, disusun kemas ikut fasa.', 'body' => 'bs_body_w3'), array('key' => 'w4', 'day' => 7, 'subject' => 'Kenapa kami cakap "rujuk doktor" banyak kali', 'preview' => 'Sikit tentang cara kami tulis, dan kenapa anda boleh percaya.', 'body' => 'bs_body_w4'), array('key' => 'w5', 'day' => 11, 'subject' => 'Jom sertai kami di WhatsApp', 'preview' => 'Tempat lebih santai untuk tanya dan berkongsi.', 'body' => 'bs_body_w5'), array('key' => 'w6', 'day' => 14, 'subject' => '3 kesilapan biasa yang mak ayah baru selalu buat', 'preview' => 'Bukan nak menakutkan, cuma nak anda lebih bersedia.', 'body' => 'bs_body_w6'), array('key' => 'w7', 'day' => 18, 'subject' => 'Kita jumpa lagi ya', 'preview' => 'Sikit ucapan, dan cara kekal berhubung.', 'body' => 'bs_body_w7'), ); } /* Pautan dikongsi */ function bs_lnk() { return array( 'ebook' => 'https://bayisihat.com/checkout/', 'bag' => 'https://bayisihat.com/wp-content/uploads/lead/checklist-hospital-bag-bayisihat.pdf', 'imun' => 'https://bayisihat.com/wp-content/uploads/lead/jadual-imunisasi-bayisihat.pdf', 'week' => 'https://bayisihat.com/kehamilan-mingguan/', 'edd' => 'https://bayisihat.com/alat/kalkulator-tarikh-bersalin/', 'imunt' => 'https://bayisihat.com/alat/jadual-imunisasi/', 'pgn' => 'https://bayisihat.com/panduan-kehamilan/', 'baby' => 'https://bayisihat.com/panduan-bayi/', 'tools' => 'https://bayisihat.com/alat/', 'wa' => 'https://chat.whatsapp.com/FgjRktQ47VP6j5o7cjdf1k', ); } /* ---------- Badan setiap e-mel (HTML) ---------- */ function bs_body_w1($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn) . ' 🌱') . bs_p('Terima kasih sebab sudi langgan Bayi Sihat. Ini semua hadiah percuma anda:') . bs_p('📘 <a href="' . esc_url($l['ebook']) . '" style="color:#A05C36">Ebook Panduan Kehamilan Lengkap</a> (73 muka surat)<br>' . '📄 <a href="' . esc_url($l['bag']) . '" style="color:#A05C36">Checklist Beg Hospital</a><br>' . '📄 <a href="' . esc_url($l['imun']) . '" style="color:#A05C36">Jadual Imunisasi Bayi</a>') . bs_btn($l['ebook'], 'Muat turun ebook percuma') . bs_p('Simpan dalam telefon, atau print tampal atas peti sejuk. Guna bila-bila perlu.') . bs_p('Sikit pasal kami. Bayi Sihat ni macam kakak senior yang dah lalui semua fasa, dari mengandung sampai anak dah pandai lari. Kami takkan penuhkan inbox anda dengan spam. Apa yang anda boleh jangka: tips penjagaan bayi praktikal ikut konteks Malaysia, panduan ringkas ikut fasa, dan info jujur. Untuk hal kesihatan serius, kami sentiasa pesan supaya rujuk doktor atau klinik.') . bs_p('Satu permintaan je hari ni. <strong>Balas e-mel ni</strong> dan bagitahu: anda di fasa mana sekarang? Sedang hamil, atau baby dah lahir? Satu ayat pun cukup. Jawapan anda bantu kami hantar kandungan yang betul-betul kena.') . bs_p('Selamat datang sekali lagi.') . bs_sign(); } function bs_body_w2($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn)) . bs_p('Pernah tak duduk malam-malam, buka Google, cuba kira sendiri "sekarang ni masuk minggu ke berapa?" atau "bila sebenarnya tarikh jangkaan bersalin aku?"') . bs_p('Kami pun pernah. Sebab tu kami siapkan tiga alat percuma, supaya anda tak payah buat kerja kira-kira tu lagi:') . bs_p('1. <a href="' . esc_url($l['week']) . '" style="color:#A05C36">Penjejak Kehamilan Minggu 1-40</a> — masukkan tarikh, terus nampak minggu semasa & perkembangan bayi.<br>' . '2. <a href="' . esc_url($l['edd']) . '" style="color:#A05C36">Kalkulator EDD</a> — kira tarikh jangkaan bersalin dalam beberapa saat.<br>' . '3. <a href="' . esc_url($l['imunt']) . '" style="color:#A05C36">Jadual Imunisasi</a> — susunan suntikan bayi ikut jadual kebangsaan Malaysia.') . bs_btn($l['tools'], 'Buka semua alat percuma') . bs_p('Semuanya percuma, tak perlu daftar apa-apa, terus guna dari telefon.') . bs_p('Simpan e-mel ni. Bila perlu nanti, anda tahu di mana nak cari.') . bs_p('Jaga diri,') . bs_sign(); } function bs_body_w3($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn)) . bs_p('Info tentang kehamilan dan bayi ni memang banyak sampai buat pening. Jadi kami susun semua dalam dua panduan besar, ikut fasa, supaya anda tak tersesat.') . bs_p('🤰 <a href="' . esc_url($l['pgn']) . '" style="color:#A05C36">Panduan Kehamilan Lengkap</a> — dari tanda awal hamil, loya, senaman selamat, sampai tanda-tanda nak bersalin.') . bs_p('👶 <a href="' . esc_url($l['baby']) . '" style="color:#A05C36">Panduan Bayi 0-12 Bulan</a> — penyusuan, tidur, imunisasi, tumbesaran, dan penjagaan harian.') . bs_p('Cadangan kami: pilih <strong>SATU</strong> topik yang paling anda risau sekarang, baca tu dulu. Yang lain boleh tunggu.') . bs_p('Kalau ada topik yang anda nak kami tulis tapi belum ada, balas je e-mel ni. Kami betul-betul baca.') . bs_sign(); } function bs_body_w4($fn, $unsub) { return bs_p(bs_greet($fn)) . bs_p('Anda mungkin perasan, dalam artikel kami ada je ayat "rujuk doktor atau klinik". Bukan sebab malas nak bagi jawapan. Sebab kami betul-betul ambil berat.') . bs_p('Info kesihatan bayi ni bukan benda main-main. Jadi ini janji kami pada anda:') . bs_p('• Kami rujuk garis panduan rasmi seperti KKM, bukan cerita dengar-dengar.<br>' . '• Kalau kami tak pasti sesuatu, kami akan cakap terus yang kami tak pasti.<br>' . '• Untuk keadaan serius atau kecemasan, kami sentiasa pesan jumpa doktor. Sebab setiap bayi berbeza, dan tiada artikel boleh ganti pemeriksaan sebenar.') . bs_p('Kami nak jadi tempat pertama anda cari info, dan tempat yang anda boleh percaya. Itu lebih penting dari sekadar nampak pandai.') . bs_p('Kalau e-mel kami bermanfaat setakat ni, kami akan sangat hargai kalau anda kongsi <a href="https://bayisihat.com" style="color:#A05C36">bayisihat.com</a> dengan seorang kawan yang sedang hamil atau baru dapat baby. Itu sahaja cara kami berkembang.') . bs_p('Terima kasih sebab percaya kami.') . bs_sign(); } function bs_body_w5($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn)) . bs_p('E-mel memang bagus untuk panduan panjang. Tapi kadang-kadang anda cuma nak tanya soalan cepat, atau nak tengok tips ringkas hari-hari.') . bs_p('Untuk tu, kami ada WhatsApp Bayi Sihat. Tempat lebih santai, lebih dekat.') . bs_btn($l['wa'], '💬 Sertai WhatsApp Bayi Sihat') . bs_p('Tiada spam, tiada jualan paksa. Cuma tips berguna dan komuniti mak ayah yang lalui fasa yang sama macam anda.') . bs_p('Jumpa di sana!') . bs_sign(); } function bs_body_w6($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn)) . bs_p('Selepas banyak berbual dengan mak ayah, ada beberapa perkara yang kami nampak selalu berulang. Bukan salah sesiapa, cuma tiada orang beritahu awal-awal. Jadi biar kami kongsi:') . bs_p('<strong>1. Tunggu terlalu lama sebelum minta bantuan.</strong> Kalau ada rasa tak kena, jangan tunggu sampai teruk. Tanya doktor lebih awal. Lebih baik "over-worry" dari terlepas benda penting.') . bs_p('<strong>2. Bandingkan bayi sendiri dengan bayi orang lain.</strong> Setiap bayi ada tempo sendiri untuk merangkak, bercakap, dan sebagainya. Ada julat normal yang luas. Fokus pada bayi anda, bukan feed Instagram.') . bs_p('<strong>3. Lupa jaga diri sendiri.</strong> Mak ayah yang cukup rehat dan tenang, lebih mampu jaga bayi. Berehat bukan tanda lemah, ia sebahagian dari penjagaan.') . bs_p('Kami dah tulis panduan penuh untuk setiap fasa, rujuk bila perlu:') . bs_btn($l['tools'], 'Lihat semua panduan & alat') . bs_p('Anda buat dengan baik, ' . bs_nm($fn) . '. Betul.') . bs_sign(); } function bs_body_w7($fn, $unsub) { $l = bs_lnk(); return bs_p(bs_greet($fn)) . bs_p('Ini e-mel terakhir dalam siri aluan kami. Lepas ni, kami akan hantar panduan dan tips baru dari semasa ke semasa, tak kerap sampai rimas.') . bs_p('Sebelum tu, terima kasih. Terima kasih sebab bagi kami ruang dalam inbox anda, dan sebab percayakan kami dalam salah satu fasa paling penting dalam hidup anda.') . bs_p('Kalau anda nak kekal dekat dengan kami:') . bs_p('• Simpan <a href="https://bayisihat.com" style="color:#A05C36">bayisihat.com</a> sebagai tempat rujukan bila-bila perlu<br>' . '• <a href="' . esc_url($l['wa']) . '" style="color:#A05C36">Sertai WhatsApp kami</a> untuk tips harian<br>' . '• Balas e-mel ni bila-bila anda ada soalan. Kami baca semua.') . bs_p('Doa kami, semoga perjalanan anda sebagai mak ayah dipermudahkan, dan si manja membesar sihat dan gembira.') . bs_p('Jumpa lagi.') . bs_sign(); } /* ---------- Penghantar (Resend) ---------- */ function bs_drip_send($email, $id, $st, $key) { $fn = bs_sub_firstname($id); $unsub = bs_unsub_url($id); $html = bs_email_wrap(call_user_func($st['body'], $fn, $unsub), $st['preview'], $unsub); $subject = $st['subject']; $resp = wp_remote_post('https://api.resend.com/emails', array( 'timeout' => 20, 'headers' => array( 'Authorization' => 'Bearer ' . $key, 'Content-Type' => 'application/json', ), 'body' => wp_json_encode(array( 'from' => bs_drip_from(), 'to' => array($email), 'reply_to' => bs_drip_reply(), 'subject' => $subject, 'html' => $html, 'headers' => array( 'List-Unsubscribe' => '<' . $unsub . '>', 'List-Unsubscribe-Post' => 'List-Unsubscribe=One-Click', ), 'tags' => array(array('name' => 'stream', 'value' => 'welcome'), array('name' => 'step', 'value' => $st['key'])), )), )); if (is_wp_error($resp)) return array(false, $resp->get_error_message()); $code = wp_remote_retrieve_response_code($resp); $bodyr = wp_remote_retrieve_body($resp); return array($code >= 200 && $code < 300, $code . ' ' . substr($bodyr, 0, 140)); } /* ---------- Runner ---------- */ /** * Semak semua subscriber, hantar SATU e-mel yang tertunggak (bila due) per subscriber setiap larian. * @param bool $dry Kalau true, tak hantar — cuma lapor apa yang akan dihantar. */ function bs_drip_run($dry = false, $limit = 300) { $out = array('sent' => 0, 'skipped_inactive' => false, 'log' => array()); if (!$dry && !bs_drip_active()) { $out['skipped_inactive'] = true; $out['log'][] = 'Enjin TIDAK aktif (option bs_drip_active != 1). Tiada e-mel dihantar.'; return $out; } $key = bs_resend_key(); if (!$dry && $key === '') { $out['log'][] = 'RALAT: tiada Resend API key (BS_RESEND_KEY / option bs_resend_key).'; return $out; } $q = new WP_Query(array( 'post_type' => 'bs_subscriber', 'post_status' => 'publish', 'posts_per_page' => $limit, 'fields' => 'ids', 'no_found_rows' => true, 'meta_query' => array(array('key' => 'bs_unsub', 'compare' => 'NOT EXISTS')), )); $steps = bs_drip_steps(); $now = current_time('timestamp'); foreach ($q->posts as $id) { $email = get_post_field('post_title', $id); if (!is_email($email)) { $out['log'][] = "lompat (emel tak sah) #$id"; continue; } $signup = get_post_meta($id, 'bs_sub_date', true); if (!$signup) $signup = get_post_field('post_date', $id); $sig_ts = strtotime($signup); if (!$sig_ts) { $out['log'][] = "lompat (tarikh tak sah) $email"; continue; } $done = get_post_meta($id, 'bs_drip_sent', true); if (!is_array($done)) $done = array(); foreach ($steps as $st) { if (in_array($st['key'], $done, true)) continue; $due = $sig_ts + ($st['day'] * DAY_IN_SECONDS); if ($now < $due) continue; // belum sampai masa if ($dry) { $out['log'][] = "[dry] $email <= {$st['key']} (Hari {$st['day']})"; $out['sent']++; break; } list($ok, $info) = bs_drip_send($email, $id, $st, $key); if ($ok) { $done[] = $st['key']; update_post_meta($id, 'bs_drip_sent', $done); update_post_meta($id, 'bs_drip_last', current_time('mysql')); $out['sent']++; $out['log'][] = "HANTAR {$st['key']} -> $email"; } else { $out['log'][] = "GAGAL {$st['key']} -> $email :: $info"; } break; // satu e-mel per subscriber per larian (elak burst / lindungi deliverability) } } return $out; }