1 %%%-------------------------------------------------------------------
2 %%% File : irc_bot2.erl
3 %%% Author : ortitz <orbitz@blong.orbitz>
4 %%% Description : Irc bot gen_server
6 %%% Created : 23 Mar 2006 by ortitz <orbitz@blong.orbitz>
7 %%%-------------------------------------------------------------------
13 -behaviour(gen_server
).
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]).
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 %%====================================================================
31 %%====================================================================
32 %%--------------------------------------------------------------------
33 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
34 %% Description: Starts the server
35 %%--------------------------------------------------------------------
37 gen_server:start_link(?MODULE
, [Client
], []).
39 %%====================================================================
40 %% gen_server callbacks
41 %%====================================================================
43 %%--------------------------------------------------------------------
44 %% Function: init(Args) -> {ok, State} |
45 %% {ok, State, Timeout} |
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 %%--------------------------------------------------------------------
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
,
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
,
71 %%--------------------------------------------------------------------
72 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
73 %% {reply, Reply, State, Timeout}
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
),
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
),
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
) ->
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
) ->
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
),
121 handle_cast({irc_message
, {_
, "KICK", [Channel
, Nick
, _
]}}, #state
{irclib
=Irclib
, nick
=Nick
} = State
) ->
122 irc_lib:join(Irclib
, Channel
),
124 handle_cast({irc_message
, {_
, "KICK", [Channel
, Nick
]}}, #state
{irclib
=Irclib
, nick
=Nick
} = State
) ->
125 irc_lib:join(Irclib
, Channel
),
127 handle_cast({irc_message
, {_Who
, "JOIN", [_Where
]}}, State
) ->
128 %% flood_policy:handle({Who, Where, self()}, join, self()),
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",
140 AppHandler
! {irc_message
, self(), {From
, "PRIVMSG", [To
, Message
]}},
141 msg_dispatch:dispatch("PRIVMSG", [From
, To
, Message
], [self()]),
143 handle_cast({say
, Where
, What
}, #state
{irclib
=Irclib
} = State
) ->
144 irc_lib:say(Irclib
, Where
, What
),
147 handle_cast({irc_message
, _
}, 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
}) ->
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
) ->
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() ->
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
}).
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
),
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
]),
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
}).