2010-12-10 17:03:24 -07:00
|
|
|
let debug = prerr_endline
|
|
|
|
|
|
|
|
let file_descr_of_int (i:int) =
|
|
|
|
let blob = Marshal.to_string i [] in
|
2010-12-10 17:07:16 -07:00
|
|
|
(Marshal.from_string blob 0 : Unix.file_descr)
|
2010-12-10 17:03:24 -07:00
|
|
|
|
2009-02-08 20:25:27 -07:00
|
|
|
let write iobuf command args text =
|
|
|
|
let cmd = Command.create None command args text in
|
|
|
|
Iobuf.write iobuf cmd
|
|
|
|
|
2010-12-09 08:22:44 -07:00
|
|
|
let msg iobuf recip text =
|
|
|
|
write iobuf "PRIVMSG" [recip] (Some text)
|
|
|
|
|
2010-12-10 17:03:24 -07:00
|
|
|
let split = Str.split (Str.regexp "[ \t]*\r?\n")
|
|
|
|
|
|
|
|
(** Callback upon completion of the external command helper *)
|
|
|
|
let extern_callback iobuf sender forum text =
|
|
|
|
let lines = split text in
|
|
|
|
let nlines = List.length lines in
|
|
|
|
let recip =
|
|
|
|
if (nlines > 5) then begin
|
|
|
|
if (forum <> sender) then
|
|
|
|
msg iobuf forum (Format.sprintf "%d lines, sending privately" nlines);
|
|
|
|
sender
|
|
|
|
end else
|
|
|
|
forum
|
|
|
|
in
|
|
|
|
let rec f = function
|
|
|
|
| [] ->
|
|
|
|
()
|
|
|
|
| "" :: tl ->
|
|
|
|
f tl
|
|
|
|
| line :: tl ->
|
|
|
|
if line.[0] == ':' then
|
|
|
|
(* Interpret as raw IRC commands *)
|
|
|
|
let ine = Str.string_after line 1 in
|
|
|
|
let cmd = Command.from_string ine in
|
|
|
|
Iobuf.write iobuf cmd
|
|
|
|
else
|
|
|
|
(* Naive case: send to the recipient *)
|
|
|
|
msg iobuf recip line;
|
|
|
|
f tl
|
|
|
|
in
|
|
|
|
f lines
|
2010-12-09 08:22:44 -07:00
|
|
|
|
2010-12-14 17:13:52 -07:00
|
|
|
let nick_of_nuhost s =
|
|
|
|
try
|
|
|
|
Irc.nick (Irc.nuhost_of_string s)
|
|
|
|
with Not_found ->
|
|
|
|
s
|
|
|
|
|
|
|
|
let handle_command outbuf handle_cmd thisbuf cmd =
|
|
|
|
let (prefix, command, args, trailing) = Command.as_tuple cmd in
|
|
|
|
let (sender, forum) =
|
|
|
|
match (prefix, command, args, trailing) with
|
|
|
|
| (Some suhost, "PRIVMSG", [target], _)
|
|
|
|
| (Some suhost, "NOTICE", [target], _) ->
|
|
|
|
let sender = nick_of_nuhost suhost in
|
|
|
|
let forum = if Irc.is_channel target then target else sender in
|
|
|
|
(sender, forum)
|
|
|
|
|
|
|
|
(* Here's why the IRC protocol blows: *)
|
|
|
|
| (Some suhost, "PART", [forum], _)
|
|
|
|
| (Some suhost, "JOIN", [forum], _)
|
|
|
|
| (Some suhost, "MODE", forum :: _, _)
|
|
|
|
| (Some suhost, "INVITE", _, Some forum)
|
|
|
|
| (Some suhost, "TOPIC", forum :: _, _)
|
|
|
|
| (Some suhost, "KICK", forum :: _, _) ->
|
|
|
|
(nick_of_nuhost suhost, forum)
|
|
|
|
|
|
|
|
| (Some suhost, "JOIN", [], Some chan) ->
|
|
|
|
(nick_of_nuhost suhost, chan)
|
|
|
|
|
|
|
|
| (Some _, "NICK", [sender], _) ->
|
|
|
|
(sender, sender)
|
|
|
|
|
|
|
|
| (Some suhost, "QUIT", _, _)
|
|
|
|
| (Some suhost, _, _, _) ->
|
|
|
|
let sender = nick_of_nuhost suhost in
|
|
|
|
(sender, sender)
|
|
|
|
|
|
|
|
| (_, "PING", _, Some text) ->
|
|
|
|
write outbuf "PONG" [] (Some text);
|
|
|
|
("", "")
|
|
|
|
|
|
|
|
| (None, _, _, _) ->
|
|
|
|
("", "")
|
|
|
|
in
|
|
|
|
let pfx =
|
|
|
|
match prefix with
|
|
|
|
| Some txt -> txt
|
|
|
|
| None -> ""
|
|
|
|
in
|
|
|
|
let text =
|
|
|
|
match trailing with
|
|
|
|
| Some txt -> txt
|
|
|
|
| None -> ""
|
|
|
|
in
|
|
|
|
let argv =
|
|
|
|
Array.append
|
|
|
|
[|handle_cmd; sender; forum; pfx; command|]
|
|
|
|
(Array.of_list args)
|
|
|
|
in
|
2010-12-10 17:03:24 -07:00
|
|
|
Process.create_canned
|
2010-12-14 17:13:52 -07:00
|
|
|
(Iobuf.dispatcher thisbuf)
|
2010-12-10 17:03:24 -07:00
|
|
|
text
|
2010-12-14 17:13:52 -07:00
|
|
|
(extern_callback outbuf sender forum)
|
|
|
|
handle_cmd
|
|
|
|
argv
|
|
|
|
|
2009-02-08 20:25:27 -07:00
|
|
|
|
2010-12-10 17:03:24 -07:00
|
|
|
let discard_command iobuf cmd = ()
|
|
|
|
|
2009-02-08 20:25:27 -07:00
|
|
|
let handle_error iobuf str =
|
2010-12-10 17:03:24 -07:00
|
|
|
prerr_endline ("!!! " ^ str)
|
2009-02-08 20:25:27 -07:00
|
|
|
|
|
|
|
let main () =
|
2010-12-14 17:13:52 -07:00
|
|
|
let handler = ref "/bin/true" in
|
|
|
|
let inputfn = ref "" in
|
|
|
|
let nick = ref "bot" in
|
|
|
|
let user = ref "bot" in
|
|
|
|
let mode = ref "+i" in
|
|
|
|
let realname = ref "I'm a little printf, short and stdout" in
|
|
|
|
let connect = ref [||] in
|
|
|
|
let append_connect s = connect := Array.append !connect [|s|] in
|
|
|
|
let speclist =
|
|
|
|
[
|
|
|
|
("-n", Arg.Set_string nick, "Nickname");
|
|
|
|
("-u", Arg.Set_string user, "Username");
|
|
|
|
("-m", Arg.Set_string mode, "Mode");
|
|
|
|
("-r", Arg.Set_string realname, "Real name");
|
|
|
|
("-a", Arg.Set_string handler, "IRC message handler");
|
|
|
|
("-i", Arg.Set_string inputfn, "Command FIFO");
|
|
|
|
]
|
2010-12-10 17:07:16 -07:00
|
|
|
in
|
2010-12-14 17:13:52 -07:00
|
|
|
let usage = "usage: bot [OPTIONS] CONNECT-COMMAND [ARGS ...]" in
|
|
|
|
Arg.parse speclist append_connect usage;
|
|
|
|
if (Array.length !connect) < 1 then begin
|
|
|
|
prerr_endline "Error: must specify connect command.";
|
|
|
|
prerr_endline "";
|
|
|
|
prerr_endline "Run with --help for usage information.";
|
|
|
|
exit 64 (* EX_USAGE *)
|
|
|
|
end;
|
|
|
|
|
|
|
|
let dispatcher = Dispatch.create () in
|
|
|
|
let conn_out, conn_in = Process.spawn (!connect).(0) !connect in
|
|
|
|
let iobuf_out = Iobuf.create dispatcher conn_out "out"
|
|
|
|
discard_command
|
|
|
|
handle_error
|
|
|
|
in
|
|
|
|
let _ = Iobuf.create dispatcher conn_in "in"
|
|
|
|
(handle_command iobuf_out !handler)
|
|
|
|
handle_error
|
|
|
|
in
|
|
|
|
write iobuf_out "NICK" [!nick] None;
|
|
|
|
write iobuf_out "USER" [!user; !mode; "merf"] (Some !realname);
|
|
|
|
Dispatch.run dispatcher
|
2009-02-08 20:25:27 -07:00
|
|
|
|
|
|
|
let _ =
|
|
|
|
main ()
|