Refactors the leave-DM destructive core into a shared `removeDmMember`
helper and adds a kick endpoint that reuses it. Both endpoints write the
`member_removed` system message, delete the dm_members + read_states
rows, broadcast `dm_member_removed`, and queue a federation
`member_remove` event with the appropriate `reason` ('leave' | 'kick').
Branching invariants preserved by the helper:
- Ownership transfer fires only on self-leave when the leaver was the
owner. Kicks cannot orphan a group (the owner is still present), so
the transfer block is skipped.
- Soft-delete on last-member-empty fires only on self-leave. Kicks are
guaranteed to leave the owner behind, so the channel can never be
empty after a kick.
Endpoint validation:
- 1-on-1 DM → 400 'Cannot kick from a 1-on-1 DM'
- Caller not the owner → 403 'Only the group owner can remove members'
- Self-target → 400 'Owners cannot kick themselves; use leave instead'
- Target not a member → 404
- Channel missing or soft-deleted → 404
The kicked user is also evicted from the DM voice room (mirroring the
self-leave path) and receives `dm_channel_closed` so the client closes
the channel locally.