Related
I am using user send packet hook in my custom module, I am not understanding why getting badmatch error for each of the packet received. i have added declaration part here too.
**Code**
-module(mod_post_log).
-behaviour(gen_mod).
-export([start/2,stop/1,log_user_send/4,post_result/1,send_message/3]).
**strong text**
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("logger.hrl").
start(Host, _Opts) ->
ok = case inets:start() of
{error, {already_started, inets}} ->
ok;
ok ->
ok
end,
ejabberd_hooks:add(user_send_packet, Host,
?MODULE, log_user_send, 50),
ok.
stop(Host) ->
ejabberd_hooks:delete(user_send_packet, Host,
?MODULE, log_user_send, 50),
ok.
log_user_send(Packet, _C2SState, From, To) ->
ok = log_packet(From, To, Packet),
Packet.
log_packet(From, To, #xmlel{name = <<"message">>} = Packet) ->
MessageId = xml:get_tag_attr_s(<<"id">>, Packet),
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
send_message(From, To, MessageId, Type),
%% send_message(From, To, xml:get_tag_attr_s(list_to_binary("id"), Packet),xml:get_tag_attr_s(list_to_binary("type"), Packet)),
%% ?INFO_MSG("mod_ack_receipt - MsgID: ~p To: ~p From: ~p", [MessageId, To, From]),
ok = log_message(From, To, Packet);
log_packet(_From, _To, _Packet) ->
ok.
log_message(From, To, #xmlel{attrs = Attrs} = Packet) ->
Type = lists:keyfind(<<"type">>, 1, Attrs),
log_message_filter(Type, From, To, Packet).
log_message_filter({<<"type">>, Type}, From, To, Packet)
when Type =:= <<"chat">>;
Type =:= <<"groupchat">> ->
log_chat(From, To, Packet);
log_message_filter(_Other, _From, _To, _Packet) ->
ok.
log_chat(From, To, #xmlel{children = Els} = Packet) ->
case get_body(Els) of
no_body ->
ok;
{ok, _Body} ->
log_chat_with_body(From, To, Packet)
end.
log_chat_with_body(From, To, Packet) ->
case should_acknowledge(Packet) of
S when (S==true) ->
post_xml(From, To, Packet);
false ->
ok
end,
Packet.
should_acknowledge(#xmlel{name = <<"message">>} = Packet) ->
case {xml:get_attr_s(<<"type">>, Packet#xmlel.attrs),
xml:get_subtag_cdata(Packet, <<"body">>)} of
{<<"error">>, _} ->
false;
{_, <<>>} ->
%% Empty body
false;
_ ->
true
end;
should_acknowledge(#xmlel{}) ->
false.
post_xml(From, To, Packet) ->
Ts = to_iso_8601_date(os:timestamp()),
MessageId = xml:get_tag_attr_s(list_to_binary("id"), Packet),
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
Chat_sent = xml:get_tag_attr_s(list_to_binary("chat_sent"), Packet),
Subject = xml:get_path_s(Packet, [{elem, list_to_binary("subject")},cdata]),
Body = xml:get_path_s(Packet,[{elem, list_to_binary("body")}, cdata]),
Sep = "&",
Post = [
"from=", From#jid.luser, Sep,
"to=", To#jid.luser, Sep,
"body=", binary_to_list(Body), Sep,
"type=", binary_to_list(Type),Sep,
"chat_sent=", binary_to_list(Chat_sent),Sep,
"subject=",binary_to_list(Subject),Sep,
"message_id=", binary_to_list(MessageId),Sep,
"xml=",xml:element_to_binary(Packet),Sep,
"done=11"
],
Url = get_opt(url),
TsHeader = get_opt(ts_header, "X-Message-Timestamp"),
FromHeader = get_opt(from_header, "X-Message-From"),
ToHeader = get_opt(to_header, "X-Message-To"),
HttpOptions = get_opt(http_options, []),
ReqOptions = get_opt(req_options, []),
{ok, _ReqId} = httpc:request(post,
{Url, [], "application/x-www-form-urlencoded", list_to_binary(Post)},
HttpOptions,
[ {sync, false},
{receiver, {?MODULE, post_result, []}}
| ReqOptions ]),
?INFO_MSG("post http - MsgID: xml: ~p", [post]),
ok.
post_result({_ReqId, {error, Reason}}) ->
report_error([ {error, Reason } ]);
post_result({_ReqId, Result}) ->
{StatusLine, Headers, Body} = Result,
{_HttpVersion, StatusCode, ReasonPhrase} = StatusLine,
if StatusCode < 200;
StatusCode > 299 ->
ok = report_error([ {status_code, StatusCode},
{reason_phrase, ReasonPhrase},
{headers, Headers},
{body, Body} ]),
ok;
true ->
ok
end.
send_message(From, To, MessageId,Type) ->
Packet = #xmlel{name = <<"iq">>,
attrs = [{<<"to">>, From},{<<"ack">>, MessageId}, {<<"type">>, <<"result">>},{<<"chattype">>, Type}],
children =
[#xmlel{name = <<"ack">>,
attrs = [{<<"xmlns">>,<<"xmpp:ack">> }],
children = []}]},
ejabberd_router:route(From, From, Packet).
get_body(Els) ->
XmlEls = [ El || El <- Els, is_record(El, xmlel) ],
case lists:keyfind(<<"body">>, #xmlel.name, XmlEls) of
false ->
no_body;
#xmlel{children = InnerEls} ->
case lists:keyfind(xmlcdata, 1, InnerEls) of
false ->
no_body;
{xmlcdata, Body} ->
{ok, Body}
end
end.
get_opt(Opt) ->
get_opt(Opt, undefined).
get_opt(Opt, Default) ->
F = fun(Val) when is_binary(Val) -> binary_to_list(Val);
(Val) -> Val
end,
gen_mod:get_module_opt(global, ?MODULE, Opt, F, Default).
report_error(ReportArgs) ->
ok = error_logger:error_report([ mod_post_log_cannot_post | ReportArgs ]).
**Error Log**
{{badmatch,{xmlel,<<"message">>,
[{<<"xml:lang">>,<<"en">>},
{<<"to">>,<<"123456789#xyz.myapp.com">>},
{<<"id">>,<<"AF1xJ-149">>},
{<<"chat_sent">>,<<"2015-12-15T06:45:29.399Z">>},
{<<"type">>,<<"chat">>}],
[{xmlel,<<"size">>,[],[{xmlcdata,<<"0">>}]},
{xmlel,<<"subject">>,[],[{xmlcdata,<<"poke">>}]},
{xmlel,<<"body">>,[],[{xmlcdata,<<"95">>}]},
{xmlel,<<"request">>,
[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],
[]}]}},
[{mod_post_log,log_packet,3,
[{file,"src/mod_post_log.erl"},{line,44}]},
{mod_post_log,log_user_send,4,
[{file,"src/mod_post_log.erl"},{line,33}]},
{ejabberd_hooks,safe_apply,3,
[{file,"src/ejabberd_hooks.erl"},{line,382}]},
{ejabberd_hooks,run_fold1,4,
[{file,"src/ejabberd_hooks.erl"},{line,365}]},
{ejabberd_c2s,session_established2,2,
[{file,"src/ejabberd_c2s.erl"},{line,1296}]},
{p1_fsm,handle_msg,10,[{file,"src/p1_fsm.erl"},{line,582}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}
In file src/mod_post_log.erl at line 44 in function mod_post_log:log_packet/3 is some match operator which doesn't expect to get
{xmlel,<<"message">>,
[{<<"xml:lang">>,<<"en">>},
{<<"to">>,<<"123456789#xyz.myapp.com">>},
{<<"id">>,<<"AF1xJ-149">>},
{<<"chat_sent">>,<<"2015-12-15T06:45:29.399Z">>},
{<<"type">>,<<"chat">>}],
[{xmlel,<<"size">>,[],[{xmlcdata,<<"0">>}]},
{xmlel,<<"subject">>,[],[{xmlcdata,<<"poke">>}]},
{xmlel,<<"body">>,[],[{xmlcdata,<<"95">>}]},
{xmlel,<<"request">>,
[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],
[]}]}
it would be much better to see full module "mod_post_log.erl" as is.
But i think that problem is here :
MessageId = xml:get_tag_attr_s(<<"id">>, Packet),
Packet is a record, and function wait for iolist, so it should be something like
MessageId = xml:get_tag_attr_s(<<"id">>, Packet#xmlel.{{see record declaration}}),
Here is the json data
{
"title": "The Title",
"content": "The Content"
}
curl -vX POST http://localhost:10003/sample -H "Content-Type:application/json" \ -d '{ "title": "The Title", "content": "The Content" }'
-export([content_types_accepted/2]).
allowed_methods(Req, State) ->
{[<<"GET">>, <<"POST">>], Req, State}.
content_types_accepted(Req, State) ->
{[{{<<"application">>, <<"json">>, []}, welcome}], Req, State}.
welcome(Req, State) ->
Req_method = cowboy_req:method(Req),
io:format("Req_method is ~p ~n", [Req_method]),
Req_Body = cowboy_req:body(Req),
io:format("Body is ~p ~n", [Req_Body]),
Body = <<"<h1>This is a response for other methods</h1>">>,
io:format("Body is ~p ~n",[Body]),
{Body, Req, State}.
I see Method and Body but trying to catch json data unable to do such.
I was looking for http-Post-method, passing Json data using content_type_accepted method. Here is the code which worked for me :)
-export([init/3]).
-export([welcome/2, terminate/3, allowed_methods/2]).
-export([content_types_accepted/2]).
init(_Transport, _Req, []) ->
{upgrade, protocol, cowboy_rest}.
allowed_methods(Req, State) ->
{[<<"POST">>], Req, State}.
content_types_accepted(Req, State) ->
{[{<<"application/json">>, welcome}], Req, State}.
terminate(_Reason, _Req, _State) ->
ok.
welcome(Req, State) ->
{ok, ReqBody, Req2} = cowboy_req:body(Req),
Req_Body_decoded = jsx:decode(ReqBody),
[{<<"title">>,Title},{<<"content">>,Content}] = Req_Body_decoded,
Title1 = binary_to_list(Title),
Content1 = binary_to_list(Content),
io:format("Title1 is ~p ~n ", [Title1]),
io:format("Content1 is ~p ~n", [Content1]),
io:format("Title is ~p ~n", [Title]),
io:format("Content is ~p ~n", [Content]),
lager:log(info, [], "Request Body", [Req_Body_decoded]),
Res1 = cowboy_req:set_resp_body(ReqBody, Req2),
Res2 = cowboy_req:delete_resp_header(<<"content-type">>, Res1),
Res3 = cowboy_req:set_resp_header(<<"content-type">>, <<"application/json">>, Res2),
{true, Res3, State}.
Making an HTTP POST in Erlang itself is very simple. Taken from this other StackOverflow answer, here is a simple Example:
ssl:start(),
application:start(inets),
PostBody = "{ \"title\": \"The Title\", \"content\": \"The Content\" }",
Url = "http://some.url/endpoint",
httpc:request(post,
{Url, [],
"application/x-www-form-urlencoded",
PostBody
}, [], []).
Hope this helps!
These resources also seem like they would be helpful:
http://erlang.org/doc/man/httpc.html
http://no-fucking-idea.com/blog/2013/01/22/making-request-to-rest-resources-in-erlang/
To catch the json values you can use jiffy https://github.com/davisp/jiffy
Example usage :
{JsonDecode} = jiffy:decode(ReqBody),
error_logger:info_msg("JSON Decoded by jiffy ", JsonDecode),
UserId = proplists:get_value(<<"userid">>, JsonDecode),
Amount = proplists:get_value(<<"amount">>, JsonDecode),
Rate = proplists:get_value(<<"rate">>, JsonDecode),
Duration = proplists:get_value(<<"duration">>, JsonDecode),
I'm trying to implement mod_blocking that will work only with mod_privacy in odbc module.
I keep getting the response:
<error code='500' type='wait'><internal-server-error0 xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
Here is my code:
process_blocklist_block(LUser, LServer, JIDs) ->
Filter = fun (List) ->
AlreadyBlocked = list_to_blocklist_jids(List, []),
lists:foldr(fun (JID, List1) ->
case lists:member(JID, AlreadyBlocked)
of
true -> List1;
false ->
[#listitem{type = jid,
value = JID,
action = deny,
order = 0,
match_all = true}
| List1]
end
end,
List, JIDs)
end,
case process_blocklist_block(LUser, LServer, Filter,odbc)
of
{atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default,
UserList),
broadcast_blocklist_event(LUser, LServer,
{block, JIDs}),
{result, [], UserList};
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
end.
process_blocklist_block(LUser, LServer, Filter, odbc) ->
F = fun () ->
Default = case mod_privacy_odbc:sql_get_default_privacy_list_t(LUser)
of
{selected, [<<"name">>], []} ->
Name = <<"Blocked contacts">>,
mod_privacy_odbc:sql_add_privacy_list(LUser, Name),
mod_privacy_odbc:sql_set_default_privacy_list(LUser, Name),
Name;
{selected, [<<"name">>], [[Name]]} -> Name
end,
{selected, [<<"id">>], [[ID]]} = mod_privacy_odbc:sql_get_privacy_list_id_t(LUser, Default),
case mod_privacy_odbc:sql_get_privacy_list_data_by_id_t(ID)
of
{selected,
[<<"t">>, <<"value">>, <<"action">>, <<"ord">>,
<<"match_all">>, <<"match_iq">>, <<"match_message">>,
<<"match_presence_in">>, <<"match_presence_out">>],
RItems = [_ | _]} ->
List = lists:map(fun mod_privacy_odbc:raw_to_item/1, RItems);
_ -> List = []
end,
NewList = Filter(List),
NewRItems = lists:map(fun mod_privacy_odbc:item_to_raw/1, NewList),
mod_privacy_odbc:sql_set_privacy_list(ID, NewRItems),
{ok, Default, NewList}
end,
ejabberd_odbc:sql_transaction(LServer, F).
I've checked all the queries. They work fine. I struggling with understanding the logic of this piece of code. What is the best way to debug this code, so i can understand when my logic is failing? Could anyone point me in the right direction?
UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default,
UserList),
broadcast_blocklist_event(LUser, LServer,
{block, JIDs}),
{result, [], UserList};
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
if you expect
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
is the point where server returns 'internal server error', you can just change this place to
_Otherwise ->
?WARNING_MSG("Got some unexpected result in mod_priv ~100000p",[_Otherwise]),
{error, ?ERR_INTERNAL_SERVER_ERROR)
and then look at log/ejabberd.log
Of course you need to start mongooseim with loglevel at least 3 in the ejabberd.cfg
I'm trying to build a REST API using the Yaws web server but I'm unable to get Yaws to dispatch requests to my module.
-module(rest).
-include_lib("stdlib/include/qlc.hrl").
-include_lib("yaws/include/yaws_api.hrl").
-export([out/1, addAirport/4, handle/2]).
-compile(export_all).
-define(RECORD_TYPE, airport).
-define(RECORD_KEY_FIELD, code).
-record(airport,
{code, city, country, name }).
start() ->
application:start(mnesia).
do_this_once() ->
mnesia:create_table(airport, [{attributes,record_info(fields,airport)},{index, [country]}]),
mnesia:stop().
out(Arg) ->
Method = method(Arg) ,
io:format("~p:~p ~p Request ~n", [?MODULE, ?LINE, Method]),
handle(Method, Arg).
method(Arg) ->
Rec = Arg#arg.req,
Rec#http_request.method.
convert_to_json(Lines) ->
Data = [{obj, [{airport, Line#?RECORD_TYPE.code},
{city, Line#?RECORD_TYPE.city},
{country, Line#?RECORD_TYPE.country},
{name, Line#?RECORD_TYPE.name}]} || Line <- Lines],
JsonData = {obj, [{data, Data}]},
rfc4627:encode(JsonData).
addAirport(Code, City, Country, Name) ->
NewRec = #?RECORD_TYPE{?RECORD_KEY_FIELD = Code,
city = City,
country = Country,
name = Name},
io:format("~p:~p Adding Airport ~p~n",
[?MODULE,?LINE, NewRec]),
Add = fun() ->
mnesia:write(NewRec)
end,
{atomic, _Rec} = mnesia:transaction(Add),
NewRec.
handle('GET', _Arg) ->
io:format("~n ~p:~p GET Request ~n", [?MODULE, ?LINE]),
Records = do(qlc:q([X || X <- mnesia:table(?RECORD_TYPE)])),
Json = convert_to_json( Records),
io:format("~n ~p:~p GET Request Response ~p ~n", [?MODULE, ?LINE,
Json]),
{html, Json};
handle('POST', Arg) ->
{ok, Json, _} = rfc4627:decode(Arg#arg.clidata),
io:format("~n~p:~p POST request ~p~n",
[?MODULE, ?LINE, Json]),
Airport = rfc4627:get_field(Json, "airport", <<>>),
City = rfc4627:get_field(Json, "city", <<>>),
Country = rfc4627:get_field(Json, "country", <<>>),
Name= rfc4627:get_field(Json, "name", <<>>),
_Status = addAirport(Airport, City, Country, Name),
[{status, 201},
{html, Arg#arg.clidata}];
handle('PUT', Arg) ->
[IndexValue,_] = string:tokens(Arg#arg.pathinfo),
{ok, Json, _} = rfc4627:decode(Arg#arg.clidata),
io:format("~p:~p PUT request ~p ~p~n",
[?MODULE, ?LINE, IndexValue, Json]),
Airport = rfc4627:get_field(Json, "airport", <<>>),
City = rfc4627:get_field(Json, "city", <<>>),
Country = rfc4627:get_field(Json, "country", <<>>),
Name= rfc4627:get_field(Json, "name", <<>>),
NewRec = #?RECORD_TYPE{
?RECORD_KEY_FIELD = Airport,
city = City,
country = Country,
name = Name},
io:format("~p:~p Renaming ~p",
[?MODULE, ?LINE, NewRec]),
ChangeName = fun() ->
mnesia:delete(
{?RECORD_KEY_FIELD, IndexValue}),
mnesia:write(NewRec)
end,
{atomic, _Rec} = mnesia:transaction(ChangeName),
[{status, 200},
{html, IndexValue}];
handle('DELETE', Arg) ->
[IndexValue, _ ] = string:tokens(Arg#arg.pathinfo),
io:format("~p:~p DELETE request ~p",
[?MODULE, ?LINE, IndexValue]),
Delete = fun() ->
mnesia:delete(
{?RECORD_KEY_FIELD, IndexValue})
end,
Resp = mnesia:transaction(Delete),
case Resp of
{atomic, ok} ->
[{status, 204}];
{_, Error} ->
io:format("~p:~p Error ~p ",
[?MODULE, ?LINE, Error]),
[{status, 400},
{html, Error}]
end;
handle(Method,_) ->
[{error, "Unknown method " ++ Method},
{status, 405},
{header, "Allow: GET, HEAD, POST, PUT, DELETE"}
].
do(Q)->
F = fun() ->
qlc:e(Q)
end,
{atomic, Value} = mnesia:transaction(F),
Value.
You need to register your module as a Yaws appmod in the server configuration portion of your yaws.conf file. Please see the Yaws appmod documentation for the necessary details.
I am a beginner with Erlang/Nitrogen.
I am toying with a bidding system back by a mnesia db.
On my index page I have the following code and the various items and their properties get created dynamically from the database:
%% -*- mode: nitrogen -*-
-module (index).
-compile(export_all).
-include_lib("nitrogen/include/wf.hrl").
main() -> #template { file="./site/templates/bare.html" }.
title() -> "Meir Panim Gala Dinner silent auction".
body() ->
Header = [#panel{id=header, body=[#h1{text="Meir Panim Gala Dinner silent auction"}]}],
{atomic, Items} = item_database:get_all(),
Elements = lists:map(fun(X) ->
{item, Index, Title, _, Picture, _, _, Reserve, CurrentBid} = X,
#panel{id=items, body=[
#span{id=title, text=Title},
#image{id=image, image= "images/" ++ Picture},
#span{id=currentbid, text="Current bid: £" ++ integer_to_list(CurrentBid)},
#span{id=reserve, text="Reserve: £" ++ wf:to_list(Reserve)},
#link{id=showalert, text="More info / Place your bid", postback="showalert"++integer_to_list(Index)}
]
}
end, Items),
wf:f([Header, Elements]).
{atomic, Items} = item_database:get_all(),
Actions = lists:map(fun(X) ->
{item, Index, _, _, _, _, _, _, _} = X,
event("showalert"++integer_to_list(Index)) ->
wf:wire(#alert{text="action "++integer_to_list(Index)++" clicked"})
end, Items).
I tried to create my events in the same manner but it was not working.
In my code the alerts will be replaced with lightboxes containing a form to accept bids.
Please help and tell me what I am doing wrong.
As far as I know you catch events in page with "event".
so I would try something like :
postback={bid, Index}
and at down catch it with :
event({bid, Index})->
%% do stuff
ok;
event(_)->
ok.
update:
this is only an example of how you can fix it, its not the best way.
%% -*- mode: nitrogen -*-
-module (index).
-compile(export_all).
-include_lib("nitrogen/include/wf.hrl").
main() -> #template { file="./site/templates/bare.html" }.
title() -> "Meir Panim Gala Dinner silent auction".
body() ->
Header = [#panel{id=header, body=[#h1{text="Meir Panim Gala Dinner silent auction"}]}],
{atomic, Items} = item_database:get_all(),
Elements = lists:map(fun(X) ->
{item, Index, Title, _, Picture, _, _, Reserve, CurrentBid} = X,
#panel{id=items, body=[
#span{id=title, text=Title},
#image{id=image, image= "images/" ++ Picture},
#span{id=currentbid, text="Current bid: £" ++ integer_to_list(CurrentBid)},
#span{id=reserve, text="Reserve: £" ++ wf:to_list(Reserve)},
#link{id=showalert, text="More info / Place your bid", postback={bid,Index}}
]
}
end, Items),
wf:f([Header, Elements]).
event({bid, Idx})->
%% you would better have a function to get one item at a time in item_database
case item_database:get_by_index(Idx) of
{atomic, X} ->
%% This is not the right way, use records
{item, Index, Title, _, Picture, _, _, Reserve, CurrentBid} = X,
wf:wire(#alert{text="action "++ Title ++" clicked"});
_ ->
wf:wire(#alert{text="item not found"})
end;
event(_)->
ok.
%% -*- mode: nitrogen -*-
-module (index).
-compile(export_all).
-include_lib("nitrogen/include/wf.hrl").
main() -> #template { file="./site/templates/bare.html" }.
title() -> "Welcome to Nitrogen".
body() ->
{atomic, Records} = item_database:get_all(),
Elements = lists:map(fun(X) ->
{item, Index, Title, _, _, _, _, _, _} = X,
#panel{body=[
#span{text=Title, style="font-size: 20px; font-weight: bold;"},
#br{},
#link{text="more info / place your bid", postback="showinfo"++integer_to_list(Index)},
#br{},
#link{text="register your bid", postback="registerbid"++integer_to_list(Index)},
#br{},
#br{},
#lightbox{id="lightbox"++integer_to_list(Index), style="display: none;", body=[
#span{text=Title, style="font-size: 24px; font-weight: bold; color: #ffffff;"}
]}
]}
end, Records),
wf:f([Elements]).
event(Event) ->
case (re:run(Event, "showinfo")) of
{match, _} ->
[_, _, SI] = re:split(Event, "(showinfo)"),
ShowIndex = binary:bin_to_list(SI),
wf:wire(#show{target="lightbox"++ShowIndex});
_ -> ok
end,
case (re:run(Event, "registerbid")) of
{match, _} ->
[_, _, RI] = re:split(Event, "(registerbid)"),
RegisterIndex = binary:bin_to_list(RI),
wf:wire(#alert{text="registerbid"++RegisterIndex++" clicked"});
_ -> ok
end.