It's fairly straightfoward to do this by exploiting the fact that, when a player is underwater, their air meter will begin decreasing, which can be measured by command blocks.
First, set up the necessary scoreboard objectives (running these commands once):
/scoreboard objectives add air air
/scoreboard objectives add difference dummy
Then, every tick, run these command blocks in order (using a repeating command block and a chain command block):
/execute as @a run scoreboard players operation @s difference -= @s air
<command goes here>
/execute as @a run scoreboard players operation @s difference = @s air
Between the two
/execute commands, you can select underwater players using the selector
This isn't 100% reliable for a few reasons, but it should work:
- It fails to detect players who are underwater with a water breathing effect (which doesn't matter at all if what your command does is give players water breathing). You can detect players who gained the water breathing effect while underwater (but not players who had water breathing beforehand) with
- It fails to detect players the exact tick they take damage from drowning.