Skip to content

Instantly share code, notes, and snippets.

@MaskRay
Created June 23, 2012 14:17
Show Gist options
  • Save MaskRay/2978437 to your computer and use it in GitHub Desktop.
Save MaskRay/2978437 to your computer and use it in GitHub Desktop.
erl-chat
{port,8888}.
-module(core).
-behavior(gen_server).
-export([start_link/0, stop/0, nick/1, join/1, msg/2, part/1, error_msg/0, init/1, terminate/2, handle_cast/2, handle_call/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, null, []).
stop() ->
gen_server:cast(?MODULE, stop).
%% Services API
nick(Nick) -> gen_server:call(?MODULE, {nick, Nick}).
msg(Channel, Msg) -> gen_server:call(?MODULE, {msg, Channel, Msg}).
join(Channel) -> gen_server:call(?MODULE, {join, Channel}).
part(Channel) -> gen_server:call(?MODULE, {part, Channel}).
error_msg() -> "unrecognized command".
%% Callback
init(null) ->
ets:new(nicks_channels, [named_table]),
ets:new(nicks, [named_table]),
{ok, null}.
terminate(_Reason, _) ->
ok.
handle_cast(stop, _) ->
{stop, normal, null}.
handle_call({nick, Nick}, {Pid,_Tag}, _) ->
error_logger:info_msg("-- nick ~p~n", [length(Nick)]),
case ets:match_object(nicks, {'_', Nick}) of
[] ->
case ets:match_object(nicks, {Pid, '_'}) of
[{Pid, OldNick}] ->
ets:delete(nicks, {Pid, OldNick});
_ -> ok
end,
ets:insert(nicks, {Pid, Nick}),
{reply, ok, null};
[{Pid, Nick}] ->
{reply, ok, null};
[_] ->
{reply, {error, "nick occupied"}, null}
end;
handle_call({msg, Channel, Msg}, {Pid,_Tag}, _) ->
case ets:match_object(nicks, {Pid, '_'}) of
[] ->
{reply, {error, "not login"}, null};
[{Pid, Nick}] ->
error_logger:info_msg("-- ~p~n", [ets:match(nicks_channels, {'$0', Channel})]),
lists:foreach(fun([Nick2]) ->
case ets:match_object(nicks, {'_', Nick2}) of
[{Pid2, Nick2}] ->
Pid2 ! {msg, Nick, Channel, Msg}
end
end, ets:match(nicks_channels, {'$0', Channel})),
{reply, ok, null}
end;
handle_call({join, Channel}, {Pid,_Tag}, _) ->
case ets:match_object(nicks, {Pid, '_'}) of
[] ->
{reply, {error, "not login"}, null};
[{Pid, Nick}] ->
error_logger:info_msg("-- ~s joins ~s~n", [Nick,Channel]),
ets:insert(nicks_channels, {Nick, Channel}),
{reply, ok, null}
end;
handle_call({part, Channel}, {Pid,_Tag}, _) ->
case ets:match_object(nicks, {Pid, '_'}) of
[] ->
{reply, {error, "not login"}, null};
[{Pid, Nick}] ->
ets:delete(nicks_channels, {Nick, Channel}),
{reply, ok, null}
end.
-module(gui).
-export([]).
start() ->
wx:new(),
Frame = wxFrame:new(wx:null(), ?wxID_ANY, "MicroBlog"),
-module(server).
-export([start/1]).
start(ListenPort) ->
case gen_tcp:listen(ListenPort, [binary, {active, false}, {reuseaddr, true}]) of
{ok, ListenSocket} ->
accept(ListenSocket);
{error, Reason} ->
{error, Reason}
end.
accept(ListenSocket) ->
{ok,Socket} = gen_tcp:accept(ListenSocket),
Pid = spawn(fun() ->
error_logger:info_msg("connection accepted~n", []),
loop(Socket) end),
gen_tcp:controlling_process(Socket, Pid),
inet:setopts(Socket, [{active, true}]),
accept(ListenSocket).
loop(Socket) ->
receive
{msg, Nick, Channel, Msg} ->
gen_tcp:send(Socket, ["/msg ", Nick, " ", Channel, " ", Msg]),
loop(Socket);
{tcp, Socket, Data} ->
error_logger:info_msg("receive ~s~n", [Data]),
case Data of
<<"/nick ", Data2/binary>> ->
Nick = trim(binary_to_list(Data2)),
error_logger:info_msg("~w changes nick to ~s~n", [Socket, Nick]),
gen_tcp:send(Socket, term_to_msg(core:nick(Nick))),
loop(Socket);
<<"/msg ", Data2/binary>> ->
{Channel, Msg} = lists:splitwith(fun($ )->false;(_)->true end, binary_to_list(Data2)),
error_logger:info_msg("~w msg ~s:~s~n", [Socket, Channel, Msg]),
gen_tcp:send(Socket, term_to_msg(core:msg(Channel, if length(Msg) > 1 -> tl(Msg); true -> Msg end))),
loop(Socket);
<<"/join ", Data2/binary>> ->
Channel = trim(binary_to_list(Data2)),
error_logger:info_msg("~w joins ~s~n", [Socket, Channel]),
gen_tcp:send(Socket, term_to_msg(core:join(Channel))),
loop(Socket);
<<"/part ", Data2/binary>> ->
Channel = trim(binary_to_list(Data2)),
error_logger:info_msg("~w leaves ~s~n", [Socket, Channel]),
gen_tcp:send(Socket, term_to_msg(core:part(Channel))),
loop(Socket);
_ ->
gen_tcp:send(Socket, core:error_msg()),
loop(Socket)
end;
{tcp_closed, Socket} ->
error_logger:info_msg("~w closes~n", [Socket])
end.
term_to_msg(ok) ->
"ok";
term_to_msg({error, Msg}) ->
"error: " ++ Msg.
trim(S) ->
X = re:replace(S, "^[ \r\n]*", "", [{return,list}]),
re:replace(X, "[ \r\n]*$", "", [{return,list}]).
-module(tcp).
start(Num,ListenPort) ->
case gen_tcp:listen(ListenPort, [{active, false}]) of
{ok, ListenSocket} ->
accept(ListenPort);
{error, Reason} ->
{error, Reason}
end.
accept(ListenSocket) ->
{ok,Socket} = gen_tcp:accept(ListenSocket),
Pid = spawn(fun() ->
error_logger:info_msg("connection accepted~n", []),
loop(Socket)),
gen_tcp:controlling_process(Socket, Pid),
accept(ListenSocket).
loop(Socket) ->
inet:setopts(Sock, [{active, true}]),
receive
{tcp, Socket, Data} ->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment