diff --git a/Makefile b/Makefile index 1a75df0..00d33ee 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ OCAMLC = ocamlc OCAMLDEP = ocamldep $(INCLUDES) OCAMLLIBS = unix.cma str.cma nums.cma -bot: irc.cmo dispatch.cmo command.cmo iobuf.cmo cdb.cmo bindings.cmo plugin.cmo infobot.cmo bot.cmo infobot.cmo +bot: irc.cmo dispatch.cmo command.cmo iobuf.cmo cdb.cmo bindings.cmo infobot.cmo bot.cmo infobot.cmo $(OCAMLC) -o $@ $(OCAMLLIBS) $^ .PHONY: clean diff --git a/bot.ml b/bot.ml index b11b9b4..548f411 100644 --- a/bot.ml +++ b/bot.ml @@ -1,10 +1,15 @@ +type bot = { + store: Infobot.t; +} + let write iobuf command args text = let cmd = Command.create None command args text in print_endline ("--> " ^ (Command.as_string cmd)); Iobuf.write iobuf cmd -let handle_command iobuf cmd = +let handle_command bot iobuf cmd = print_endline ("<-- " ^ (Command.as_string cmd)); + Infobot.handle_command bot.store iobuf cmd; match Command.as_tuple cmd with | (_, "PING", _, text) -> write iobuf "PONG" [] text @@ -22,9 +27,12 @@ let main () = let host = Unix.gethostbyname "woozle.org" in let dispatcher = Dispatch.create 5 in let conn = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in + let bot = {store = Infobot.create "info.cdb"} in let _ = Unix.connect conn (Unix.ADDR_INET (host.Unix.h_addr_list.(0), 6667)) in - let iobuf = Iobuf.create dispatcher conn "woozle" Plugin.handle_command handle_error in - Plugin.register handle_command; + let iobuf = Iobuf.create dispatcher conn "woozle" + (handle_command bot) + handle_error + in write iobuf "NICK" ["bot"] None; write iobuf "USER" ["bot"; "bot"; "bot"] (Some "A Bot"); Dispatch.run dispatcher diff --git a/dispatch.ml b/dispatch.ml index 6dacc69..3b4d636 100644 --- a/dispatch.ml +++ b/dispatch.ml @@ -22,14 +22,6 @@ type t = { timers : Timer.t ref; } -(* select(), poll(), and epoll() treat timeout as an upper bound of time - to wait. This fudge factor helps ensure that given no FD activity, - this isn't run in a tight loop as a timer approaches. This value was - determined experimentally on a 1.25GHz G4 PPC to work most of the - time. Your mileage may vary. *) - -let timeout_fudge = 0.001 - let create size = {read_fds = ref []; write_fds = ref []; @@ -88,7 +80,7 @@ let delete_timer d time = let rec dispatch_timers d now = - if (!(d.timers) != Timer.empty) then + if not (Timer.is_empty !(d.timers)) then let (time, handler) = Timer.min_elt !(d.timers) in if now < time then () @@ -113,28 +105,29 @@ let rec dispatch_results d (read_ready, write_ready, except_ready) = dispatch Exception except_ready let once d = - let now = Unix.gettimeofday () in - let timeout = + (* You might think it'd work better to use the timeout of select(). + Not so! select() waits *at most* timeout ms. Doing things + this way results in a tight loop as the timer approaches. *) + let interval = try - let (time, _) = Timer.min_elt !(d.timers) in - let delta = (time -. now +. timeout_fudge) in + let (next, _) = Timer.min_elt !(d.timers) in + let delta = (next -. (Unix.gettimeofday ())) in max delta 0.0 with Not_found -> - (-1.0) + 0.0 in - (* select () waits *at most* timeout ms. If you have fds but they're -not - doing anything, multiple calls to once may be required. This is - lame. *) - let result = Unix.select !(d.read_fds) !(d.write_fds) !(d.except_fds) timeout in + let s = { Unix.it_interval = interval; Unix.it_value = 0.0 } in + let _ = Sys.set_signal Sys.sigalrm Sys.Signal_ignore in + let _ = Unix.setitimer Unix.ITIMER_REAL s in + let result = Unix.select !(d.read_fds) !(d.write_fds) !(d.except_fds) (-1.0) in dispatch_results d result; dispatch_timers d (Unix.gettimeofday ()) -let rec run d = - if ((!(d.handlers) == Fd_map.empty) && - (!(d.timers) == Timer.empty)) then +let rec run d = + if (Fd_map.is_empty !(d.handlers)) && (Timer.is_empty !(d.timers)) then () else begin once d; run d end + diff --git a/infobot.ml b/infobot.ml index 2f0e9ef..3e73592 100644 --- a/infobot.ml +++ b/infobot.ml @@ -1,19 +1,56 @@ -let info_db = Cdb.open_cdb_in "/home/neale/src/firebot/info.cdb" +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 choose_one key = - let matches = Cdb.get_matches info_db key in - match Stream.npeek 120 matches with - | [] -> raise Not_found - | keys -> choice keys +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 handle_privmsg iobuf sender target text = +let choose_one ib key = + match (Cdb.get_matches ib.db key) with + | [] -> + raise Not_found + | keys -> + choice keys + +let handle_privmsg store iobuf sender target text = try - let factoid = choose_one text in + 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 let response = match factoid.[0] with | ':' -> @@ -21,20 +58,16 @@ let handle_privmsg iobuf sender target text = | '\\' -> Str.string_after factoid 1 | _ -> - Printf.sprintf "I've heard that %s is %s" text factoid + Printf.sprintf "I overheard that %s is %s" text factoid in Iobuf.write iobuf (Command.create None "PRIVMSG" [target] (Some response)) with Not_found -> () -let handle_command iobuf cmd = - print_endline (" if Irc.is_channel target then - handle_privmsg iobuf sender target text + handle_privmsg store iobuf sender target text | _ -> () - -let _ = Plugin.register handle_command -let _ = print_endline "========= INFOBOT" diff --git a/plugin.ml b/plugin.ml deleted file mode 100644 index f14d71e..0000000 --- a/plugin.ml +++ /dev/null @@ -1,24 +0,0 @@ -type handler = Iobuf.t -> Command.t -> unit - -let handlers = ref [] - -let register handler = - handlers := !handlers @ [handler] - -let unregister handler = - handlers := List.filter ((<>) handler) !handlers - -let handle_command iobuf cmd = - let rec loop h = - match h with - | [] -> () - | handler :: tl -> - begin - try - handler iobuf cmd - with _ -> - () - end; - loop tl - in - loop !handlers diff --git a/plugin.mli b/plugin.mli deleted file mode 100644 index e1380f9..0000000 --- a/plugin.mli +++ /dev/null @@ -1,5 +0,0 @@ -type handler = Iobuf.t -> Command.t -> unit - -val register : handler -> unit -val unregister : handler -> unit -val handle_command : Iobuf.t -> Command.t -> unit