Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to better implement the call transfer function #10

Open
estvita opened this issue Jan 9, 2025 · 7 comments
Open

How to better implement the call transfer function #10

estvita opened this issue Jan 9, 2025 · 7 comments

Comments

@estvita
Copy link
Contributor

estvita commented Jan 9, 2025

Hello! In my personal project, where I started using the connector, I realized I needed to implement a function to transfer a call from assistant to a live operator (or an operator queue). On the voice assistant’s side, I understand that I need to create a new “call transfer” feature, which the assistant can invoke at the subscriber’s request. This function would obtain the destination number (for example, from a database or configuration) and then instruct OpenSIPS to redirect the call. From what I’ve gathered, I can use either the callops module or the call-api module to accomplish this. Am I moving in the right direction?

@razvancrainea
Copy link
Member

call-api/callops is not the right tool to do this, as they do not work with B2B calls - however, you can manually issue a REFER message using ua_session_update within the dialog to achieve the transfer.

@estvita
Copy link
Contributor Author

estvita commented Jan 9, 2025

@razvancrainea
ok, thanks for the tip
i did it like this

def mi_update(params):
    resp = mi_conn.execute('ua_session_update', params)
    print(resp)

...
elif msg["name"] == "transfer_call":
    params = {
        "key": self.call.b2b_key,
        "method": "REFER",
        "body": "Refer-To: <sip:999@asterisk>\r\n"
                "Referred-By: <sip:openai@opensips>\r\n"
    }
    mi_update(params)

the request goes to asterisk, but in response I get a message about an invalid request


2025/01/09 17:52:10.651996 opensips:5060 -> asterisk:5060
REFER sip:separator@asterisk:5060 SIP/2.0
Via: SIP/2.0/UDP opensips:5060;branch=z9hG4bKf3d1.3f5ac9f7.0
To: <sip:separator@opensips>;tag=e6bf1cd2-2893-4dbe-9c49-e8fb5a066430
From: <sip:openai@opensips>;tag=B2B.9.0.1736434314.2002050160
CSeq: 2 REFER
Call-ID: dad060bb-feab-4c22-b743-4095f9c10d77
Max-Forwards: 70
Content-Length: 81
User-Agent: OpenSIPS (3.5.3 (x86_64/linux))
Content-Type: application/sdp
Contact: <sip:opensips:5060>

Refer-To: <sip:999@asterisk>
Referred-By: <sip:openai@opensips>



2025/01/09 17:52:10.652489 asterisk:5060 -> opensips:5060
SIP/2.0 400 Bad Request
Via: SIP/2.0/UDP opensips:5060;rport=5060;received=opensips;branch=z9hG4bKf3d1.3f5ac9f7.0
Call-ID: dad060bb-feab-4c22-b743-4095f9c10d77
From: <sip:openai@opensips>;tag=B2B.9.0.1736434314.2002050160
To: <sip:separator@opensips>;tag=e6bf1cd2-2893-4dbe-9c49-e8fb5a066430
CSeq: 2 REFER
Reason: Q.850;cause=16
Server: FPBX-17.0.19.23(21.5.0)
Content-Length:  0

@razvancrainea
Copy link
Member

The Refer-To and Referred-By should not be in body, but rather in extra_headers argument of the ua_session_update MI command.
It would be nice to have the values of Refer-To configurable as well when doing the transfer. I believe the Referred-By header can be extracted from the To header of the call, right?

@estvita
Copy link
Contributor Author

estvita commented Jan 10, 2025

yes, I understand about dynamic parameters that I will extract from headers, now I want to work out the call transfer scenario on static data

@estvita
Copy link
Contributor Author

estvita commented Jan 11, 2025

@razvancrainea
I did the following

                elif msg["name"] == "transfer_call":
                    params = {
                        "key": self.call.b2b_key,
                        "method": "REFER",
                        "extra_headers": (
                            "Refer-To: <sip:999@asterisk>\r\n"
                            "Referred-By: <sip:openai@opensips>\r\n"
                        )
                    }
                    mi_update(params)

and this is what happens next:

2025/01/11 15:35:22.886174 opensips:5060 -> asterisk:5060
REFER sip:separator@asterisk:5060 SIP/2.0
Via: SIP/2.0/UDP opensips:5060;branch=z9hG4bK7e55.f2fc7467.0
To: <sip:separator@opensips>;tag=00e49590-18f0-48a9-a846-c7905d5d8fd2
From: <sip:openai@opensips>;tag=B2B.387.358.1736591714.815896195
CSeq: 2 REFER
Call-ID: 29ee759d-3552-4944-b120-ca8242785208
Max-Forwards: 70
Content-Length: 0
User-Agent: OpenSIPS (3.5.3 (x86_64/linux))
Content-Type: application/sdp
  1. Request-Line - REFER sip:separator@asterisk:5060 SIP/2.0 does not contain the number to which I am directing the call, it displays the name of the account in Asterisk from which the call is made
  2. no sip headers Refer-To and Referred-By which are specified in the code

@estvita
Copy link
Contributor Author

estvita commented Jan 18, 2025

I launched the connector in debug mode, and there I see that ua_session_update passes the necessary headers (Refer-To and Referred-By ) to opensips, but for some reason opensips does not pass them to asterisk, so I get a 408 error

ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:mi_datagram:mi_datagram_callback: received 244 |{"jsonrpc": "2.0", "id": "12979", "method": "ua_session_update", "params": {"key": "B2B.267.105.1737119835.749594385", "method": "REFER", "extra_headers": "Refer-To: <sip:999@asterisk_ip>\r\nReferred-By: <sip:openai@opensips_ip>\r\n"}}|
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:b2b_entities:b2b_parse_key: hash_index = [267]  - local_index= [105]
ai-voice-connector-engine    | 2025-01-17 13:17:22,064 - tid: 140368651212608 - INFO - response.output_item.done
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:b2b_entities:b2b_parse_key: hash_index = [267]  - local_index= [105]
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:b2b_entities:_b2b_send_request: Send request [REFER] for entity type [0] for dlg[0x7fa5682de740]->[B2B.267.105.1737119835.749594385]
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:t_uac: next_hop=<sip:asterisk@asterisk_ip:5060>
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:core:mk_proxy: doing DNS lookup...
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:t_uac: sending socket is opensips_ip 
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:print_request_uri: sip:asterisk@asterisk_ip:5060
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:set_timer: relative timeout is 500000
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:insert_timer_unsafe: [4]: 0x7fa5682e0ab0 (21300000)
ai-voice-connector-engine    | 2025-01-17 13:17:22,074 - tid: 140368651212608 - INFO - response.done
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:tm:insert_timer_unsafe: [0]: 0x7fa5682e0ae0 (22)
ai-voice-connector-opensips  | Jan 17 13:17:22 [8] DBG:mi_datagram:mi_datagram_callback: the response: {"jsonrpc":"2.0","result":"OK","id":"12979"} has been sent in 44 octets
ai-voice-connector-opensips  | Jan 17 13:17:22 [66] DBG:tm:utimer_routine: timer routine:4,tl=0x7fa5682e0ab0 next=(nil), timeout=21300000
ai-voice-connector-opensips  | Jan 17 13:17:22 [66] DBG:tm:retransmission_handler: retransmission_handler : request resending (t=0x7fa5682e0890, REFER sip ... )
ai-voice-connector-opensips  | Jan 17 13:17:22 [66] DBG:tm:set_timer: relative timeout is 1000000
ai-voice-connector-opensips  | Jan 17 13:17:22 [66] DBG:tm:insert_timer_unsafe: [5]: 0x7fa5682e0ab0 (22300000)
ai-voice-connector-opensips  | Jan 17 13:17:22 [66] DBG:tm:retransmission_handler: retransmission_handler : done
ai-voice-connector-engine    | 2025-01-17 13:17:22,813 - tid: 140368651212608 - INFO - Speaker: Please connect me to the operator
ai-voice-connector-opensips  | Jan 17 13:17:23 [55] DBG:tm:utimer_routine: timer routine:5,tl=0x7fa5682e0ab0 next=(nil), timeout=22300000
ai-voice-connector-opensips  | Jan 17 13:17:23 [55] DBG:tm:retransmission_handler: retransmission_handler : request resending (t=0x7fa5682e0890, REFER sip ... )
ai-voice-connector-opensips  | Jan 17 13:17:23 [55] DBG:tm:set_timer: relative timeout is 2000000
ai-voice-connector-opensips  | Jan 17 13:17:23 [55] DBG:tm:insert_timer_unsafe: [6]: 0x7fa5682e0ab0 (24300000)
ai-voice-connector-opensips  | Jan 17 13:17:23 [55] DBG:tm:retransmission_handler: retransmission_handler : done
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:timer_routine: timer routine:0,tl=0x7fa5682e0ae0 next=(nil), timeout=22
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:final_response_handler: Cancel sent out, sending 408 (0x7fa5682e0890)
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:t_should_relay_response: T_code=0, new_code=408
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:t_pick_branch: picked branch 0, code 408 (prio=800)
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:is_3263_failure: dns-failover test: branch=0, last_recv=408, flags=0
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:t_should_relay_response: trying DNS-based failover
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:local_reply: branch=0, save=0, winner=0
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:local_reply: local transaction completed
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:run_any_trans_callbacks: trans=0x7fa5682e0890, callback type 256, id 0 entered
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:b2b_entities:b2b_tm_cback: tm [0x7fa5682e0890] notification cb for [408] reply
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:b2b_entities:b2b_parse_key: hash_index = [267]  - local_index= [105]
ai-voice-connector-engine    | 2025-01-17 13:17:23,942 - tid: 140368651212608 - ERROR - Failed to send reply: Error executing command: 500: Failed to send reply
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:b2b_entities:b2b_tm_cback: Received reply [408] for dialog [0x7fa5682de740], method [REFER]
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_to_param: tag=809b5324-c0a9-4cf4-8fba-e741a6f122d7
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_to_param: end of header reached, state=11
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: end of header reached, state=29
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: display={"990"}, ruri={sip:990@asterisk_ip}
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_to_param: tag=B2B.267.105.1737119835.749594385
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_to_param: end of header reached, state=13
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: end of header reached, state=29
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: display={}, ruri={sip:openai@opensips_ip}
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str key='B2B.267.105.1737119835.749594385'
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str entity_type='UAS'
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str event_type='REJECTED'
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str method='REFER'
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set int status=408
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str reason='Timeout'
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str body=''
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_headers_aux: flags=ffffffffffffffff
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str headers='From: <sip:openai@opensips_ip>;tag=B2B.267.105.1737119835.749594385
ai-voice-connector-opensips  | '
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_param_set: set str extra_params=''
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_dummy_sip_msg: allocating new sip msg
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_msg: SIP Request:
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_msg:  method:  <DUMMY>
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_msg:  uri:     <sip:[email protected]>
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_msg:  version: <SIP/2.0>
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_headers_aux: flags=ffffffffffffffff
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_via_param: found param type 232, <branch> = <z9hG4bKdummy>; state=16
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_via: end of header reached, state=5
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_headers_aux: via found, flags=ffffffffffffffff
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_headers_aux: this is the first via
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: end of header reached, state=10
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:_parse_to: display={}, ruri={sip:[email protected]}
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_hdr_field_aux: <To> [20]; uri=[sip:[email protected]] 
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_hdr_field_aux: to body [<sip:[email protected]>
ai-voice-connector-opensips  | ]
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_hdr_field_aux: cseq <CSeq>: <1> <DUMMY>
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_hdr_field_aux: found end of header
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:parse_headers_aux: flags=ffffffffffffffff
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:get_dummy_sip_msg: setting as static to 0x7fa56a158448
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:evi_raise_event_msg: found subscriber 127.0.0.1
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:release_dummy_sip_msg: cleaning the static sip msg 0x7fa56a158448
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:core:destroy_avp_list: destroying list (nil)
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:insert_timer_unsafe: [2]: 0x7fa5682e0910 (27)
ai-voice-connector-opensips  | Jan 17 13:17:23 [13] DBG:tm:final_response_handler: done
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:mi_datagram:mi_datagram_callback: received 184 |{"jsonrpc": "2.0", "id": "32236", "method": "ua_session_reply", "params": {"key": "B2B.267.105.1737119835.749594385", "method": "REFER", "code": 405, "reason": "Method not supported"}}|
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:b2b_entities:b2b_parse_key: hash_index = [267]  - local_index= [105]
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:b2b_entities:_b2b_send_reply: For server entity
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:b2b_entities:b2b_parse_key: hash_index = [267]  - local_index= [105]
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:b2b_entities:_b2b_send_reply: code = 405, last_method= 4096
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] ERROR:b2b_entities:_b2b_send_reply: Tm transaction not saved!
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] ERROR:b2b_entities:b2b_ua_mi_reply: Failed to send reply
ai-voice-connector-opensips  | Jan 17 13:17:23 [8] DBG:mi_datagram:mi_datagram_callback: the response: {"jsonrpc":"2.0","error":{"code":500,"message":"Failed to send reply"},"id":"32236"} has been sent in 84 octets

@estvita
Copy link
Contributor Author

estvita commented Jan 26, 2025

hi
i found a temporary solution - if i substitute an empty parameter "body" then the extra_headers Refer-To and Referred-By are passed in the request

elif msg["name"] == "transfer_call":
    params = {
        'key': self.call.b2b_key,
        'method': "REFER",
        'extra_headers': ( "Refer-To: <sip:999@asterisk>\r\n"
                            "Referred-By: <sip:openai@opensips>\r\n"),
        'body': ""
    }
    mi_conn.execute('ua_session_update', params)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants