<?php
/**
 * MySender client for PHP (PHP 7.4+, needs the curl extension).
 * Send WhatsApp messages from YOUR software through YOUR OWN WhatsApp number linked in your MySender account.
 *
 *   require 'mysender-client.php';
 *   $ms = new MySenderClient('YOUR_AUTH_KEY', 'YOUR_APP_KEY');
 *   $r  = $ms->sendText('9876543210', 'Hello from my software');          // 10-digit Indian numbers get 91 added automatically
 *   if (!$r['ok']) { error_log($r['error']); }
 *
 * Full guide: https://mysender.mawanta.com/developers
 */
class MySenderClient
{
    const MAX_CHARS = 4000;                       // one WhatsApp message (longer texts are split)

    private $auth, $app, $base, $country, $lastSend = 0;

    public function __construct($authKey, $appKey, $baseUrl = 'https://mysender.mawanta.com', $defaultCountry = '91')
    {
        $this->auth = trim($authKey); $this->app = trim($appKey);
        $this->base = rtrim($baseUrl, '/'); $this->country = $defaultCountry;
    }

    /** "98765 43210", "+91 98765-43210", "09876543210" -> 919876543210 ; null if it cannot be a mobile number */
    public function number($raw)
    {
        $d = ltrim(preg_replace('/\D+/', '', (string) $raw), '0');
        if (strlen($d) === 10) $d = $this->country . $d;
        return (strlen($d) >= 11 && strlen($d) <= 13) ? $d : null;
    }

    public function sendText($to, $message)                      { return $this->send($to, ['message' => $message]); }
    public function sendFile($to, $message, $publicFileUrl)      { return $this->send($to, ['message' => $message, 'file' => $publicFileUrl]); }
    /** $variables: ['{name}' => 'Ramesh', '{amount}' => '1500'] (the keys exactly as written in the template) */
    public function sendTemplate($to, $templateId, array $variables = []) { return $this->send($to, ['template_id' => $templateId, 'variables' => $variables]); }

    /** long text is cut into parts at line ends and sent one after another (10-20 s apart) */
    public function send($to, array $payload)
    {
        $num = $this->number($to);
        if (!$num) return ['ok' => false, 'status' => 0, 'error' => 'Invalid mobile number: ' . $to];
        $parts = [$payload];
        if (isset($payload['message']) && mb_strlen($payload['message']) > self::MAX_CHARS && empty($payload['file'])) {
            $parts = [];
            foreach (self::split($payload['message']) as $chunk) $parts[] = ['message' => $chunk];
        }
        $last = null;
        foreach ($parts as $i => $p) {
            if ($i > 0) sleep(random_int(10, 20));
            $last = $this->post($p + ['to' => $num]);
            if (!$last['ok']) return $last;
        }
        return $last;
    }

    /**
     * Send many, one at a time, never faster than one message every $minGap seconds (random up to $maxGap) - this is what keeps a number safe.
     * $rows: [['to' => '9876543210', 'message' => '...'], ...]  (or with 'file' / 'template_id' / 'variables')
     * $onResult(row, result) is called after every message. Returns the list of results.
     */
    public function sendMany(array $rows, $onResult = null, $minGap = 10, $maxGap = 20)
    {
        $out = [];
        foreach ($rows as $row) {
            $wait = $this->lastSend + random_int($minGap, $maxGap) - time();
            if ($wait > 0) sleep($wait);
            $to = $row['to']; unset($row['to']);
            $res = $this->send($to, $row);
            $this->lastSend = time();
            $out[] = $res;
            if ($onResult) call_user_func($onResult, $row + ['to' => $to], $res);
        }
        return $out;
    }

    private function post(array $data)
    {
        $data += ['authkey' => $this->auth, 'appkey' => $this->app];
        $err = ''; $status = 0; $body = '';
        for ($try = 1; $try <= 4; $try++) {
            $ch = curl_init($this->base . '/api/create-message');
            curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 90, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_HTTPHEADER => ['Accept: application/json']]);
            $body = (string) curl_exec($ch); $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $err = curl_error($ch); curl_close($ch);
            $j = json_decode($body, true);
            if ($status === 200 && is_array($j) && ($j['message_status'] ?? '') === 'Success') return ['ok' => true, 'status' => 200, 'data' => $j['data'] ?? [], 'error' => null];
            // temporary trouble (too many requests, server busy, network): wait and try again
            if ($status === 429 || $status >= 500 || $status === 0) { sleep(random_int(15, 40) * $try); continue; }
            break;
        }
        $j = isset($j) && is_array($j) ? $j : [];
        $msg = $j['error'] ?? ($j['message'] ?? ($err ?: 'Unknown error'));
        if (!empty($j['data']) && is_array($j['data'])) $msg .= ' ' . json_encode($j['data']);
        return ['ok' => false, 'status' => $status, 'error' => trim($msg)];
    }

    private static function split($text)
    {
        $out = []; $cur = '';
        foreach (preg_split('/(\r?\n)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE) as $piece) {
            if (mb_strlen($cur . $piece) > self::MAX_CHARS) { if ($cur !== '') $out[] = $cur; $cur = ''; while (mb_strlen($piece) > self::MAX_CHARS) { $out[] = mb_substr($piece, 0, self::MAX_CHARS); $piece = mb_substr($piece, self::MAX_CHARS); } }
            $cur .= $piece;
        }
        if (trim($cur) !== '') $out[] = $cur;
        return $out;
    }
}
