Sorry for nit-picking, but
- the on disconnect / on quit event won't kill the timers (ocoff alias), as they don't know a $chan value. But then, you don't need those events at all - you don't start any offline timer

- what's the use of the variable if you evaluate it instantly to the $chan?
-
all the scripts (not only yours) do
evaluate the channel name in a timer command

My two cents:
; ----- SETUP START -----
; duration (in s) to wait for op after join and between requests
alias oc.delay { return 15 }
; numer of times to ask for ops before part
alias oc.request { return 3 }
; ----- SETUP END -----
on me:*:join:#: oc.beg
on me:*:part:#: oc.off
on *:deop:#: if ($opnick == $me) oc.beg
on *:kick:#: if ($knick == $me) oc.off
on *:op:#: if ($opnick == $me) oc.off
alias -l oc.beg {
.timeroc $+ # $$oc.request $$oc.delay oc
oc.ask # $calc($oc.request * $oc.delay)
}
alias -l oc {
var %c = $mid($ctimer,3-)
if ($me isop %c) { .timeroc $+ %c off }
elseif ($timer($ctimer).reps == 0) { part %c }
else { oc.ask %c $calc($timer($ctimer).reps * $timer($ctimer).delay) }
}
alias -l oc.off { .timeroc $+ $chan off }
alias -l oc.ask { msg $1 I need to be opped. If I don't get ops, I'll auto-part in $duration($2) }