I have been trying to work with https://github.com/cordova-sms/cordova-sms-plugin
For Android, it works as expected. Unfortunately, I can't capture the activity for ios and windows. It opens the Messages app (as we know) but it doesn't wait for the Messages response whether it has been sent, canceled, or failed.
It always response success. I am already using async (work on Android).
That's because I can't use the success and error parameters.
It only accepts 3 parameters:
sms.send(number, message, options);
It doesn't accept 5 parameters:
sms.send(number, message, options, success, error);
Error message is:
[ts] Expected 2-3 arguments, but got 5.
(method) SMS.send(phoneNumber: string | string[], message: string, options?: SmsOptions): Promise<any>
Has anyone able to capture Canceled SMS with this or other plugins?
P.S. I tried using other plugins but it can't even send.
According to your error-message (and your tags) you are using the cordova-plugin with ionic-native. In this case you do not need to pass a success/error callback because the ionic-team built a nice wrapper for it so it supports promise callbacks (docs for the .send() method).
To use it you have to modify your code as follows:
sms.send(number, message, options).then(
success => {
console.log(success);
},
error => {
console.log(error);
},
);
Please be aware that the callbacks only work correctly on iOS for this plugin. On the Android platform it will always return success immediately because of limitations of the operating system. For more see this FAQ on their github page.
0
Google changed the policy regarding to SMS access, so the direct reading of incoming SMS is no longer allowed and the associated permissions will be removed (SMS_READ).
Now, it is necessary to use the Android SMS Retriever API your SMS message needs to comply a specific format in order to be intercepted by your app.
In Cordova use this plugin to easily read incoming SMS:
cordova plugin add cordova-plugin-android-sms-retriever Github: https://github.com/diegosiao/cordova-plugin-android-sms-retriever
Related
I'm trying to set a webhook callback that will be called by Twilio Studio at every stage of a posted message journey and send the activity status back to the webhook I set.
Studio Create Method (What Im currently using)
client.studio \
.v2 \
.flows(TwilioConstants.TWILIOFLOW_PROXY) \
.executions \
.create(
to=f'whatsapp:{user_number}',
from_=f'whatsapp:{TwilioConstants.BASENUMBER}',
parameters={
'name': data.customer.name,
'user_email': data.customer.email}
)
the create method will only accept 3 arguments
create(self, to, from_, parameters=values.unset):
However the messages create method accepts many arguments including the status_callback
client.messages.create
Can anyone tell me how I can get real-time individual WhatsApp message detailed updates sent to a webhook I set myself?
I have tried setting the webhook in:
WhatsApp Sender -> Status callback URL
but that appears to only send very granular data back and only when the user interacts with the message (to be fair I've not let the message timeout yet but possibly this would also be triggered if this happened or an error was encountered)
What I would like is more detailed information being posted back to my webhook such as found in Monitor -> Messaging -> Message Details
Any help with this one would be very much appreciated.
You are right; there is no status callback available in the Studio API. However, you can invoke your "own status callback" by using the Send HTTP Request widget within Studio.
You can either send all the needed parameters in that request or just the execution SID and then use the Execution API to fetch the details about the current step.
My app is implemented in React Native and is using Firebase for notifications. We have webhooks on the web app that are triggered on certain events. Those webhooks then send a request to the respective Firebase Cloud Function. With information from the request's header they get the topic they must send the notification to; from the request's body it gathers the information to be sent as data to the device. They are sent as data notifications only, the handler (using the React Native FCM API) then shows a local notification already translated with i18n using the react-native-push-notifications package. Everything works fine until I hit the weird situation described below.
Everything was doing alright both on Android and iOS until I launched the app on Test Flight for internal testing and then it stopped working after some time on iOS. Eventually, I noticed that when more than one iPhone subscribed to the same topic it eventually lead to inconsistencies in the delivery of the data notifications. The first iPhone to subscribe to the topic usually worked, the others didn't, the first one most of the time eventually stopped working as well or sometimes it just kept working while the others still didn't. I used the Firebase Console to send some test notification to the subscribed devices and it they actually received it. I then changed my Cloud Functions' code to avoid sending any data and just send some example body and title and it turns out the problem was here. As soon as I send something through the data field in the admin.messaging.Message object to be sent as argument to the admin.messaging().send method, they aren't received by the iPhone devices subscribed to the topic (or it's received by one or two max, the first ones to currently subscribe to the topic. But they usually stop receiving them after a while as well).
This is really really weird and being so inconsistent makes it practically impossible to debug with my current knowledge. Some things to keep in mind:
All Android devices still receive the notifications without a problem
I've watched the iPhone's console through Xcode to see if there was some error when processing the notification, in case they were actually getting the notification but they it failed before it was shown to the user. But nothing is logged by the SpringBoard process, making me conclude the notifications aren't actually getting to the device
I've manually sent notifications with cURL to APNs (with this guide). They were received fine
All notifications without data, regardless of the iOS specific apn headers, payload, etc, are received
What can be the cause of the problem? Or there's something in my code causing this strange behavior (which I doubt, since it works fine on Android and works fine in iOS as well on specific scenarios), there's some type of bug on Firebase's side causing some notifications to not be sent or, finally, there's some error on Apple's APNs side causing this. Highly doubt the last one, if the fault lays on any exterior factor it probably should be on Firebase's handling of topics.
Really would appreciate some help. Thanks in advance. Sorry If I didn't gave enough information, I actually never had the need to do a question on Stack Overflow. I'll leave below an example of a cloud function as well.
exports.orderPending = functions
.region("europe-west2")
.https.onRequest((req, res) => {
if (req.method == "POST") {
res.status("200").send("Webhook successful");
const topic = req.headers["code"];
const message = {
data: {
id: JSON.stringify(req.body.order.id),
event: "orderPending",
order: JSON.stringify(req.body.order),
},
topic,
android: {
priority: "high",
},
apns: {
payload: {
aps: {
contentAvailable: true,
},
},
headers: {
"apns-push-type": "background",
"apns-priority": "5",
"apns-topic": "APP BUNDLE ID HERE",
},
},
};
functions.logger.log(req.body.order.id);
functions.logger.log(topic);
admin.messaging().send(message)
.then((response) => {
console.log("Successfully sent message: ", response);
})
.catch((error) => {
console.log("Error sending message: ", error);
});
}
});
Turns out the data payload was exceeding the 4KB APNs limit but since when you send by topic it doesn't show any errors at all I had no way of knowing. So yeah, add this to your checklist
I'm working on embedding a soft phone into a web page that will go into Odoo (web based ERP system). It will allow inbound and outbound calls for employees.
The token expires every hour. So this means the user will have to refresh the page every hour. I could do an http refresh but if the user is on a call when it does the refresh it will knock them off the call.
How do we get around this so we can build a fully working dialer?
Twilio evangelist here.
I'd suggest using JavaScript to do an asynchronous HTTP request to get a new token from your server and then updating the instance of client with it.
Hope that helps.
Another Twilio evangelist here!
You can actually listen for the offline event on the Twilio.Device object. From the documentation:
.offline( handler(device) )
Register a handler function to be called when the offline event is
fired. This is triggered when the connection to Twilio drops or the
device's capability token is invalid/expired. In either of these
scenarios, the device cannot receive incoming connections or make
outgoing connections. If the token expires during an active connection
the offline event handler will be called, but the connection will not
be terminated. In this situation you will have to call
Twilio.Device.setup() with a valid token before attempting or
receiving the next connection.
So you want something like:
Twilio.Device.offline(function(device) {
fetchTokenFromServer(function(token) {
device.setup(token);
});
});
where fetchTokenFromServer makes the HTTP request that Devin suggested in his answer.
Let me know if this helps.
I just ran into this issue so hopefully my solution can help you and others.
I was using twilio.js v1.3 and tried implementing my offline callback like #philnash recommended, but kept getting the error device.setup is not a function. I then tried using Twilio.Device.setup(newToken) and was able to get the capability token refreshed but also ended up getting a new error: Cannot read property 'setToken' of undefined.
I ended up having to use twilio.js v1.4 to make the error go away. My working solution looks like this:
Twilio.Device.offline(function(device) {
$.ajax(urlToGetNewToken, type: 'get').done(function(newToken) {
Twilio.Device.setup(newToken)
})
})
TLDR: Is there some way I can use a callback or get a return value from react to my native code (iOS)? Or can I use a set of locks to enforce ordering for eventdispatcher and eventemitter listeners to enforce an ordering?
More Information:
So I'm using the event dispatcher in my iOS code (self.bridge.eventDispatcher) and that's working great. I'm able to send events with information to my react code.
However, I noticed that this works asynchronously. I currently use this because if I require information on my iOS side, I send a ping to my react side requesting this information. I then lock upon the request and wait for my react code to use NativeModules and invoke an iOS method where I get the requested data.
Basically, enforcing a synchronous pattern feels a little dangerous because I'm not sure whether bridging methods can fail. For example, I can send an event to react and then lock. If my react side does not get it, or fails to send a notification to the iOS side, then I will never unlock and then will have deadlock. So to this, I have two questions. Is bridging reliable enough to avoid deadlocking through this method? Or is there a better way to accomplish the same result and request information from my react side from my iOS code?
Awesome so I got it, turns out there's a structure called RCTResponseSenderBlock. I made another
iOS method:
-(void)tmpMethod:(RCTResponseSenderBlock)callback{
[self.bridge.eventDispatcher sendAppEventWithName:#"channel" body:#{#"Block":callback}
] ;
}
javascriptReciever:
EventEmitter.addListener("channel", async event => {
console.log(event)
event.Block(["Hello There"]);
return;
});
ios Method Invocation:
[self tmpMethod:^(NSArray* response){
NSLog((NSString*)[response objectAtIndex:0]); //prints Hello There
}];
UPDATE
Turns out when I try to do the same for Android, I can't. The Android platform uses WriteableMap or WriteableArray to send events using the following method:
private void sendEvent(ReactContext reactContext,
String eventName,
#Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
WriteableMap and WriteableArray both do not accept objects such as callbacks which made it not possible to request information from the Javascript side. I also attempted to pass in a Promise as opposed to a WriteableMap or WriteableArray and that threw an error. To communicate asynchronously in a synchronous context for android,
I will have to send an event from native to javascript
lock twice to prevent further execution in side native
on my javascript side invoke a method on my native side once viewing the request with the requested data
unlock within the native method invoked by javascript
resume execution since the program has been unlocked
unlock once further after handling whatever needed to be handled synchronously. (Comment if you want my code implementation)
EDIT AGAIN:
The above flow didn't work. I don't have any control over threads in Android since ReactNative always uses the main thread. So if I end up locking the main thread then react-native cannot enter another method to unlock, thus I have deadlock. So not possible to enforce synchronous exchange of data with android.
We use Twilio SDK in our iOS app. It works fine but sometimes didStopListeningForIncomingConnections callback is called with error=31000 ("General error"). After that, the device turns to a strange state: it seems to be online but it's impossible to call it. And it shows "unconnected" state on the device.
So the questions are:
1. What does this 31000 error means?
2. What should we do in such a case? How to reconnect device to Twilio?
Megan from Twilio here.
You can see what an error for Twilio Client means here: https://www.twilio.com/docs/api/client/errors
However, 31000 is a rather vague and less than ideal error message as you describe. In this case, it is likely that the Twilio capability token has probably expired while the application is in the background, and if you merely call the listen method whenever they are receiving the 31000 generic error, it might cause the client SDK to result in a error-retry loop and crash the application eventually.
At the time of your writing with TwilioClient iOS SDK v1.2.5, it is suggested to use the following sample code in your did-stop-listening callback:
- (void)device:(TCDevice*)device didStopListeningForIncomingConnections:(NSError*)error {
if ( [self checkCapabilityTokenStillValid] ) {
// if the token has not yet expired, use the `listen` method of `TCDevice` to resume the listening state
[self.device listen];
}
else {
// restart all over by requesting a new capability token and initialize/update the `TCDevice` instance again
[self login];
}
}
The TwilioClient iOS SDK takes care of dispatching the listen and updateCapabilitiyToken: methods to the current thread for execution, therefore it's safe to call them directly in the didStopListeningForIncomingConnections. The did-stop-listening delegate method is always triggered with dispatch_get_main_queue() of Grand Central Dispatch.
Hope this may help others if they run into the same generic error.
This may or may not be the issue, we have encountered 31000 errors two times in our development and both were a result of generating the JWT on our server api. To be clear the error was a 31000 on the client, but the reason for this was in the construction of the JWT, and the params we wanted twilio to send back to our application.
When passing in an object to allow_client_outgoing or allow_client_incoming the twilio sdk concats this all in their scope attribute in their JWT. It added it to the scope:client:outgoing?appSid= which looks like a query string. That means it has a size limit of 2048. So exceeding this length generates a 31000 error.
In addition adding the objects doesn't seem to always implicitly serialize the object correctly, it introduces characters that can generate errors in their corresponding mobile sdks (but not their web sdk ... weird) so we took care of this by explicitly serializing objects to JSON before they are inserted into the JWT.
I hope both of these examples help you track down the issue.