diff --git a/Makefile b/Makefile index 07e167f..023f39c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ OCAMLC = ocamlc -g OCAMLDEP = ocamldep $(INCLUDES) OCAMLLIBS = unix.cma str.cma nums.cma -bot: irc.cmo dispatch.cmo process.cmo command.cmo iobuf.cmo cdb.cmo bindings.cmo infobot.cmo bot.cmo infobot.cmo +bot: irc.cmo dispatch.cmo process.cmo command.cmo iobuf.cmo bot.cmo $(OCAMLC) -o $@ $(OCAMLLIBS) $^ .PHONY: clean diff --git a/bindings.ml b/bindings.ml deleted file mode 100644 index f291913..0000000 --- a/bindings.ml +++ /dev/null @@ -1,40 +0,0 @@ -type callback = Iobuf.t -> Command.t -> unit -type t = (string * Str.regexp * callback) list - -let create () = [] - -let remove b id = - let keep = function - | (id', _, _) -> - id' <> id - in - List.filter keep b - -let add b id regex cb = - (id, regex, cb) :: (remove b id) - -let lookup b text = - let rec groups str i acc = - try - groups str (i + 1) ((Str.matched_group i str) :: acc) - with - | Not_found -> - groups str (i + 1) ("" :: acc) - | Invalid_argument _ -> - List.rev acc - in - let rec loop b acc = - match b with - | [] -> - List.rev acc - | (id, regex, cb) :: tl -> - try - ignore (Str.search_forward regex text 0); - loop tl ((id, cb, groups text 0 []) :: acc) - with Not_found -> - loop tl acc - in - loop b [] - - - diff --git a/bot.ml b/bot.ml index f0c27e1..32a7343 100644 --- a/bot.ml +++ b/bot.ml @@ -31,7 +31,7 @@ let extern_callback iobuf sender forum text = | "" :: tl -> f tl | line :: tl -> - if line.[0] == ':' then + if line.[0] == '\007' then (* Interpret as raw IRC commands *) let ine = Str.string_after line 1 in let cmd = Command.from_string ine in @@ -63,6 +63,7 @@ let handle_command outbuf handle_cmd thisbuf cmd = | (Some suhost, "PART", [forum], _) | (Some suhost, "JOIN", [forum], _) | (Some suhost, "MODE", forum :: _, _) + | (Some suhost, "INVITE", [_; forum], None) | (Some suhost, "INVITE", _, Some forum) | (Some suhost, "TOPIC", forum :: _, _) | (Some suhost, "KICK", forum :: _, _) -> diff --git a/cdb.ml b/cdb.ml deleted file mode 100644 index 12ed011..0000000 --- a/cdb.ml +++ /dev/null @@ -1,310 +0,0 @@ -(* - * Copyright (c) 2003 Dustin Sallings - * - * arch-tag: 1E3B7401-2AE1-11D8-A379-000393CB0F1E - *) - -(** CDB Implementation. - {{:http://cr.yp.to/cdb/cdb.txt} http://cr.yp.to/cdb/cdb.txt} - *) - -(* The cdb hash function is ``h = ((h << 5) + h) ^ c'', with a starting - hash of 5381. - *) - -(** CDB creation handle. *) -type cdb_creator = { - table_count: int array; - (* Hash index pointers *) - mutable pointers: (Int32.t * Int32.t) list; - out: out_channel; -} - -(** Initial hash value *) -let hash_init = Int64.of_int 5381 - -let ff64 = Int64.of_int 0xff -(* I need to do this of_string because it's larger than an ocaml int *) -let ffffffff64 = Int64.of_string "0xffffffff" -let ff32 = Int32.of_int 0xff - -(** Hash the given string. *) -let hash s = - let h = ref hash_init in - String.iter (fun c -> h := Int64.logand ffffffff64 (Int64.logxor - (Int64.add (Int64.shift_left !h 5) !h) - (Int64.of_int (int_of_char c))) - ) s; - Int64.to_int32 !h - -let write_le cdc i = - output_byte cdc.out (i land 0xff); - output_byte cdc.out ((i lsr 8) land 0xff); - output_byte cdc.out ((i lsr 16) land 0xff); - output_byte cdc.out ((i lsr 24) land 0xff) - -(** Write a little endian integer to the file *) -let write_le32 cdc i = - output_byte cdc.out (Int32.to_int (Int32.logand ff32 i)); - output_byte cdc.out (Int32.to_int - (Int32.logand ff32 (Int32.shift_right_logical i 8))); - output_byte cdc.out (Int32.to_int - (Int32.logand ff32 (Int32.shift_right_logical i 16))); - output_byte cdc.out (Int32.to_int - (Int32.logand ff32 (Int32.shift_right_logical i 24))) - -(** - Open a cdb creator for writing. - - @param fn the file to write - *) -let open_out fn = - let s = { table_count=Array.make 256 0; - pointers=[]; - out=open_out_bin fn - } in - (* Skip over the header *) - seek_out s.out 2048; - s - -(** - Convert out_channel to cdb_creator. - - @param out_channel the out_channel to convert - *) -let cdb_creator_of_out_channel out_channel = - let s = { table_count=Array.make 256 0; - pointers=[]; - out=out_channel - } in - (* Skip over the header *) - seek_out s.out 2048; - s - -let hash_to_table h = - Int32.to_int (Int32.logand h ff32) - -let hash_to_bucket h len = - Int32.to_int (Int32.rem (Int32.shift_right_logical h 8) (Int32.of_int len)) - -let pos_out_32 x = - Int64.to_int32 (LargeFile.pos_out x) - -(** Add a value to the cdb *) -let add cdc k v = - (* Add the hash to the list *) - let h = hash k in - cdc.pointers <- (h, pos_out_32 cdc.out) :: cdc.pointers; - let table = hash_to_table h in - cdc.table_count.(table) <- cdc.table_count.(table) + 1; - - (* Add the data to the file *) - write_le cdc (String.length k); - write_le cdc (String.length v); - output_string cdc.out k; - output_string cdc.out v - -(** Process a hash table *) -let process_table cdc table_start slot_table slot_pointers i tc = - (* Length of the table *) - let len = tc * 2 in - (* Store the table position *) - slot_table := (pos_out_32 cdc.out, Int32.of_int len) :: !slot_table; - (* Build the hash table *) - let ht = Array.make len None in - let cur_p = ref table_start.(i) in - (* Lookup entries by slot number *) - let lookupSlot x = - try Hashtbl.find slot_pointers x - with Not_found -> (Int32.zero,Int32.zero) - in - (* from 0 to tc-1 because the loop will run an extra time otherwise *) - for u = 0 to (tc - 1) do - let hp = lookupSlot !cur_p in - cur_p := !cur_p + 1; - - (* Find an available hash bucket *) - let rec find_where where = - match ht.(where) with - | None -> - where - | _ -> - if ((where + 1) = len) then (find_where 0) - else (find_where (where + 1)) - in - let where = find_where (hash_to_bucket (fst hp) len) in - ht.(where) <- Some hp; - done; - (* Write this hash table *) - Array.iter (fun hpp -> - let h,t = match hpp with - None -> Int32.zero,Int32.zero - | Some(h,t) -> h,t; - in - write_le32 cdc h; write_le32 cdc t - ) ht - -(** Close and finish the cdb creator. *) -let close_cdb_out cdc = - let cur_entry = ref 0 in - let table_start = Array.make 256 0 in - (* Find all the hash starts *) - Array.iteri (fun i x -> - cur_entry := !cur_entry + x; - table_start.(i) <- !cur_entry) cdc.table_count; - (* Build out the slot pointers hash *) - let slot_pointers = Hashtbl.create (List.length cdc.pointers) in - (* Fill in the slot pointers *) - List.iter (fun hp -> - let h = fst hp in - let table = hash_to_table h in - table_start.(table) <- table_start.(table) - 1; - Hashtbl.replace slot_pointers table_start.(table) hp; - ) cdc.pointers; - (* Write the shit out *) - let slot_table = ref [] in - (* Write out the hash tables *) - Array.iteri (process_table cdc table_start slot_table slot_pointers) - cdc.table_count; - (* write out the pointer sets *) - seek_out cdc.out 0; - List.iter (fun x -> write_le32 cdc (fst x); write_le32 cdc (snd x)) - (List.rev !slot_table); - close_out cdc.out - -(** {1 Iterating a cdb file} *) - -(* read a little-endian integer *) -let read_le f = - let a = (input_byte f) in - let b = (input_byte f) in - let c = (input_byte f) in - let d = (input_byte f) in - a lor (b lsl 8) lor (c lsl 16) lor (d lsl 24) - -(* Int32 version of read_le *) -let read_le32 f = - let a = (input_byte f) in - let b = (input_byte f) in - let c = (input_byte f) in - let d = (input_byte f) in - Int32.logor (Int32.of_int (a lor (b lsl 8) lor (c lsl 16))) - (Int32.shift_left (Int32.of_int d) 24) - -(** - Iterate a CDB. - - @param f the function to call for every key/value pair - @param fn the name of the cdb to iterate - *) -let iter f fn = - let fin = open_in_bin fn in - try - (* Figure out where the end of all data is *) - let eod = read_le32 fin in - (* Seek to the record section *) - seek_in fin 2048; - let rec loop() = - (* (pos_in fin) < eod *) - if (Int32.compare (Int64.to_int32 (LargeFile.pos_in fin)) eod < 0) - then ( - let klen = read_le fin in - let dlen = read_le fin in - let key = String.create klen in - let data = String.create dlen in - really_input fin key 0 klen; - really_input fin data 0 dlen; - f key data; - loop() - ) in - loop(); - close_in fin; - with x -> close_in fin; raise x; - -(** {1 Searching } *) - -(** Type type of a cdb_file. *) -type cdb_file = { - f: in_channel; - (* Position * length *) - tables: (Int32.t * int) array; -} - -(** Open a CDB file for searching. - - @param fn the file to open - *) -let open_cdb_in fn = - let fin = open_in_bin fn in - let tables = Array.make 256 (Int32.zero,0) in - (* Set the positions and lengths *) - Array.iteri (fun i it -> - let pos = read_le32 fin in - let len = read_le fin in - tables.(i) <- (pos,len) - ) tables; - {f=fin; tables=tables} - -(** - Close a cdb file. - - @param cdf the cdb file to close - *) -let close_cdb_in cdf = - close_in cdf.f - -(** Get a list of matches. - - @param cdf the cdb file - @param key the key to search - *) -let get_matches cdf key = - let kh = hash key in - (* Find out where the hash table is *) - let hpos, hlen = cdf.tables.(hash_to_table kh) in - let rec loop x acc = - if (x >= hlen) then - acc - else - let acc' = - (* Calculate the slot containing these entries *) - let lslot = ((hash_to_bucket kh hlen) + x) mod hlen in - let spos = Int32.add (Int32.of_int (lslot * 8)) hpos in - let _ = LargeFile.seek_in cdf.f (Int64.of_int32 spos) in - let h = read_le32 cdf.f in - let pos = read_le32 cdf.f in - (* validate that we a real bucket *) - if (h = kh) && ((Int32.compare pos Int32.zero) > 0) then - let _ = LargeFile.seek_in cdf.f (Int64.of_int32 pos) in - let klen = read_le cdf.f in - if (klen = String.length key) then - let dlen = read_le cdf.f in - let rkey = String.create klen in - really_input cdf.f rkey 0 klen; - if (rkey = key) then - let rdata = String.create dlen in - really_input cdf.f rdata 0 dlen; - rdata :: acc - else - acc - else - acc - else - acc - in - loop (x + 1) acc' - in - List.rev (loop 0 []) - -(** - Find the first record with the given key. - - @param cdf the cdb_file - @param key the key to find - *) -let find cdf key = - match (get_matches cdf key) with - | [] -> - raise Not_found - | r :: _ -> - r diff --git a/cdb.mli b/cdb.mli deleted file mode 100644 index 2927705..0000000 --- a/cdb.mli +++ /dev/null @@ -1,40 +0,0 @@ - -(** CDB Implementation. - {{:http://cr.yp.to/cdb/cdb.txt} http://cr.yp.to/cdb/cdb.txt} - *) - -(** {1 Utilities} *) -val hash : string -> int32 - -(** {1 Building a CDB } *) - -type cdb_creator = { - table_count : int array; - mutable pointers : (Int32.t * Int32.t) list; - out : out_channel; -} -val open_out : string -> cdb_creator -val cdb_creator_of_out_channel : Pervasives.out_channel -> cdb_creator -val add : cdb_creator -> string -> string -> unit -val close_cdb_out : cdb_creator -> unit - -(** {1 Iterating a CDB } *) - -val iter : (string -> string -> unit) -> string -> unit - -(** {1 Searching } *) - -type cdb_file = { - f: in_channel; - tables: (Int32.t * int) array; -} - -val open_cdb_in : string -> cdb_file -val close_cdb_in : cdb_file -> unit - -val get_matches : cdb_file -> string -> string list -val find : cdb_file -> string -> string - -(* - * arch-tag: 55F4CBF0-2B50-11D8-BEDC-000393CFE6B8 - *) diff --git a/cobalt b/cobalt new file mode 100755 index 0000000..4967985 --- /dev/null +++ b/cobalt @@ -0,0 +1,7 @@ +#! /bin/sh + +exec ./bot \ + -n cobalt \ + -u cobalt \ + -a ./cobalt-handler \ + socat STDIO OPENSSL:woozle.org:994,verify=0 diff --git a/cobalt-handler b/cobalt-handler new file mode 100755 index 0000000..5b3f425 --- /dev/null +++ b/cobalt-handler @@ -0,0 +1,24 @@ +#! /bin/sh + +sender=$1; export sender; shift +forum=$1; export forum; shift +prefix=$1; export prefix; shift +command=$1; export command; shift +# $* is now args +text=$(cat) + +case $command in + 001) + printf '\aJOIN #woozle\n' + printf '\aJOIN #foozle\n' + printf '\aJOIN #bot\n' + ;; + PRIVMSG) + ./firebot "$text" || \ + ./infobot woozle.cdb "$text" + ;; + INVITE) + printf '\aJOIN %s\n' "$forum" + ;; +esac + diff --git a/firebot b/firebot new file mode 100755 index 0000000..0666c11 --- /dev/null +++ b/firebot @@ -0,0 +1,37 @@ +#! /bin/sh + +firebot () { + read cmd args + case $cmd in + calc) + printf "%s = " "$args" + echo "$args" | bc -l + ;; + units) + src=$(echo "$args" | sed 's/ ->.*//') + dst=$(echo "$args" | sed 's/.*-> //') + units -1 -v -- "$src" "$dst" + ;; + .note) + who=$(echo $args | cut -d\ -f1 | tr -d -c A-Za-z0-9) + what=$(echo $args | cut -d\ -f2-) + when=$(date) + echo "($when) <$sender> $what" > notes/$who + echo "I've left $who a note, $sender." + ;; + *) + exit 1 + ;; + esac +} + +if [ -f notes/$sender ]; then + echo "Welcome back, $sender. Your messages:" + cat notes/$sender + rm notes/$sender +fi + +echo "$1" | firebot + + + diff --git a/infobot b/infobot index 099bc7c..573c741 100755 --- a/infobot +++ b/infobot @@ -35,10 +35,9 @@ EOF cdb -s $db | head -n 1 ;; !l*) - printf "%s is:" "$args" - cdb -q -m $db "$args" | while read v; do - printf " \"%s\" |" "$v" - done + printf "%s" "$args" + cdb -q -m $db "$args" | \ + awk '{printf(" | \"%s\"", $0);}' echo ;; !a*) @@ -46,40 +45,35 @@ EOF val=$(printf "%s" "$args" | sed 's/.*+= //') (printf "+%d,%d:%s->%s\n" ${#key} ${#val} "$key" "$val"; cdb -d $db) | cdb -c $db + echo "Okay, $sender, I added a factoid to $key" ;; !r*) key=$(printf "%s" "$args" | sed 's/ -=.*//') val=$(printf "%s" "$args" | sed 's/.*-= //') - gs=":$key->.*$val" - case $(cdb -d $db | grep -c "$gs") in - 0) - echo 'No matches' - ;; - 1) - cdb -d $db | grep -v "$gs" | cdb -c $db - echo "I removed $val from $key" - ;; - *) - echo 'That maches more than one entry; please refine your request' - ;; - esac + re=":$key->.*$val" + n=$(cdb -d $db | grep -c "$re") + if [ $n -gt 0 ]; then + cdb -d $db | grep -a -v ":$key->.*$val" | cdb -c $db + echo "Okay, $sender, I removed $n factoids from $key" + else + echo "Nothing matched, $sender." + fi ;; !f*) - cdb -d $db | fgrep -v ":$args->" | cdb -c $db - echo "I forgot $args" + cdb -d $db | grep -a -F -v ":$args->" | cdb -c $db + echo "I removed all factoids from $args" ;; *) resp=$(lookup "$text" | shuf -n 1 | sed "s/\$sender/$sender/") case "$resp" in "") + exit 1 ;; \\*) - echo "$resp" | cut -b2- + printf "%s" "$resp" | cut -b2- ;; :*) - echo -ne '\001ACTION ' - echo -n $(echo "$resp" | cut -b2-) - echo -e '\001' + printf '\001ACTION %s\001\n' "$(echo "$resp" | cut -b2-)" ;; *) echo "It's been said that $text is $resp" diff --git a/infobot.ml b/infobot.ml deleted file mode 100644 index 2ee031e..0000000 --- a/infobot.ml +++ /dev/null @@ -1,70 +0,0 @@ -type t = { - filename: string; - mutable db: Cdb.cdb_file; -} - -let _ = Random.self_init () - -let create filename = - { - filename = filename; - db = Cdb.open_cdb_in filename; - } - -let choice l = - let n = Random.int (List.length l) in - List.nth l n - -let strip s = - let rec lastchar n = - match s.[n-1] with - | '.' - | '!' - | '?' - | ' ' -> - lastchar (n - 1) - | _ -> - n - in - let len = lastchar (String.length s) in - if (len = String.length s) then - None - else - Some (String.sub s 0 len) - -let choose_one ib key = - match (Cdb.get_matches ib.db key) with - | [] -> - raise Not_found - | keys -> - choice keys - -let lookup store text = - try - let text, factoid = - try - (text, choose_one store text) - with Not_found -> - match (strip text) with - | None -> - raise Not_found - | Some stext -> - (stext, choose_one store stext) - in - match factoid.[0] with - | ':' -> - Some ("\001ACTION " ^ (Str.string_after factoid 1) ^ "\001") - | '\\' -> - Some (Str.string_after factoid 1) - | _ -> - Some (Printf.sprintf "I overheard that %s is %s" text factoid) - with Not_found -> - None - - -let handle_privmsg store msg sender forum text = - match (lookup store text) with - | Some reply -> - msg reply - | None -> - () diff --git a/process.ml b/process.ml index 2894d63..6847b15 100644 --- a/process.ml +++ b/process.ml @@ -8,7 +8,6 @@ let spawn prog args = Unix.close fd0_exit; Unix.dup2 fd1_entr Unix.stdout; - Unix.dup2 fd1_entr Unix.stderr; Unix.close fd1_entr; Unix.close fd1_exit;