Ejabberd - Route message to users in offline_message_hook - erlang

I want to build a customer support chat app. There are users and an admin. Below admin there are multiple sub-admins. Initially the chat is initiated with admin only, but if the admin is offline I need to route the message to sub-admins.
offline_message_hook hook serves the purpose. I'll check if the To is admin, then I need to route the Packet to one of the sub-admins. How do I route/send the packet to other user within offline_message_hook. In short how do I change the To from the packet so that the packet is re-directed to the new sub-admin?
Here is what I've tried:-
offline_message_hook({_Action, #message{from = Peer, to = To} = Pkt} = Acc) ->
?INFO_MSG("Inside offline", []),
ejabberd_router:route(From, To, Packet),
ok.
I'm using ejabberd 17.04.105.
Update
After following user2610053's advice, I did this:-
-spec offline_message_hook({any(), message()}) -> {any(), message()}.
offline_message_hook({_Action, Msg} = Acc) ->
ejabberd_router:route(xmpp:set_to(Msg, 'praful#localhost')),
{routed, Msg}.
Following is the error:-
15:13:12.291 [error] failed to route packet:
#message{id = <<"purple187f6502">>,type = chat,lang = <<"en">>,
from = {jid,<<"praful2">>,<<"localhost">>,<<"Prafuls-MacBook-Pro">>,
<<"praful2">>,<<"localhost">>,<<"Prafuls-MacBook-Pro">>},
to = praful#localhost,subject = [],
body = [#text{lang = <<>>,data = <<"co=umon">>}],
thread = undefined,
sub_els = [{xmlel,<<"active">>,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/chatstates">>}],
[]}],
meta = #{ip => {0,0,0,0,0,0,0,1}}}
Reason = {error,{{badrecord,jid},[{ejabberd_router,do_route,1,[{file,"src/ejabberd_router.erl"},{line,343}]},{ejabberd_router,route,1,[{file,"src/ejabberd_router.erl"},{line,87}]},{mod_sunshine,offline_message_hook,1,[{file,"src/mod_sunshine.erl"},{line,24}]},{ejabberd_hooks,safe_apply,4,[{file,"src/ejabberd_hooks.erl"},{line,380}]},{ejabberd_hooks,run_fold1,4,[{file,"src/ejabberd_hooks.erl"},{line,364}]},{ejabberd_sm,route,1,[{file,"src/ejabberd_sm.erl"},{line,138}]},{ejabberd_local,route,1,[{file,"src/ejabberd_local.erl"},{line,116}]},{ejabberd_router,do_route,1,[{file,"src/ejabberd_router.erl"},{line,348}]}]}}
The user praful#localhost exist. Please advice what exactly is wrong?
Update2 - `UserReceivePacket Hook
In user_receive_packet packet hook, upon using the same function ejabberd_router:route(xmpp:set_to(Packet, jid:decode("praful#localhost"))), it throws an error saying :-
Hook user_receive_packet crashed when running mod_sunshine:user_receive_packet/1:
** Reason = {error,function_clause,[{jid,decode,[{file,"src/jid.erl"},{line,132}],["praful#localhost"]},{mod_sunshine,user_receive_packet,[{file,"src/mod_sunshine.erl"},{line,29}],1},{ejabberd_hooks,safe_apply,[{file,"src/ejabberd_hooks.erl"},{line,380}],4},{ejabberd_hooks,run_fold1,[{file,"src/ejabberd_hooks.erl"},{line,364}],4},{ejabberd_c2s,process_info,[{file,"src/ejabberd_c2s.erl"},{line,231}],2},{ejabberd_hooks,safe_apply,[{file,"src/ejabberd_hooks.erl"},{line,380}],4},{ejabberd_hooks,run_fold1,[{file,"src/ejabberd_hooks.erl"},{line,364}],4},{xmpp_stream_in,handle_info,[{file,"src/xmpp_stream_in.erl"},{line,373}],2}]}
So, I read about function_clause, but couldnt understand the same. What exactly is wrong over here?

I think you're asking about xmpp:set_to/2. Here is an example:
offline_message_hook({_Action, Msg} = Acc) ->
SubAdmins = get_sub_admins(Msg#message.to),
lists:foreach(
fun(Admin) ->
ejabberd_router:route(xmpp:set_to(Msg, Admin))
end, Admins),
{routed, Msg}.

Related

Cant load real-time data from tws

I can't get real-time tick with API for GBP.CHF#IDEALPRO/ My log:
0:53:36:282 <- 9-8-1073741829-0-GBP-CASH--0-----CHF---0---
10:53:36:282 <- 1-11-1073741830-0-GBP-CASH--0-----CHF---0-233,236,258-0-0--
10:53:36:282 -> ---R4-2-1073741830-321-Error validating request.-'bX' : cause - Please enter exchange-
10:53:36:283 <- 1-11--1073741830-0-GBP-CASH--0-----CHF---0--1-0--
10:53:36:283 -> ---S4-2--1073741830-321-Error validating request.-'bX' : cause - Please enter exchange-
10:53:36:318 <- 9-8-1073741831-0-GBP-CASH--0-----CHF---0---
Nothing incoming from tws. Please help.
Here's your log compared to my log.
1-11-1073741830-0-GBP-CASH--0 --- --CHF---0-233,236,258-0-0--
1-11-1 -0-GBP-CASH--0.0---IDEALPRO--CHF---0-233-0-0--
You just forgot the exchange.
contract = Contract()
contract.symbol = "GBP"
contract.secType = "CASH"
contract.exchange = "IDEALPRO"
contract.currency = "CHF"
app.reqMktData(1, contract, "233", False, False, None)
I'm not sure about your generic tick list, not all type are available for forex. I just threw in 233 to see if it would cause an error but it worked.

Elixir marshal ISO 8583 message error with Erlang library

I am new to elixir and I need to create ISO 8583 client with this language and Phoenix framework. I found an Erlang library for it from stackoverflow thread here, compiled successfully and followed the example in the repository here but got error when marshaling the message. Here is my Elixir code to marshal the message:
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti("0800", msg1)
msg3 = :erl8583_message.set(3, "300000", msg2)
msg4 = :erl8583_message.set(24, "045", msg3)
msg5 = :erl8583_message.set(41, "11111111", msg4)
msg6 = :erl8583_message.set(42, "222222222222222", msg5)
msg7 = :erl8583_message.set(63, "This is a Test Message", msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
That's just an elixir version from the example on the repo. This is the error I've got when running the app:
[error] #PID<0.438.0> running TestlangIsoClientWeb.Endpoint (cowboy_protocol) terminated
Server: 127.0.0.1:4001 (http)
Request: POST /api/process
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in :erl8583_marshaller_ascii.marshal_data_element/2
(erl8583) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/erl8583/src/erl8583_marshaller_ascii.erl:168: :erl8583_marshaller_ascii.marshal_data_element({:n, :fixed, 4}, "0800")
(erl8583) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/erl8583/src/erl8583_marshaller.erl:108: :erl8583_marshaller.marshal/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:61: TestlangIsoClientWeb.MyController.process/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:1: TestlangIsoClientWeb.MyController.action/2
(testlang_iso_client) lib/testlang_iso_client_web/controllers/my_controller.ex:1: TestlangIsoClientWeb.MyController.phoenix_controller_pipeline/2
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.instrument/4
(phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.plug_builder_call/2
(testlang_iso_client) lib/testlang_iso_client_web/endpoint.ex:1: TestlangIsoClientWeb.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /home/muhammad/Workspace/testlang/testlang_iso_client/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
Is there something I missed to make it work? Any help would be very appreciated.
Updated
I have tried to changed the string parameter to charlist, but still got the same error. Here is the code snippet:
def test(conn, _params) do
IO.puts("Test")
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti('0800', msg1)
msg3 = :erl8583_message.set(3, '300000', msg2)
msg4 = :erl8583_message.set(24, '045', msg3)
msg5 = :erl8583_message.set(41, '11111111', msg4)
msg6 = :erl8583_message.set(42, '222222222222222', msg5)
msg7 = :erl8583_message.set(63, 'This is a Test Message', msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
json(conn, %{status: "ok"})
end
Here is the function erl8583_marshaller.erl:108 mentioned in the stacktrace:
marshal(Message, MarshalHandlers) ->
OptionsRecord = parse_options(MarshalHandlers, #marshal_options{}),
{Marshalled1, Message1} = init_marshalling(OptionsRecord, Message),
MarshalledMti = encode_mti(OptionsRecord, Message1), % --- Line 108
Marshalled2 = <<Marshalled1/binary, MarshalledMti/binary>>,
{MarshalledBitmap, Message2} = encode_bitmap(OptionsRecord, Message1),
Marshalled3 = <<Marshalled2/binary, MarshalledBitmap/binary>>,
MarshalledFields = encode_fields(OptionsRecord, Message2),
Marshalled4 = <<Marshalled3/binary, MarshalledFields/binary>>,
end_marshalling(OptionsRecord, Message2, Marshalled4).
And here is the function erl8583_marshaller_ascii.erl:168 mentioned in the stacktrace:
%%
%% Local Functions
%%
marshal_data_element({n, llvar, Length}, FieldValue) when length(FieldValue) =< Length ->
erl8583_convert:integer_to_string(length(FieldValue), 2) ++ FieldValue;
I don't understand why the call to that function was failed to match with parameters {:n, :fixed, 4}, "0800" that was sent from my function. I have tried to change the double quotes to single quotes with no success. Is there any other suggestions what am I supposed to do?
there may be a bug in the code.
you can reference this issue here
there is one elixir library out which is relatively new.
its called ale8583.
documentation is still coming out on this one but looks very promising.
you can check it out.
Try passing charlists instead of strings:
msg1 = :erl8583_message.new()
msg2 = :erl8583_message.set_mti('0800', msg1)
msg3 = :erl8583_message.set(3, '300000', msg2)
msg4 = :erl8583_message.set(24, '045', msg3)
msg5 = :erl8583_message.set(41, '11111111', msg4)
msg6 = :erl8583_message.set(42, '222222222222222', msg5)
msg7 = :erl8583_message.set(63, 'This is a Test Message', msg6)
marshalled = :erl8583_marshaller_ascii.marshal(msg7)
There is potential for confusion here:
What Erlang calls "strings" and puts in double quotes ("foo"), Elixir calls "charlists" and puts in single quotes ('foo').
What Elixir calls "strings" and puts in double quotes ("foo"), Erlang calls "binaries" and puts in double quotes plus angle brackets (<<"foo">>).
It seems like the erl8583 library expects Erlang strings throughout.

How can I get a StateData of a room conference ejabberd

I want to get the current state data record from a particular room.
I would like to have something like...
get_state_data(RoomName)->
StateData = get_record(Room),
SateData.
>get_state_data("someroom").
{state,"8799879","conference.chat.dev.com",
"conference.chat.dev.com",mod_muc_odbc,
{muc,muc_create,muc_admin,
muc_create},
{jid,"8799879",
"conference.chat.dev.com",[],
"8799879",
"conference.chat.dev.com",[]},
{config,"567567","asdfa","asdf",
"sdfa","sdfa","sdf",true,true,
true,anyone,true,true,true,true,
true,true,false,true,false,false,
false,[],true,true,1800,200,
false,
{0,nil}},
{dict,3,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],
[],[],[],[],[],[]},
{{[],[],[],[],[],[],[],[],......
I was able to find the record by using this methods
[{,{,_},Pid}] = mnesia:dirty_read(muc_online_room, {"8799879",
"conference.chat.dev.com"}).
get_room_state(Room_pid) ->
{ok, R} = gen_fsm:sync_send_all_state_event(Room_pid, get_state),
R.

Ejabberd 13.12 how to add element XMPP Packet?

I am using ejabberd hook named "filter-packet" to make a module. Here I want to add an element to packet. How to do it? My Code is -
on_filter_packet({From, To, Packet}=Input) ->
Type = xml:get_tag_attr_s(list_to_binary("type"), Packet),
if (Type == <<"groupchat">>) ->
?INFO_MSG("type is group chat", []),
NPacket={Packet, [{xmlelement, "time",
[],
[{xmlcdata, "testtime"}]}]},
{From, To, NPacket};
true ->
Input
end.
This code is giving error of bad match. Any Help ?
13.12 uses different type for xmlelement.
Packet is a type of record #xmlel, so you need to insert new element to Packet#xmlel.children.
on_filter_packet({From, To, #xmlel{ children=OldChildren } = Packet}=Input) ->
...
TimeElem = #xmlel{ name = <<"time">>,
children =
[{xmlcdata, <<"testtime">>}]},
NPacket = Packet#xmlel{ children = [TimeElem|OldChildren] },
...
Not tested, but will work.

cowboy 'GET' request variables values

Please tell me, how I can get the variables values of the GET request
I do so:
{Method, Req2} = cowboy_req:method(Req),
{FwdIPRaw, Req3} = cowboy_req:header(<<"x-forwarded-for">>, Req2),
{ClientCookie, Req4} = cowboy_req:cookie(<<"cook">>, Req3),
{ok, GetVals, Req5} = cowboy_req:qs_vals(Req4),
MessageId = proplists:get_value(<<"mid">>, GetVals),
EchoName = proplists:get_value(<<"m">>, GetVals),
{ok, Req5, State}.
But I get an error: qs_vals - undefined
For current cowboy version (0.9.0) documentation says that cowboy_req:qs_vals(Req4) returns {QsVals, Req5} where QsVals can be [] (empty list).
So try 0.9.0 cowboy version and fix call {ok, GetVals, Req5} = cowboy_req:qs_vals(Req4) to {GetVals, Req5} = cowboy_req:qs_vals(Req4).

Resources