false, 'ERROR' => 'method_not_allowed'], 405); } $body = readJsonBody(); $channelId = (int) ($body['ChannelID'] ?? 0); $agent = trim($body['Agent'] ?? ''); if ($channelId <= 0) { jsonResponse(['OK' => false, 'ERROR' => 'channel_id_required']); } if ($agent === '') { jsonResponse(['OK' => false, 'ERROR' => 'agent_required']); } // Verify channel exists and is not archived $channel = queryOne("SELECT * FROM Hub_Channels WHERE ID = ?", [$channelId]); if (!$channel) { jsonResponse(['OK' => false, 'ERROR' => 'channel_not_found'], 404); } if ((bool) $channel['IsArchived']) { jsonResponse(['OK' => false, 'ERROR' => 'channel_archived']); } // Private/direct channels can't be self-joined if ($channel['ChannelType'] !== 'public') { jsonResponse(['OK' => false, 'ERROR' => 'channel_not_public']); } // Check if already a member $existing = queryOne( "SELECT ID FROM Hub_ChannelMembers WHERE ChannelID = ? AND AgentAddress = ?", [$channelId, $agent] ); if ($existing) { jsonResponse(['OK' => true, 'Note' => 'already_member']); } // Add member queryTimed( "INSERT INTO Hub_ChannelMembers (ChannelID, AgentAddress, Role) VALUES (?, ?, 'member')", [$channelId, $agent] ); jsonResponse(['OK' => true]);