How do I set custom parameters for Twilio TVOCallInvite on iOS? - ios

I'm trying to pass custom parameters in call invite structure but don't receive them on the recipient side.
let connectOptions: TVOConnectOptions = TVOConnectOptions(accessToken: token) { (builder) in
builder.params = ["To": "Recipeint Id",
"From": "Caller name"]
builder.uuid = uuid
}
self.call = TwilioVoice.connect(with: connectOptions, delegate: self)
Any ideas?

You need to add logic in backend or you can say server code for the same.
Link for server code in node
https://github.com/twilio/voice-quickstart-server-node
need to modify below function
function makeCall(request, response) {
// The recipient of the call, a phone number or a client
var to = null;
if (request.method == 'POST') {
to = request.body.to;
callerId = request.body.from; //--> new added line for caller name
} else {
to = request.query.to;
callerId = request.body.from; //--> new added line for caller name
}
const voiceResponse = new VoiceResponse();
if (!to) {
voiceResponse.say("Congratulations! You have made your first call! Good bye.");
} else if (isNumber(to)) {
const dial = voiceResponse.dial({callerId : callerNumber});
dial.number(to);
} else {
const dial = voiceResponse.dial({callerId : callerId});
dial.client(to);
}
console.log('Response:' + voiceResponse.toString());
return response.send(voiceResponse.toString());
}
also need make var instead of const for callerId variable , and If you are passing caller name then node code format should save value in this format
'client:callername'
i.e. client: and after that value which is being passed from iOS app

Gave up actually with this idea... Followed this instruction so that the app reports a call to CallKit and then updates the caller name after that.

Related

twilio/voice-sdk does not listen incoming call listener when I call on my twilio number

I am using the following stack with versions
Laravel (9.11) vue.js (2.x) php (8.1.0) twilio/voice-sdk
(2.1.1) twilio/sdk (6.37)
Workflow of my application:
I am making an inbound contact center for voice calls by using a task router, where a customer initiates the call from his/her phone to our contact center base number(+1 873 --- 0331)
Step #1
when the user call on this number(+1 873 --- 0331) voice webhook is called with the following code for IVR
public function webhookForContactCenterBaseNumber(Request $request)
{
$response = new VoiceResponse();
$params = array();
$params['action'] = secure_url('/api/webhook-for-contact-center-ivr');
$params['numDigits'] = 1;
$params['timeout'] = 10;
$params['method'] = "POST";
$gather = $response->gather($params);
$gather->say('For Spanish, please press one.', ['language' => 'es']);
$gather->say('For Enghlish,please press two.', ['language' => 'en']);
return $response;
}
Step #2
When the user presses A digit(1/2) I create a task with workflow via the task router
public function webhookForContactCenterIvr(Request $request)
{
$response = new VoiceResponse();
$digits = $request['Digits'];
$language = $digits == 1 ? 'es' : 'en';
switch ($digits) {
case 1 || 2:
$response->enqueue(null, [
'waitUrl' => 'http://twimlets.com/holdmusic?Bucket=com.twilio.music.classical',
'workflowSid' => 'WW456fb07f4fdc4f55779dcb6bd90f9273'
])
->task(json_encode([
'selected_language' => $language,
]));
break;
default:
$response->say("Sorry, Caller. You can only press 1 for spanish, or 2 for english.");
break;
}
return $response;
}
step #3
After task creation, I make the targeted agent available manually from the console with the label ‘idle’, then following webhook called.
According to documentation bridge call was created between caller and agent Twilio phone number via Twilio caller id
public function assigment(Request $request)
{
$assignment_instruction = [
'instruction' => 'dequeue',
'post_work_activity_sid' => 'WA92871fe67075e6556c02e92de6---924',
'from' => '+1647---4676' // a verified phone number from your twilio account
];
return $this->respond($assignment_instruction, ['Content-Type', 'application/json']);
}
Call logs:
step #4
namespace App\Http\Controllers\Api;
use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VoiceGrant;
use Illuminate\Http\Request;
use Twilio\Rest\Client;
class TwilioController extends ApiController
{
// Required for all Twilio access tokens
private $twilioAccountSid;
private $twilioAccountAuthToken;
private $twilioApiKey;
private $twilioApiSecret;
private $identity;
public function __construct()
{
$this->twilioAccountSid = config('general.twilio_account_sid');
$this->twilioAccountAuthToken = config('general.twilio_auth_token');
$this->twilioApiKey = 'SK45e57c57f923e5c3c0903f48b70ba9de';
$this->twilioApiSecret = 'uqDNnlnDZbWZCKBwlmMdlMIIonhh3X3K';
// choose a random username for the connecting user
$this->identity = 'daffdfwerweds';
}
public function getCallAccessToken()
{
$token = new AccessToken(
$this->twilioAccountSid,
$this->twilioApiKey,
$this->twilioApiSecret,
3600,
$this->identity
);
// Create Voice grant
$voiceGrant = new VoiceGrant();
// Optional: add to allow incoming calls
$voiceGrant->setIncomingAllow(true);
// Add grant to token
$token->addGrant($voiceGrant);
return $this->respond([
'status' => true,
'message' => '',
'data' => [
'accessToken' => $token->toJWT()
]
]);
}
public function getTwilioKey($frindlyName)
{
$twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
return $twilio->newKeys->create(["friendlyName" => $frindlyName]);
}
public function getKeys()
{
$twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
$keys = $twilio->keys
->read(20);
foreach ($keys as $record) {
$twilio->keys($record->sid)
->delete();
}
}
public function getAllCalls(Request $request)
{
$twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
$calls = $twilio->calls
->read([], 20);
foreach ($calls as $record) {
// print($record->sid);
$twilio->calls($record->sid)
->delete();
}
}
}
Step #5
I have installed twilio/voice-sdk in vue and register my device with following code
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImN0eSI6InR3aWxpby1mcGE7dj0xIn0.eyJqdGkiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlLTE2NTU3MzgxNjMiLCJpc3MiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlIiwic3ViIjoiQUMwMWExYTRmMDdjMGMwMDlhMmIyZTEyYmJkZWVhYjQ2NSIsImV4cCI6MTY1NTc0MTc2MywiZ3JhbnRzIjp7ImlkZW50aXR5IjoiZGFmZmRmd2Vyd2VkcyIsInZvaWNlIjp7ImluY29taW5nIjp7ImFsbG93Ijp0cnVlfX19fQ.4COIn-EQMQnD6alKUSOZPGIWG3jB5k17K418xCsSiZs"
const device = new Device(accessToken, {
logLevel: 1,
// Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
// providing better audio quality in restrained network conditions.
codecPreferences: ["opus", "pcmu"]
});
const handleSuccessfulRegistration = () => {
console.log('The device is ready to receive incoming calls.')
}
device.register();
device.on('registered', handleSuccessfulRegistration);
device.on('error', (twilioError, call) => {
console.log('An error has occurred: ', twilioError);
});
device.on('incoming', call => {
console.log('call received-----------------')
});
Verify token on jwt.io
Test Device Registration in console:
I was facing the same issue, thanks for detailed information, I go through the whole detail, and here is the answer after that issue will be fixed,
in step #4 you are creating call access token, and you are adding worker/agent identity you need to add some identity against the worker inside the Twilio console, in your case, it should be like that,
in code
$this->identity = 'daffdfwerweds';
in Twilio console under task router/workspace/workers/open target work
most important part
{contact_uri":"client:daffdfwerweds"}
Your browser will listen the incoming call via SDK if call router toward you this worker.
that's all.

how to get dynamically the caller phone number and pass it as one of JSON key in Twilio run function wdget?

I am new in Twilio and trying to develop an IVR that at some point runs a function (Run Fuuntion Widget). The function should send http request including the user phone number to a service provider to make payment for a product selected by the user.
I tryed the code bellow but the variable is not getting the user phone number.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
var Client = require("#paymentsds/mpesa").Client;
var phoneNumber = event.From
var phoneNumberNorm = phoneNumber.substring(4);
var client = new Client({
apiKey: 'xxxxxxxxxxxxxxxxxx', // API Key
publicKey: 'xxxxxxxxxxxxxxxxxx', // Public Key
serviceProviderCode: '171717' // input_ServiceProviderCode
});
var paymentData = {};
paymentData['from'] = phoneNumberNorm;
paymentData['reference'] = '11114';
paymentData['transaction'] = 'T12344CC';
paymentData['amount'] = '10';
client.receive(paymentData).then(function(r) {
// Handle success scenario
twiml.say("Your payment was successfull");
callback(null, twiml);
}).catch(function(e) {
// Handle success scenario
twiml.say("Your payment failed");
callback(null, twiml);
});
};
Twilio developer evangelist here.
You will need to pass the phone number into the Function via the Run Function widget.
You need to add a Function Parameter, for example From, and then the data you want to pass, which for the incoming phone number is {{contact.channel.address}}. Like so:
Then you can use event.From in your Function code.

Stripe IOS SDK not matching AllResponseFields?

I'm trying to do a simple count check for a customer to see if they need to add a source before moving onto another page. However, my calls to the STPCustomer object's attributes are not matching the information for the full response.
I've tried clearing the customer cache and reloading the customer, but uh... still no match.
Here's a truncated like, essentials code. The class has the STPPaymentContextDelegate attached.
private var customerContext = STPCustomerContext?
private var paymentContext = STPPaymentContext?
func setupStripe() {
self.paymentContext = STPPaymentContext(customerContext: self.customerContext!)
self.paymentContext?.hostViewController = self
self.paymentContext?.delegate = self
self.paymentView = STPPaymentMethodsViewController(configuration: STPPaymentConfiguration.shared(), theme: STPTheme.default(), customerContext: self.customerContext!, delegate: self)
}
func getCustomerSources() {
if let customer = customerContext.retrieveCustomer({ (customer, error) in
if customer != nil {
print(customer.sources.count)
print(customer.sources)
print(customer.allResponseFields)
}
})
}
When I run getCustomerSources() on the test customer, I am expecting:
1
[ba_1Dvf46LrBVaGM6Sq9qIYhOlJ]
AnyHashable("id"): cus_number, AnyHashable("email"): rosa_diaz#gmail.com, AnyHashable("default_source"): ba_1Dvf46LrBVaGM6Sq9qIYhOlJ, AnyHashable("created"): 1548220659, AnyHashable("description"): LiG8WbVhT8SVhta8LdfUuWzOwQn2, AnyHashable("livemode"): 0, AnyHashable("object"): customer, AnyHashable("sources"): {
data = (
{
"bank_name" = "STRIPE TEST BANK";
country = US;
currency = usd;
customer = "cus_EOQzwwGPjzopjS";
fingerprint = 1AQMB9nzeGSGXHst;
id = "ba_1Dvf46LrBVaGM6Sq9qIYhOlJ";
last4 = 6789;
metadata = {
};
object = "bank_account";
"routing_number" = 110000000;
status = verified;
}
);
"has_more" = 0;
object = list;
"total_count" = 1;
url = "/v1/customers/cus_EOQzwwGPjzopjS/sources";
}])
However, instead of my 1 and ba_1Dvf46LrBVaGM6Sq9qIYhOlJ, I am getting 0 and []. But the allResponseFields section is the same.
Any ideas as to why there's a discrepancy between what the STPCustomer object attributes are returning and what the actual response is telling me?
it's hard to say what's going on without knowing more about the error parameter that your callback receives. It sounds like there might be an error parsing the API response, and printing the error passed into your retrieveCustomer block should give you a better idea about what's going on. If the error persists I'd recommend contacting support#stripe.com with details, as they can help directly with iOS app questions!

putting an outgoing call from a twilio client to a phone on hold(Get phone side call Sid)

I am trying to implement 'hold call' functionality into my system. I have looked at this question and some twilio docs, and recommended way to do this is to dequeue the call, which will automatically play the hold music, and then retrieve from queue when hold is complete(un-hold).
This works perfectly fine, when I have an incoming call from a mobile, to my twilio client, and I can dequeue the call, and all works fine.
However when I do it with the outgoing call from the twilio client to the mobile, if I update the call to issue dequeue instruction, I get to hear the hold music on the client side itself, and the phone gets disconnected. I suppose this is because I have set the client leg of the call on hold. So the question is how do I get the call sid for the mobile leg of the call.
I have tried querying CallResource by ParentCallId, but that does not return anything in the case of outgoing calls. Any ideas?
Call is initiated on the client with:
var params = {
To: num
};
console.log('Calling ' + num + '...');
Twilio.Device.connect(params);
The connect API callback uses a simple Dial verb.
Client code for saving callid on connect:
Twilio.Device.connect(function (conn) {
if (conn._direction === 'OUTGOING') {
$scope.outgoing_call_sid = conn.parameters.CallSid;
$scope.number = conn.message.To;
} else {
$scope.incoming_call_sid = conn.parameters.CallSid;
$scope.number = conn.parameters.From;
}
$scope.message = 'In call with ' + $scope.number;
$scope.status = 'InCall';
});
Client code on hold button click:
$scope.hold = function () {
$scope.status = 'Hold';
$scope.message = 'Putting on hold...';
if ($scope.outgoing_call_sid) {
return $http.get(serviceBase + 'api/twilio/hold?callid=' + $scope.outgoing_call_sid);
}
};
Server side Hold API call:
public IHttpActionResult Hold(string callid) { /
//callid = GetLegCallId(callid); //Try to replace with child call
CallResource.Update(new UpdateCallOptions(callid) { Url = ConfigurationManager.AppSettings["ngrokUrl"] + "/api/twilio/enqueue", Method = HttpMethod.Get });
return Ok();
}
Code for getting any child calls:
public string GetLegCallId(string callId)
{
var calls = CallResource.Read(new ReadCallOptions() { ParentCallSid = callId });
if (calls.GetEnumerator().Current != null)
return calls.GetEnumerator().Current.Sid;
}
My bad. Twilio wasnt the issue. Issue was with usage of calls.GetEnumerator().Current != null.
Should do MoveNext on the Enumerator, before Current will have a value. Resolved by doing that. Stupid:(

Backbone.js: POST request with empty value

I am trying to make a POST request.
Here my code:
var myModel = new MydModel({
content: "ciao"
});
console.log(myModel.get("content")); // "ciao"
myModel.save();
If I look to the network activity it looks like this:
The response part {id:0, content:"", ……}
In the header part: Request Payload {"content":"ciao"}
Here my model:
define([], function () {
var MyModel = Backbone.Model.extend({
url: function url ()
{
return "http://localhost/users";
}
});
return MyModel;
});
Is it my problem or is it in the server part?
send/receive vs request/response
a server receives requests and sends responses
a client sends requests and receives responses
in short
if {id:0, content:"", ……} (the response) is wrong, it's your server
if {"content":"asdasdsa"} (the request) is wrong, it's your client
There is little problem with receiving JSON-payload that "Backbone-client" sends to your Apache-server.
All you need to do is to manually parse JSON-payload from input on the server side ("php://input", for PHP), like this:
if($_SERVER['REQUEST_METHOD'] == 'PUT' || $_SERVER['REQUEST_METHOD'] == 'POST') {
$postStr = file_get_contents("php://input");
//json_decode throws error or returns null if input is invalid json
try {
$json = json_decode($postStr, true);
if(empty($json)) {
throw new Exception("Not valid json");
}
//must not be json, try query str instead
} catch(Errfor $e) {
$postVars = parse_str($postStr);
foreach($postVars as $key=>$data) {
$_POST[$key] = $data;
}
}
}
Full explanation you can find here:
http://colinbookman.com/2014/04/08/php-puts-posts-and-backbone-js/

Resources