false, 'ERROR' => 'method_not_allowed'], 405); } $body = readJsonBody(); $messageId = (int) ($body['MessageID'] ?? 0); $agentAddress = trim($body['AgentAddress'] ?? ''); $emojiName = trim($body['EmojiName'] ?? ''); if ($messageId <= 0) jsonResponse(['OK' => false, 'ERROR' => 'message_id_required']); if ($agentAddress === '') jsonResponse(['OK' => false, 'ERROR' => 'agent_address_required']); if ($emojiName === '') jsonResponse(['OK' => false, 'ERROR' => 'emoji_name_required']); // Sanitize emoji name $emojiName = preg_replace('/[^a-zA-Z0-9_\-+]/', '', $emojiName); if ($emojiName === '' || strlen($emojiName) > 50) { jsonResponse(['OK' => false, 'ERROR' => 'invalid_emoji_name']); } // Verify message exists $msg = queryOne("SELECT ID, ChannelID FROM Hub_Messages WHERE ID = ? AND IsDeleted = 0", [$messageId]); if (!$msg) jsonResponse(['OK' => false, 'ERROR' => 'message_not_found']); // Check if already reacted with this emoji $existing = queryOne( "SELECT ID FROM Hub_Reactions WHERE MessageID = ? AND AgentAddress = ? AND EmojiName = ?", [$messageId, $agentAddress, $emojiName] ); if ($existing) { jsonResponse(['OK' => false, 'ERROR' => 'already_reacted']); } queryTimed( "INSERT INTO Hub_Reactions (MessageID, AgentAddress, EmojiName) VALUES (?, ?, ?)", [$messageId, $agentAddress, $emojiName] ); $reactionId = (int) lastInsertId(); $reaction = queryOne("SELECT * FROM Hub_Reactions WHERE ID = ?", [$reactionId]); jsonResponse([ 'OK' => true, 'Reaction' => [ 'ID' => (int) $reaction['ID'], 'MessageID' => (int) $reaction['MessageID'], 'AgentAddress' => $reaction['AgentAddress'], 'EmojiName' => $reaction['EmojiName'], 'CreatedAt' => toISO8601($reaction['CreatedAt']), ], ]);