Implementing Call Transfer via button click - twilio

I'm looking to implement a call system that supports call transfer on our 'softphone' built using the Voice SDk, and frankly, I'm overwhelmed by the numerous articles on this but most are outdated (early 2015) or not just working for some reason.
My code involves initiating calls as a conference so warm transfer can be utilized. Hence, I'll demonstrate one side of the call (incoming) for this example.
Lead calls one of the sales person on our app, say SalesPerson A, SpA for short.
SpA would like, probably even before picking the call, transfer it to SpB on the same app.
I'm using each sales person fullname as Identity with every token (return jsonify(identity=fetch_user_fullname(), token=token.to_jwt().decode()), and call transfer is only applicable to all online sales persons.
#app.route('/call', methods=['POST'])
def inbound():
call_sid = flask.request.values.get('CallSid')
response = VoiceResponse()
response.dial().conference(call_sid)
call = client.calls.create(to='client:salespersonA',
from_='twilio_number_called_by_lead',
url = BASE_URL + '/conf/' + call_sid)
return Response(str(response), mimetype='application/xml')
#app.route('/conf/<conference_name>', methods=['GET', 'POST'])
def conference_line(conference_name):
response = VoiceResponse()
response.dial(hangupOnStar=True).conference(conference_name)
response.gather(action=BASE_URL + '/add-agent/' + conference_name,
numDigits=1)
return Response(str(response), 200, mimetype="application/xml")
#app.route('/add-agent/<conference_name>', methods=['POST'])
def add_second_agent(conference_name):
client.calls.create(to='client:salespersonB', from_='twilio_number_called_by_lead',
url=BASE_URL + '/conference/' + conference_name)
response = VoiceResponse()
response.dial(hangupOnStar=True).conference(conference_name)
return Response(str(response), 200, mimetype="application/xml")
My problem with this is since a conference call is initiated when a salesperson answers the line, that associated twilio account is billed (I would have like that caller gets billed only) - due to my twilio number initiating conference call to salespersonA
Also, my issue is most of these holders have a null value and that when lead calls and the conference is initiated, the caller ID to our salespersonA is the twilio number lead dialed, I would have expected the caller ID to be the lead's contact.
Response: 201 {"date_updated": null, "price_unit": "USD", "parent_call_sid": null, "caller_name": null, "duration": null, "from": "twilio_number_called_by_lead", "to": "client:salespersonA", "annotation": null, "answered_by": null, "sid": "CA04...", "queue_time": "0", "price": null, "api_version": "2010-04-01", "status": "queued", "direction": "outbound-api", "start_time": null, "date_created": null, "from_formatted": "twilio_number_called_by_lead_formatted", "group_sid": null, "trunk_sid": null, "forwarded_from": null, "uri": "/2010-04-01/Accounts/AC6../Calls/CA04....json", "account_sid": "AC6", "end_time": null, "to_formatted": "LABEL-9EQk7FJ", "phone_number_sid": null, "subresource_uris": {"feedback": "/2010-04-01/Accounts/AC6/Calls/CA04.../Feedback.json", "notifications": "/2010-04-01/Accounts/AC6/Calls/CA04.../Notifications.json", "recordings": "/2010-04-01/Accounts/AC6.../Calls/CA04.../Recordings.json", "streams": "/2010-04-01/Accounts/AC6.../Calls/CA04.../Streams.json", "payments": "/2010-04-01/Accounts/AC6.../Calls/CA04.../Payments.json", "siprec": "/2010-04-01/Accounts/AC6.../Calls/CA04.../Siprec.json", "events": "/2010-04-01/Accounts/AC6.../Calls/CA04.../Events.json", "feedback_summaries": "/2010-04-01/Accounts/AC6.../Calls/FeedbackSummary.json"}}
This implementation uses keypad to transfer the call, I'm looking to use a button. And Gather does not listen to buttons so I'm lost.
call_sid: CA044876bcd8b74071f9bf9daec3bbae01

Related

Can you filter out personal details from Python Twilio logger?

By default the Twilio Python client seems to log out personal information (emails and numbers). I know you can access the logger like this twilio_logger = logging.getLogger('twilio.http_client')
Is there a simple setting change we can make to filter out this personal information, either directly through the library or by changing the logger?
NOTE: I have attempted to do this by creating a custom formatter:
class SensitiveDataFormatter(logging.Formatter):
"""Formatter that removes sensitive information in urls."""
#staticmethod
def _filter(s):
print('filter')
return re.sub(r'([A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,})', r'[email_redacted]', s)
def format(self, record):
original = logging.Formatter.format(self, record)
return self._filter(original)
LOGGING = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'sensitive'
},
},
'root': {
'handlers': ['console'],
'level': 'INFO'
},
'formatters': {
'sensitive': {
'()': 'my_project.settings.logging.sensitive_data_formatter.SensitiveDataFormatter'
}
}
}
This works in the general case, however I'm struggling to apply it to Twilio
It seems that you can apply filters to Python loggers using logger config and a custom class that inherits from logging.Filter.
I'm not a Python developer, so pointing you to this blog post is the best way I can answer this question. I will note that the post appears to be from 2014, so may need updates for Python 3, but the Python logging documentation is also pretty extensive, so should help you out here.
Here is how you can configure the Twilio logger in the Python library to use a custom formatter. To test this I took your formatter and modified it to redact Twilio accounts, messaging services and phone numbers.
client = Client(account_sid, auth_token)
class SensitiveDataFormatter(logging.Formatter):
#staticmethod
def _filter(s):
filters = [
[r'(AC[a-f0-9]+)', '[account_redacted]'],
[r'(MG[a-f0-9]+)', '[messaging_service_redacted]'],
[r'(\+[0-9]+)', '[phone_redacted]'],
]
for f in filters:
s = re.sub(f[0], f[1], s)
return s
def format(self, record):
original = logging.Formatter.format(self, record)
return self._filter(original)
# create a logging handler with the custom formatter attached
handler = logging.StreamHandler()
handler.setFormatter(SensitiveDataFormatter())
# add the stream handler to the Twilio logger
client.http_client.logger.addHandler(handler)
client.http_client.logger.setLevel(logging.INFO)
Example log after sending an SMS:
POST Request: https://api.twilio.com/2010-04-01/Accounts/[account_redacted]/Messages.json
PAYLOAD: {'To': '[phone_redacted]', 'From': '[messaging_service_redacted]', 'Body': 'Friendly reminder that you have an appointment with us next week.'}
POST Response: 201 {"sid": "SM4428eb430949461d9bd58d44da1dd999", "date_created": "Thu, 19 May 2022 08:57:49 [phone_redacted]", "date_updated": "Thu, 19 May 2022 08:57:49 [phone_redacted]", "date_sent": null, "account_sid": "[account_redacted]", "to": "[phone_redacted]", "from": null, "messaging_service_sid": "[messaging_service_redacted]", "body": "Friendly reminder that you have an appointment with us next week.", "status": "accepted", "num_segments": "0", "num_media": "0", "direction": "outbound-api", "api_version": "2010-04-01", "price": null, "price_unit": null, "error_code": null, "error_message": null, "uri": "/2010-04-01/Accounts/[account_redacted]/Messages/SM4428eb430949461d9bd58d44da1dd999.json", "subresource_uris": {"media": "/2010-04-01/Accounts/[account_redacted]/Messages/SM4428eb430949461d9bd58d44da1dd999/Media.json"}}

Inconsistency in MS Teams graph api - GET channels

In the response of GET - https://graph.microsoft.com/v1.0/teams/{team_id}/channels?$filter=membershipType eq 'private'.
I am getting channels which are not visible in the Teams app interface.
e.g.
{"id": "",
"createdDateTime": "",
"displayName": "testingteam",
"description": null,
"isFavoriteByDefault": null,
"email": null,
"webUrl": null,
"membershipType": "private"
},
Is there any key using which i can differentiate these forbidden channels from the valid ones?
We are not able to repro this issue. The graph api returned correct response. Can you check if all webURL part for all such teams you are getting is null. If yes you can add filter ($filter=webUrl ne null).

Exact change GLAccount for BankEntryLine

Currently we import our bank transactions. Through the REST API I read all these transactions and try to match them to our internal invoices.
If I find a match I need to change the GLAccountCode from for example 1000 to 2000 for this particular BankEntryLine. All I see on the BankEntryLine is that I can do a GET or POST but no PUT method.
Is there something wrong with my approach? Like do I have to create something else that reconciles this transaction or is there a different way of updating this transaction line?
Example BankEntryLine:
{
"d": {
"__metadata": {
"uri": "https://start.exactonline.nl/api/v1/000000/financialtransaction/BankEntryLines(guid'123000000-0000-0000-0000-000000000000')",
"type": "Exact.Web.Api.Models.Financial.BankEntryLine"
},
"Document": "00000000-0000-0000-0000-000000000000",
"DocumentNumber": 00000,
"EntryID": "00000000-0000-0000-0000-000000000000",
"EntryNumber": 00000000,
"ExchangeRate": 1,
"GLAccount": "100000000-0000-0000-0000-000000000000",
"GLAccountCode": "1000",
"ID": "123000000-0000-0000-0000-000000000000",
"LineNumber": 1,
"OffsetID": "000000000-0000-0000-0000-000000000000",
"OurRef": null,
"Project": null,
"ProjectCode": null,
"ProjectDescription": null,
"Quantity": null,
"VATCode": "4 "
}
}
API documentation: https://start.exactonline.nl/docs/HlpRestAPIResources.aspx?SourceAction=10
BankEntryLine: https://start.exactonline.nl/docs/HlpRestAPIResourcesDetails.aspx?name=FinancialTransactionBankEntryLines
There is no PUT or DELETE available for this API. I don't directly see another way to update/delete those lines.
Only possible workaround is to make a general journal entry to balance the amount of that suspense GL account to the one you need/want. But that will give you more entries and more lines to match.

How to use twilio rest api with loadrunner?

I am currently doing POC(proof of concept) on IVR loadtesting where I just want to call a IVR and have no interest in sound quality and all. So I come across twilio rest api which can make a outbound call.
It is working perfectly with python but I want to use it with loadrunner as it support rest api. I searched for twilio api urls and key value combinations. but hard luck for this. can anyone provide me the required URL and key value arrangement. or can guide me how to get those.
thanks in advance.
The documentation, Making Calls, has some code examples and example responses. You can make an API call using Postman (POST via x-www-form-urlencoded) and see the key:value pairs in the returned JSON, as shown below. API Explorer in Twilio Console is also useful for that.
{
"date_updated": null,
"price_unit": "USD",
"parent_call_sid": null,
"caller_name": null,
"duration": null,
"from": "+15095550100",
"to": "+14075550100",
"annotation": null,
"answered_by": null,
"sid": "CAaacc78574ae1b9d2bf483f0123456789",
"queue_time": "0",
"price": null,
"api_version": "2010-04-01",
"status": "queued",
"direction": "outbound-api",
"start_time": null,
"date_created": null,
"from_formatted": "(509) 555-0100",
"group_sid": null,
"trunk_sid": null,
"forwarded_from": null,
"uri": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/CAaacc78574ae1b9d2bf483f0123456789.json",
"account_sid": "ACdeca5d479509eeb8beaba0123456789",
"end_time": null,
"to_formatted": "(407) 555-0100",
"phone_number_sid": "PN0b9c2733e2a9ad0c98352b0123456789",
"subresource_uris": {
"notifications": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/CAaacc78574ae1b9d2bf483f0123456789/Notifications.json",
"recordings": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/CAaacc78574ae1b9d2bf483f0123456789/Recordings.json",
"feedback": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/CAaacc78574ae1b9d2bf483f0123456789/Feedback.json",
"payments": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/CAaacc78574ae1b9d2bf483f0123456789/Payments.json",
"feedback_summaries": "/2010-04-01/Accounts/ACdeca5d479509eeb8beaba0123456789/Calls/FeedbackSummary.json"
}}

ASPNET Waiting for a Webhook Response/Result on a WebPage

We have a web page which takes a Stripe payment, once the payment is complete Stripe can call a webhook on our server.
At this point, we can mark an Order as complete and complete any other additional tasks.
How can we have the order webpage update/move the user onto to order complete?
Should we consistently hit the server in AJAX to check if it's now complete, or is there a better way of doing this.
Q: How can we have the order webpage update/move the user onto to order complete?
Most payment engines will redirect the payment session to a URL of your choosing with a result code or a different URL per result code. These can generally be configured at the moment the request is being made or for the entire site in a general configuration. These results should not be relied on for the actual payment as that is the job for the web hook. They can be trusted enough for your site to show a general success/fail error message and let the user continue doing whatever.
Stripe also allows for this, you can specify a success_url (or successUrl in the client integration).
See Create a Checkout Session on your server for how to pass this URL to the request. You can also include a cancel_url. See the last 2 parameters in the code sample below:
curl https://api.stripe.com/v1/checkout/sessions \
-u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
-d payment_method_types[]=card \
-d line_items[][name]=T-shirt \
-d line_items[][description]="Comfortable cotton t-shirt" \
-d line_items[][images][]="https://example.com/t-shirt.png" \
-d line_items[][amount]=500 \
-d line_items[][currency]=usd \
-d line_items[][quantity]=1 \
-d success_url="https://example.com/success" \
-d cancel_url="https://example.com/cancel"
See also Checkout Purchase Fulfillment.
When your customer successfully completes their payment or initiates a subscription using Checkout, Stripe redirects them to the URL that you specified in the success_url parameter (or successUrl in the client integration). Typically, this is a page on your website that informs your customer that their payment was successful.
And as I stated above do not rely on this as the actual indicator if the payment succeeded. You should use the web hooks for that.
Do not rely on the redirect to the success_url alone for fulfilling purchases as:
Malicious users could directly access the success_url without paying and gain access to your goods or services.
Customers may not always reach the success_url after a successful payment. It is possible they close their browser tab before the redirect occurs.
Good question,
You can handle the stripe payment result to take a new effect on your page
var cardholderName = document.getElementById('cardholder-name');
var cardButton = document.getElementById('card-button');
var clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', function(ev) {
stripe.handleCardPayment(
clientSecret, cardElement, {
payment_method_data: {
billing_details: {name: cardholderName.value}
}
}
).then(function(result) {
if (result.error) {
// Display error.message in your UI.
} else {
// The payment has succeeded. update your front-end
}
});
});
Should we consistently hit the server in AJAX to check if it's now
complete, or is there a better way of doing this.
An Ajax example is here but beter way to doing that is the fetch api. You can find out all detail in here
Should we consistently hit the server in AJAX to check if it's now complete, or is there a better way of doing this.
No you shouldn't and yes there is a better way. Callback pages/webhooks would make sense if you didn't have an asp.net server handing the transaction, but they aren't necessary here.
How can we have the order webpage update/move the user onto to order
complete?
The stripe payment process only takes a couple seconds to respond with a status code. It's not the same as Paypal where the user is directed to a Paypal site and then back to your site.
The process is supposed to go:
The user enters their payment information into stripe generated
elements on your page.
Stripe gives your front end javascript a
token that represents the customer's payment data.
You post that information to your server using ajax or even a regular old fashioned form request.
The server side script handling the call uses the asp.net stripe library to send the payment and gets an answer back like the one below.
Save the transaction result to your database as needed.
a. If the stripe resonse includes "status":"succeeded" then you can serve the customer with a new page with the paid receipt.
b. If it failed for some reason you can reload the payment page and use the "failure_message" to tell the customer why their card is no good.
Let the user wait until stripe replies with a message regarding the success or failure of the payment and send him because it only takes a second.
/* SAMPLE RESPONSE FROM STRIPE
{
"id": "ch_1D658SDJ46dzUiasdfsdfaDq",
"object": "charge",
"amount": 2125,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"balance_transaction": "txn_1D658SDJ46dzUilftNXRCz64",
"captured": true,
"created": 1565431460,
"currency": "usd",
"customer": null,
"description": "856 addresses",
"destination": null,
"dispute": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"receipt_email": null,
"receipt_number": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_1D658SDJ46dzUilfalFFraDq/refunds"
},
"review": null,
"shipping": null,
"source": {
"id": "card_1D658RDJ46dzUilfbkLSOIwp",
"object": "card",
"address_city": "test",
"address_country": "US",
"address_line1": "123 test",
"address_line1_check": "pass",
"address_line2": "",
"address_state": null,
"address_zip": "32121",
"address_zip_check": "pass",
"brand": "Visa",
"country": "US",
"customer": null,
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 12,
"exp_year": 2033,
"fingerprint": "fNMgYIntTkOnLVzk",
"funding": "credit",
"last4": "4242",
"metadata": {},
"name": "Test",
"tokenization_method": null
},
"source_transfer": null,
"statement_descriptor": null,
"status": "succeeded",
"transfer_group": null
}

Resources