diff --git a/config.sample.php b/config.sample.php index 32b45c0..752b7d7 100755 --- a/config.sample.php +++ b/config.sample.php @@ -16,5 +16,6 @@ return [ 'MIN_TIP_AMOUNT' => 0.2, 'MIN_WITHDRAWAL_AMOUNT' => 0.4, - 'WITHDRAWAL_FEE' => 0.1 + 'WITHDRAWAL_FEE' => 0.1, + 'MAX_MULTI_TIPS' => 5 ]; diff --git a/src/salvium_tipbot_commands.php b/src/salvium_tipbot_commands.php index b6a4227..2699422 100644 --- a/src/salvium_tipbot_commands.php +++ b/src/salvium_tipbot_commands.php @@ -100,51 +100,88 @@ class SalviumTipBotCommands { } - private function cmd_tip(array $args, array $ctx): string { - if (count($args) < 3) return "Usage: /tip "; - - list(, $targetUsername, $amount) = $args; - $amount = (float)$amount; - - if ($amount < $this->config['MIN_TIP_AMOUNT']) { - return "Tip {$amount} for {$targetUsername} too small. Minimum tip is {$this->config['MIN_TIP_AMOUNT']} SAL."; - } - - $sender = $this->db->getUserByTelegramId($ctx['user_id']); - if (!$sender || $sender['tip_balance'] < $amount) { - return "Insufficient funds or invalid sender."; - } - - $cleanUsername = ltrim($targetUsername, '@'); - - $recipient = $this->db->ensureUserExists( - 0, - $cleanUsername, - fn() => $this->wallet->getNewSubaddress(), - true - ); - - if (!$recipient) { - return "Failed to ensure recipient exists."; - } - - $this->db->updateUserTipBalance($sender['id'], $amount, 'subtract'); - $this->db->addTip($sender['id'], $recipient['id'], $amount, $ctx['chat_id']); - - if (!empty($recipient['telegram_user_id']) && $recipient['telegram_user_id'] > 0 && $recipient['telegram_user_id'] !== (100_000 + (crc32($cleanUsername) % 900_000))) { - sendMessage($recipient['telegram_user_id'], "You received a tip of {$amount} SAL! Use /balance to check."); - } else { - sendMessage($ctx['chat_id'], "Hey @$cleanUsername, you just got a tip from @$ctx[username]!"); - sendGif( - $ctx['chat_id'], - 'CgACAgQAAxkBAAOQaDjcu6ftEKHp3ZCCKX8p6hTkqxEAAtYaAAL4YMlR4yZwk_GMuWg2BA', - "DM me and run /claim to receive it." - ); - } - - return "Tipped {$targetUsername} {$amount} SAL successfully!"; +private function cmd_tip(array $args, array $ctx): string { + if (count($args) < 3) { + return "Usage: /tip [user2 ...] "; } + $rawAmount = $args[count($args) - 1]; + $amount = (float)$rawAmount; + + if (!is_numeric($rawAmount) || $amount <= 0) { + return "Invalid tip amount."; + } + + if ($amount < $this->config['MIN_TIP_AMOUNT']) { + return "Each tip must be at least {$this->config['MIN_TIP_AMOUNT']} SAL."; + } + + + $usernames = array_filter(array_slice($args, 1, -1), function($u) { + $u = trim($u); + return $u !== '' && $u !== '@'; + }); + + $maxRecipients = $this->config['MAX_MULTI_TIPS'] ?? 1; + + if (count($usernames) > $maxRecipients) { + return "You can tip up to {$maxRecipients} users at once."; + } + + $sender = $this->db->getUserByTelegramId($ctx['user_id']); + $total = $amount * count($usernames); + + if (!$sender || $sender['tip_balance'] < $total) { + return "Insufficient funds. You need at least {$total} SAL to tip these users."; + } + + $successful = []; + + foreach ($usernames as $targetUsername) { + $cleanUsername = trim(ltrim($targetUsername, '@')); + + if ($cleanUsername === '' || !preg_match('/^[a-zA-Z0-9_]{5,32}$/', $cleanUsername)) { + continue; // skip invalid usernames + } + + try { + $recipient = $this->db->ensureUserExists( + 0, + $cleanUsername, + fn() => $this->wallet->getNewSubaddress(), + true + ); + + if (!$recipient) continue; + + $this->db->updateUserTipBalance($sender['id'], $amount, 'subtract'); + $this->db->addTip($sender['id'], $recipient['id'], $amount, $ctx['chat_id']); + $successful[] = $cleanUsername; + + if (!empty($recipient['telegram_user_id']) && $recipient['telegram_user_id'] > 0 && $recipient['telegram_user_id'] !== (100_000 + (crc32($cleanUsername) % 900_000))) { + sendMessage($recipient['telegram_user_id'], "You received a tip of {$amount} SAL! Use /balance to check."); + } else { + sendMessage($ctx['chat_id'], "Hey @$cleanUsername, you just got a tip from @$ctx[username]!"); + sendGif( + $ctx['chat_id'], + 'CgACAgQAAxkBAAOQaDjcu6ftEKHp3ZCCKX8p6hTkqxEAAtYaAAL4YMlR4yZwk_GMuWg2BA', + "DM me and run /claim to receive it." + ); + } + + } catch (Throwable $e) { + // Silently skip + } + } + + if (empty($successful)) { + return "Tip failed — no valid recipients."; + } + + return "Tipped " . implode(', ', $successful) . " {$amount} SAL each!"; +} + + private function cmd_claim(array $args, array $ctx): string { $user = $this->db->getUserByUsername($ctx['username']); diff --git a/src/salvium_tipbot_db.php b/src/salvium_tipbot_db.php index 9b6f1ba..b7a9542 100755 --- a/src/salvium_tipbot_db.php +++ b/src/salvium_tipbot_db.php @@ -127,6 +127,11 @@ class SalviumTipBotDB { callable $subaddressGenerator, bool $allowSynthetic = false ): array { + + if (!$username || trim($username) === '' || $telegramId === 0 && !$allowSynthetic) { + throw new RuntimeException("Invalid username or telegram ID."); + } + // 1. Try exact match by Telegram ID $user = $this->getUserByTelegramId($telegramId);