Create a gist now

Instantly share code, notes, and snippets.

@grawity /README.md
Last active Sep 10, 2016

What would you like to do?
Eggdrop scripts

Eggdrop 1.8 SASL script:

  1. For convenience, download this entire gist with git clone https://gist.github.com/4455067.git

  2. Get Eggdrop 1.8 from Git or from CVS. Compile and install.

    The preinit-server patch was merged into Eggdrop 1.8 recently, so you do not need to patch it manually anymore.

  3. From your Eggdrop config, source the scripts and set the SASL information.

    source "scripts/4455067/g_base64.tcl"
    source "scripts/4455067/g_cap.tcl"
    
    set sasl-user $username
    set sasl-pass "blahblah"

(For those who still need it, the old patch is still there.)

Atheme auto-reop script:

  1. From your Eggdrop config, source the g_atheme_need.tcl script.

  2. Add a user named services to your bot, with ChanServ's hostmask, and give it the +fS flags.

    .+user services *!*@services.example.com
    .chattr services +fS
    

    Otherwise the bot can think it's being notice-flooded by ChanServ and ignore it.

# g_atheme_need.tcl - handles "need op/unban/invite/key" events with Atheme
# (c) 2011 Mantas Mikulėnas <grawity@gmail.com>
# Released under the MIT Expat License.
## Configuration
#
# 0) Load this script
# 1) Add a "services" user with ChanServ's hostmask
# 2) .chattr services +fS
proc need:op {channel type} {
putlog "($channel) requesting reop"
putquick "PRIVMSG ChanServ :op $channel"
}
proc need:unban {channel type} {
putlog "($channel) requesting unban"
putquick "PRIVMSG ChanServ :unban $channel"
}
proc need:invite {channel type} {
putlog "($channel) requesting invite"
putquick "PRIVMSG ChanServ :invite $channel"
}
proc need:key {channel type} {
putlog "($channel) requesting key"
putquick "PRIVMSG ChanServ :getkey $channel"
}
proc chanserv:recvkey {nick addr hand text dest} {
set msg [split [stripcodes bcru $text] " "]
set channel [lindex $msg 1]
set key [lindex $msg 4]
putlog "($channel) received key, joining channel"
putquick "JOIN $channel $key"
return 1
}
bind need - "% op" need:op
bind need - "% unban" need:unban
bind need - "% invite" need:invite
bind need - "% key" need:key
bind notc S "Channel % key is: *" chanserv:recvkey
# g_base64.tcl - Base64 encoding/decoding routines
# (c) 2013 Mantas Mikulėnas <grawity@gmail.com>
# Released under the MIT Expat License.
if {![catch {package require Tcl 8.6}]} {
proc b64:encode {input} {
binary encode base64 $input
}
proc b64:decode {input} {
binary decode base64 $input
}
} elseif {![catch {package require base64}]} {
proc b64:encode {input} {
::base64::encode -wrapchar "" $input
}
proc b64:decode {input} {
::base64::decode $input
}
} else {
set b64map {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 + / =}
proc b64:encode {input} {
global b64map
set str {}
set pad 0
binary scan $input c* X
foreach {x y z} $X {
if {$y == {}} {set y 0; incr pad}
if {$z == {}} {set z 0; incr pad}
set n [expr {($x << 16) | ($y << 8) | $z}]
set a [expr {($n >> 18) & 63}]
set b [expr {($n >> 12) & 63}]
set c [expr {($n >> 6) & 63}]
set d [expr {$n & 63}]
append str \
[lindex $b64map $a] \
[lindex $b64map $b] \
[lindex $b64map [expr {$pad >= 2 ? 64 : $c}]] \
[lindex $b64map [expr {$pad >= 1 ? 64 : $d}]]
}
return $str
}
proc b64:decode {input} {
set str {}
set pos 0
set pad 0
set n 0
binary scan $input c* X
foreach x $X {
if {$x >= 65 && $x <= 90} { set x [expr {$x - 65}] }\
elseif {$x >= 97 && $x <= 122} { set x [expr {$x - 71}] }\
elseif {$x >= 48 && $x <= 57} { set x [expr {$x + 4}] }\
elseif {$x == 61} { set x 0; incr pad }\
else { continue }
set o [expr {18 - 6 * ($pos % 4)}]
set n [expr {$n | ($x << $o)}]
if {$o == 0} {
set a [expr {($n >> 16) & 255}]
set b [expr {($n >> 8) & 255}]
set c [expr {$n & 255}]
set n 0
if {$pad == 2} {
append str [binary format c $a]
break
} elseif {$pad == 1} {
append str [binary format cc $a $b]
break
} else {
append str [binary format ccc $a $b $c]
}
}
incr pos
}
return $str
}
}
# g_cap.tcl - IRCv3 capability negotiation and SASL support
# (c) 2013-2016 Mantas Mikulėnas <grawity@gmail.com>
# Released under the MIT Expat License.
#
# Requires: g_base64.tcl
## Configuration -- set these in your eggdrop.conf
# available mechs: EXTERNAL, PLAIN
set sasl-use-mechs {PLAIN}
# services username
set sasl-user "$username"
# password for PLAIN
set sasl-pass "hunter2"
# disconnect on failure?
set sasl-disconnect-on-fail 1
# extra capabilities to ask for
set caps-wanted {multi-prefix}
## Internal state -- do not edit anything below
if {[info procs b64:encode] == ""} {
die "You must load g_base64.tcl first."
}
set caps-enabled {}
set caps-preinit 0
set sasl-mechs {}
set sasl-midx 0
set sasl-mech "*"
set sasl-step 0
## Utility functions
proc rparse {text} {
if {[string index $text 0] == ":"} {
set pos [string first " " $text]
set vec [list [string range $text 0 [expr $pos-1]]]
}
set pos [string first " :" $text]
if {$pos < 0} {
set vec [split $text " "]
} else {
set vec [split [string range $text 0 [expr $pos-1]] " "]
lappend vec [string range $text [expr $pos+2] end]
}
return $vec
}
## Raw CAP commands
proc cap:on-connect {ev} {
global caps-preinit
switch $ev {
"preinit-server" {
set caps-preinit 1
putnow "CAP LS"
}
"init-server" {
set caps-preinit 0
}
}
return 0
}
proc raw:CAP {from keyword rest} {
global caps-preinit
global caps-wanted
global sasl-user
if {${caps-preinit} == 0} {
return 1
}
set vec [rparse [string trim $rest]]
set cmd [lindex $vec 1]
set caps [lindex $vec 2]
switch $cmd {
LS {
putlog "Server offers caps: $caps"
set wanted {}
foreach cap $caps {
if {[lsearch -exact ${caps-wanted} $cap] != -1} {
lappend wanted $cap
}
}
if {${sasl-user} != ""} {
lappend wanted "sasl"
}
if {[llength $wanted]} {
set wanted [join $wanted " "]
putlog "Requesting caps: $wanted"
putnow "CAP REQ :$wanted"
} else {
putnow "CAP END"
}
}
ACK {
putlog "Server enabled caps: $caps"
if {[lsearch -exact $caps "sasl"] != -1} {
sasl:start [sasl:get-first-mech]
} else {
putnow "CAP END"
}
}
NAK {
putlog "Server rejected caps: $caps"
if {[lsearch -exact $caps "sasl"] != -1} {
sasl:panic "Server refused SASL support"
} else {
putnow "CAP END"
}
}
}
return 1
}
## Raw IRC-SASL commands
proc raw:AUTHENTICATE {from keyword rest} {
sasl:step $rest
return 1
}
proc numeric:sasl-logged-in {from keyword rest} {
set vec [rparse $rest]
putlog "Authenticated to services as [lindex $vec 2]."
return 1
}
proc numeric:sasl-success {from keyword rest} {
putnow "CAP END"
return 1
}
proc numeric:sasl-failed {from keyword rest} {
set mech [sasl:get-next-mech]
if {$mech != "*"} {
putlog "Authentication failed, trying next mechanism"
sasl:start $mech
} else {
sasl:panic "Authentication failed"
}
return 1
}
proc numeric:sasl-mechlist {from keyword rest} {
global sasl-mechs
set vec [rparse $rest]
set sasl-mechs [lindex $vec 2]
return 1
}
## SASL mechanism functions
proc sasl:get-first-mech {} {
global sasl-use-mechs
global sasl-mechs
global sasl-midx
set sasl-mechs ${sasl-use-mechs}
set sasl-midx 0
return [lindex ${sasl-use-mechs} 0]
}
proc sasl:get-next-mech {} {
global sasl-use-mechs
global sasl-mechs
global sasl-midx
while {[incr sasl-midx] < [llength ${sasl-use-mechs}]} {
set mech [lindex ${sasl-use-mechs} ${sasl-midx}]
if {[lsearch -exact ${sasl-mechs} $mech] != -1} {
return $mech
}
}
return "*"
}
proc sasl:start {mech} {
global sasl-mech
global sasl-step
if {[info procs sasl:step:$mech] == ""} {
putlog "ERROR: Mechanism '$mech' is not supported by this script!"
putnow "AUTHENTICATE *"
return
}
set sasl-mech $mech
set sasl-step 0
putlog "Starting SASL $mech authentication"
sasl:step ""
}
proc sasl:step {data} {
global sasl-mech
global sasl-step
if {${sasl-step} == 0} {
putnow "AUTHENTICATE ${sasl-mech}"
} else {
set out [sasl:step:${sasl-mech} ${sasl-step} $data]
set len 400
set max [string length $out]
set ofs 0
while {$ofs < $max} {
set buf [string range $out $ofs [expr {$ofs + $len - 1}]]
incr ofs $len
putnow "AUTHENTICATE $buf"
}
if {$max == 0 || [string length $buf] == $len} {
putnow "AUTHENTICATE +"
}
}
set sasl-step [expr ${sasl-step} + 1]
}
proc sasl:panic {msg} {
global sasl-disconnect-on-fail
if {${sasl-disconnect-on-fail} == 1} {
putlog "$msg, disconnecting"
putnow "QUIT"
putnow "CAP END"
} else {
putlog "$msg, continuing anyway"
putnow "CAP END"
}
}
## SASL mechanism implementations
proc sasl:step:PLAIN {step data} {
global sasl-user
global sasl-pass
if {$step == 1 && $data == "+"} {
set out [join [list ${sasl-user} ${sasl-user} ${sasl-pass}] "\0"]
return [b64:encode $out]
} else {
return "*"
}
}
proc sasl:step:EXTERNAL {step data} {
global sasl-user
if {$step == 1 && $data == "+"} {
return [b64:encode ${sasl-user}]
} else {
return "*"
}
}
## Event bindings
bind EVNT - preinit-server cap:on-connect
bind EVNT - init-server cap:on-connect
bind raw - "CAP" raw:CAP
bind raw - "AUTHENTICATE" raw:AUTHENTICATE
bind raw - "900" numeric:sasl-logged-in
bind raw - "903" numeric:sasl-success
bind raw - "904" numeric:sasl-failed
bind raw - "908" numeric:sasl-mechlist
# EOF
# g_ircv3.tcl - Experimental IRCv3 extension support
# (c) 2013 Mantas Mikulėnas <grawity@gmail.com>
# Released under the MIT Expat License.
#
# Requires: g_cap.tcl
## Configurable procs -- adjust to your needs
proc user:account-changed {from account} {
set nuh [split $from "!"]
set nick [lindex $nuh 0]
if {$account == "*"} {
putlog "$nick logged out"
} else {
putlog "$nick logged in as $account"
}
}
proc user:gecos-changed {from gecos} {
set nuh [split $from "!"]
set nick [lindex $nuh 0]
putlog "$nick is actually \"$gecos\""
}
proc user:away-changed {from reason} {
set nuh [split $from "!"]
set nick [lindex $nuh 0]
if {$reason == ""} {
putlog "$nick is back"
} else {
putlog "$nick is away"
}
}
## Setup -- do not edit anything below
if {[info procs raw:CAP] == ""} {
die "You must load g_cap.tcl first."
}
lappend caps-wanted {account-notify away-notify extended-join}
proc raw:ACCOUNT {from keyword rest} {
user:account-changed $from $rest
return 1
}
proc raw:JOIN-extended {from keyword rest} {
set nuh [split $from "!"]
set vec [rparse $rest]
if {[llength $vec] > 1} {
set account [lindex $vec 1]
set gecos [lindex $vec 2]
user:account-changed $from $account
user:gecos-changed $from $gecos
}
return 0
}
proc raw:AWAY {from keyword rest} {
set vec [rparse $rest]
user:away-changed $from [lindex $vec 0]
return 0
}
bind raw - "AWAY" raw:AWAY
bind raw - "JOIN" raw:JOIN-extended
bind raw - "ACCOUNT" raw:ACCOUNT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment