adding all of botlist, initial add
[botlist.git] / botlistprojects / laughingman / orbirclib / src / irc_bot.erl
blobc3e81e7fb35c23ddb00f933ef1eee3b1c5dfd4a3
1 %%%-------------------------------------------------------------------
2 %%% File : irc_bot2.erl
3 %%% Author : ortitz <orbitz@blong.orbitz>
4 %%% Description : Irc bot gen_server
5 %%%
6 %%% Created : 23 Mar 2006 by ortitz <orbitz@blong.orbitz>
7 %%%-------------------------------------------------------------------
8 -module(irc_bot).
10 -include("db.hrl").
11 -include("irc.hrl").
13 -behaviour(gen_server).
15 %% API
16 -export([start_link/1]).
18 %% gen_server callbacks
19 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
20 terminate/2, code_change/3]).
22 %% API exports
23 -export([add_bot/1, say/3, stop/2]).
24 -export([get_irclib/1, get_nick/1, get_cur_state/1]).
26 -record(state, {nick, dict, state, irclib, pong_timeout=undefined,
27 connection_timeout, app_handler=undefined}).
29 %%====================================================================
30 %% API
31 %%====================================================================
32 %%--------------------------------------------------------------------
33 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
34 %% Description: Starts the server
35 %%--------------------------------------------------------------------
36 start_link(Client) ->
37 gen_server:start_link(?MODULE, [Client], []).
39 %%====================================================================
40 %% gen_server callbacks
41 %%====================================================================
43 %%--------------------------------------------------------------------
44 %% Function: init(Args) -> {ok, State} |
45 %% {ok, State, Timeout} |
46 %% ignore |
47 %% {stop, Reason}
49 %% Description: Initiates the server
50 %% Whenever a gen_server is started using gen_server:start/3,4
51 %% or gen_server:start_link/3,4, this function is called by the new process to initialize.
52 %%--------------------------------------------------------------------
53 init([Client]) ->
54 io:format("irc_bot:init~n"),
55 {ok, Ref} = timer:send_interval(connection_timeout(), timed_ping),
56 %ok = bot_manager:store(Client#irc_bot.botname, self()),
57 {ok, Pid} = irc_lib:start_link(
58 #irc_client_info{nick=Client#irc_bot.nick,
59 realname=Client#irc_bot.realname,
60 servers=Client#irc_bot.servers,
61 handler=self(),
62 password=Client#irc_bot.password}),
63 AppHandler = Client#irc_bot.handler,
64 io:format("irc_bot:init start_link.pid: ~p~n", [Pid]),
65 {ok, #state{irclib=Pid,
66 app_handler=AppHandler,
67 dict=dict_proc:start(dict:from_list([{join_on_connect, Client#irc_bot.channels}])),
68 connection_timeout=Ref,
69 state=connecting}}.
71 %%--------------------------------------------------------------------
72 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
73 %% {reply, Reply, State, Timeout}
74 %% {noreply, State} |
75 %% {noreply, State, Timeout} |
76 %% {stop, Reason, Reply, State} |
77 %% {stop, Reason, State}
78 %% Description: Handling call messages
79 %%--------------------------------------------------------------------
80 handle_call(get_nick, _From, #state{nick=Nick} = State) ->
81 {reply, {ok, Nick}, State};
82 handle_call(get_irclib, _From, #state{irclib=Irclib} = State) ->
83 {reply, {ok, Irclib}, State};
84 handle_call({say, Where, What}, _From, #state{irclib=Irclib} = State) ->
85 io:format("trace: irc_bot:handle_call:say()~n"),
86 irc_lib:say(Irclib, Where, What),
87 {reply, ok, State};
88 handle_call(get_cur_state, _From, #state{} = State) ->
89 % Generic method to get the current state.
90 io:format("trace: irc_bot:handle_call:get_cur_state~n"),
91 {reply, {ok, State}, State};
92 handle_call({stop, Message}, _From, #state{irclib=Irclib} = State) ->
93 irc_lib:quit(Irclib, Message),
94 irc_lib:stop(Irclib),
95 {stop, stop, State}.
97 %%--------------------------------------------------------------------
98 %% Function: handle_cast(Msg, State) -> {noreply, State} |
99 %% {noreply, State, Timeout} |
100 %% {stop, Reason, State}
101 %% Description: Handling cast messages
102 %%--------------------------------------------------------------------
103 handle_cast({irc_connect, Nick}, #state{state=connecting, dict=Dict,
104 irclib=Irclib} = State) ->
105 % Do connect stuff
106 io:format("trace: irc_bot:irc_connect~n"),
107 join_channels(Irclib, dict_proc:fetch(join_on_connect, Dict)),
108 {noreply, State#state{nick=Nick, state=idle}};
109 handle_cast({stop, _}, #state{state=connecting} = State) ->
110 {stop, stop, State};
111 handle_cast({irc_message, {_, "PONG", _}}, #state{state=pong, pong_timeout=Ref} = State) ->
112 {ok, cancel} = timer:cancel(Ref),
113 {noreply, State#state{state=idle, pong_timeout=undefined}};
114 handle_cast(irc_closed, #state{irclib=Irclib} = State) ->
115 io:format("trace: irc_bot:irc_closed~n"),
116 irc_lib:connect(Irclib),
117 {noreply, State#state{state=connecting}};
118 handle_cast({irc_message, {_, "PING", [Server]}}, #state{irclib=Irclib} = State) ->
119 irc_lib:pong(Irclib, Server),
120 {noreply, State};
121 handle_cast({irc_message, {_, "KICK", [Channel, Nick, _]}}, #state{irclib=Irclib, nick=Nick} = State) ->
122 irc_lib:join(Irclib, Channel),
123 {noreply, State};
124 handle_cast({irc_message, {_, "KICK", [Channel, Nick]}}, #state{irclib=Irclib, nick=Nick} = State) ->
125 irc_lib:join(Irclib, Channel),
126 {noreply, State};
127 handle_cast({irc_message, {_Who, "JOIN", [_Where]}}, State) ->
128 %% flood_policy:handle({Who, Where, self()}, join, self()),
129 {noreply, State};
130 handle_cast({irc_message, {From, "PRIVMSG", [To, Message]}},
131 #state{ irclib=Irclib, app_handler=AppHandler } = State) ->
132 %***********************************
133 % Let's dispatch the message:
134 % For message dispatching we always send the pid of the bot it originated from
135 % in the message. I havn't decided what other information will be helpful for this.
136 % From the bot pid, they can determine the name of the bot using bot_manager
137 %***********************************
138 io:format("INFO: incoming PRIVMSG: from:[~p] channel:[~p]~n",
139 [From, To]),
140 AppHandler ! {irc_message, self(), {From, "PRIVMSG", [To, Message]}},
141 msg_dispatch:dispatch("PRIVMSG", [From, To, Message], [self()]),
142 {noreply, State};
143 handle_cast({say, Where, What}, #state{irclib=Irclib} = State) ->
144 irc_lib:say(Irclib, Where, What),
145 {noreply, State};
146 % Catch all
147 handle_cast({irc_message, _}, State) ->
148 {noreply, State}.
150 %%--------------------------------------------------------------------
151 %% Function: handle_info(Info, State) -> {noreply, State} |
152 %% {noreply, State, Timeout} |
153 %% {stop, Reason, State}
154 %% Description: Handling all non call/cast messages
155 %%--------------------------------------------------------------------
156 handle_info(timedout_ping, #state{irclib=Irclib, state=pong} = State) ->
157 irc_lib:disconnect(Irclib),
158 irc_lib:connect(Irclib),
159 {noreply, State#state{state=connecting}};
160 handle_info(timed_ping, #state{irclib=Irclib} = State) ->
161 irc_lib:ping(Irclib),
162 {ok, Ref} = timer:send_after(pong_timeout(), timedout_ping),
163 {noreply, State#state{state=pong, pong_timeout=Ref}}.
166 %%--------------------------------------------------------------------
167 %% Function: terminate(Reason, State) -> void()
168 %% Description: This function is called by a gen_server when it is about to
169 %% terminate. It should be the opposite of Module:init/1 and do any necessary
170 %% cleaning up. When it returns, the gen_server terminates with Reason.
171 %% The return value is ignored.
172 %%--------------------------------------------------------------------
173 terminate(_Reason, #state{connection_timeout=Ref, dict=Dict}) ->
174 timer:cancel(Ref),
175 dict_proc:stop(Dict),
176 %bot_manager:erase(self()),
179 %%--------------------------------------------------------------------
180 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
181 %% Description: Convert process state when code is changed
182 %%--------------------------------------------------------------------
183 code_change(_OldVsn, State, _Extra) ->
184 {ok, State}.
186 %%--------------------------------------------------------------------
187 %%% Internal functions
188 %%--------------------------------------------------------------------
190 join_channels(Bot, [Channel | Rest]) ->
191 io:format("trace: irc_bot:join_channels()"),
192 irc_lib:join(Bot, Channel),
193 join_channels(Bot, Rest);
194 join_channels(_, []) ->
197 connection_timeout() ->
198 % 10 minutes
199 600000.
201 pong_timeout() ->
202 % 10 Seconds
203 10000.
205 % -------------------------------------------------------------
206 % Functions used to interact with the bot
207 say(Bot, Where, What) ->
208 io:format("trace: irc_bot:say<attempt cast>~n"),
209 gen_server:cast(Bot, {say, Where, What}).
211 stop(Bot, Message) ->
212 gen_server:call(Bot, {stop, Message}).
214 get_irclib(Name) ->
215 {ok, Pid} = bot_manager:fetch_pid(Name),
216 gen_server:call(Pid, get_irclib).
218 get_nick(Bot) when is_pid(Bot) ->
219 {ok, Nick} = gen_server:call(Bot, get_nick),
220 Nick.
222 get_cur_state(Bot) ->
223 {ok, State} = gen_server:call(Bot, get_cur_state),
224 io:format("trace: irc_bot:get_cur_state: [~p]~n", [State]),
225 {ok, State}.
227 % -------------------------------------------------------------
228 % Functions for manipulating the bot database
229 add_bot({Botname, Nick, Realname, Servers, Channels, Password}) ->
230 p1_db:insert_row(#irc_bot_db{botname=Botname, nick=Nick, realname=Realname, servers=Servers, channels=Channels, password=Password}).
232 %% End of the file