I'm in the process of integrating the Skype for Business Web SDK in my webapplication.
My question is about status changes (presence).
According to the samples on https://ucwa.skype.com/websdk I am listening to (my) status changes in the following way:
client.personsAndGroupsManager.mePerson.status.changed(function (status) {
console.log("My new status: " + status);
// etc...
});
This works and I can update the UI according to the status change.
There is one exception --> status 'Be right back'.
When the status is changed to 'Be right back' (BeRightBack) externally in a Skype client or in the UI of my application, I'm getting back status 'Away'.
When checking in the Dev Tools of Edge, I see the following:
POST
{"availability":"BeRightBack"}
and
GET
{"availability":"BeRightBack","_links":{"self": {"href":"/ucwa/oauth/v1/applications.... etc }},"rel":"presence"}
Also when I sign in while being logged into Skype with the 'Be right back' status, the SDK returns status 'Away'.
When running the samples on https://ucwa.skype.com/websdk I get the same result.
I change the status to 'Be right back' and the alert shows I'm 'Away'.
Am I missing something here? Is this expected behaviour?
Running into the same issue: if I log into skype for business (the desktop client) and set the user to be right back the value I get from the web sdk is away.
Investigating a little bit I noticed that method is present at sdk.js:44553 (with version 1.2.36)
/**
* Transform status values from UCWA to values known to jCafe.
* #param {string} s - status value from UCWA
*/
function fixStatus(s) {
return Person.Status[s] || {
BeRightBack: Person.Status.Away,
Idle: Person.Status.Away,
IdleOnline: Person.Status.Away,
IdleBusy: Person.Status.Away,
Unknown: Person.Status.Offline
}[s] || Person.Status.Offline;
}
Right above, you can see the declaration of the Status enum is missing the BRB value, I don't understand why though, especially since the SDK support the BRB value according to documentation
var Status;
(function (Status) {
Status.Away = 'Away';
Status.Busy = 'Busy';
Status.DoNotDisturb = 'DoNotDisturb';
Status.Offline = 'Offline';
Status.Online = 'Online';
})(Status = Person.Status || (Person.Status = {}));
To me it looks like it's a status they added after creating the SDK and for retro-compatibility reasons they are doing strange things, hard to confirm without having access to the repository though.
By the way, guys, I realized this; mePerson object has two attribute. One of them is status and the other attribute is activity.
Let me explain difference between two of them. BeRightBack is not a status, it is an activity. The status of BeRightBack is Away and activity is be-right-back. Same as OffWork. If you set the status to OffWork you will see status = Away and activity = off-work . I did not find how I can set activity. If you find anything please let me know.
here is code;
var stasus = 'BeRightBack' ;
window.skypeWebApp.personsAndGroupsManager.mePerson.activity.changed(function (status) {
// alert('Activity Changed to: ' + status);
console.log('Activity Changed to: ' + window.skypeWebApp.personsAndGroupsManager.mePerson.activity);
});
Related
I have been struggling to find a solution for this and it seems that i'm doing something in the wrong way due to my limited knowladge, so here is the breakdown of the problem:
public void RegisterNewUser()
{
FetchRegisterInputValues();
if (CheckRegisterDataIntegrity())
{
_auth.CreateUserWithEmailAndPasswordAsync(_email, _password).ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("CreateUserWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
HandleRegistrationErrors(task.Exception);
return;
}
// Firebase user has been created.
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("Firebase user created successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
else if (!CheckRegisterDataIntegrity())
{
HandleRegistrationErrors(new AggregateException("passwords do not match"));
}
}
above is the Registration function that I got straight from Firebase docs, it's very straightforward
the FetchRegisterInputValues(); function gets the email and passwords, the CheckRegisterDataIntegrity() compares the password with the password conformation in the form, and finally HandleRegistrationErrors(task.Exception); is meant to fire a popup panel to show the error,
this is how HandleRegistrationErrors(task.Exception); looks
private void HandleRegistrationErrors(AggregateException errMsg)
{
print("its here from the errors method " + errMsg.Message);
registerErrorPopup.OpenNotification();
registerErrorPopup.description = errMsg.Message;
}
it's using a UI asset from the asset store, the .OpenNotification(); starts the animation and pops it up, and then im just showing the message.
Now, I got two problems, the first is when there is an error encountered by Firebase and the if (task.IsFaulted) Condition is true, the HandleRegistrationErrors function should be called, right?. well that's exactly what happens, except only the print("it's here from the errors method " + errMsg.Message); line gets called and the rest of the function does not execute, I thought at first that its a problem with asset, but I tried doing it manually (created a native UI with unity and used SetActive() method to start the popUp), but again only print method executed, I think its because of the
CreateUserWithEmailAndPasswordAsync is Asynchronous and I should handle errors accordingly, but I really don't know how to go about it and there is no documentation that I could find.
The second problem is how to get the correct Error Message because of the task.Exception.Message always returns me a "One or more errors occurred". while the task.Exception itself gives the right message but it's not formatted correctly.
The first question is the easiest. To update your code with the minimal amount of effort, just replace ContinueWith with ContinueWithOnMainThread will force logic onto the main thread. Also, you should avoid calling task.Result if task.Exception is non-null as it will just raise the exception (see the related documentation).
For the threading related stuff: I go into much more detail about threading with Firebase and Unity here and you can read about the ContinueWithOnMainThread extension here.
For your second issue, the issue you're running into is that task.Exception is an AggregateException. I typically just attach a debugger and inspect this when debugging (or let Crashlytics analyze it in the field), and my UI state is only concerned about success or failure. If you want to inspect the error, the documentation I linked for AggregateException recommends doing something like:
task.Exception.Handle((e) => Debug.LogError($"Failed because {e}"));
Although I would play with .Flatten() or .GetBaseException() to see if those are easier to deal with.
I hope this helps!
--Patrick
I try to make a push notification for my google assistant app.
I used the sendNotification Code form the google developer site: https://developers.google.com/actions/assistant/updates/notifications
I am coding Java.
Everything is working, expect getting the correct user id.
When I hardcode my user it works, but how do I get the user id in the code?
I tried following code:
Argument arg_userId = request.getArgument(ConstantsKt.ARG_UPDATES_USER_ID);
String userId = request.getUser().getUserId();
--> I get "java.lang.reflect.InvocationTargetException"
String userId = arg_userId.getRawText();
--> same Exception
There are two problems with the approach you're taking to get the notification ID:
The ID attached to the user object is deprecated and probably unavailable.
This wasn't the ID you wanted anyway.
In the response where the user finalizes the notification, that response includes an ID which you should get and store. Since you're using Java, the code might look something like this:
ResponseBuilder responseBuilder = getResponseBuilder(request);
Argument permission = request.getArgument(ConstantsKt.ARG_PERMISSION);
if (permission != null) {
Argument userId = request.getArgument(ConstantsKt.ARG_UPDATES_USER_ID);
// code to save intent and userID in your db
responseBuilder.add("Ok, I'll start alerting you.").endConversation();
} else {
responseBuilder.add("Ok, I won't alert you.");
}
return responseBuilder.build();
I'm using firebase admin sdk in my cloud functions and I'm getting error randomly in some executions when trying to get a user by uid .
let userRecord = await admin.auth().getUser(userId);
The error details are:
{"error":{"code":400,"message":"TOO_MANY_ATTEMPTS_TRY_LATER",
"errors":[{ "message":"TOO_MANY_ATTEMPTS_TRY_LATER",
"domain":"global","reason":"invalid"}]
}
}
My cloud function executes on a real time database write and can be triggered for multiple users. In total I have 4 auth function calls in one execution first is the above one, second call is to again get user by uid or email, third call is generateEmailVerificationLink and the last call is generatePasswordResetLink.
I have checked the rate limits in documentation for auth but there is no mention of rate limit for these operation. Also the error TOO_MANY_ATTEMPTS_TRY_LATER was only mentioned in REST API for sign up with email password.
If this error is due to rate limit what should I change to prevent this error given these 4 calls are necessary for the operation needed on database write?.
EDIT:
I have identified the actual call which is throwing too many attempts error. The calls auth().generateEmailVerificationLink() and auth().generatePasswordResetLink() throw this error when called too many times.
I called these two in loop with 100 iterations and waited for the promises. The first executions finishes without any errors i.e. 200 requests. But starting second execution as soon as the first one ends will throw the error of too many attempts. So I think these two calls have limit. Now I'm trying to reduce these calls and reuse the link information. Other calls like getUserByEmail works fine.
let promises = [];
let auth = admin.auth();
let hrstart = process.hrtime()
for (let i = 0; i < 100; i++) {
promises.push(auth.getUserByEmail("user email"));
promises.push(auth.generateEmailVerificationLink("user email", {url: `https://app.firebaseapp.com/path`}));
promises.push(auth.generatePasswordResetLink("user email", {url: `https://app.firebaseapp.com/path`}));
}
Promise.all(promises)
.then(value => {
let hrend = process.hrtime(hrstart);
console.log(hrend);
// console.log(value)
});
The error was specifically in the operation auth.createEmailLink. This function has following limit: 20QPS/I.P address where QPS is (query per second). This limit can be increased by submitting the use case to Firebase.
I got this information from firebase support after submitting my issue.
Link to my github issue: https://github.com/firebase/firebase-admin-node/issues/458
I was way under 20QPS but was receiving this exception. In fact, it would always throw the TOO_MANY_ATTEMPTS_TRY_LATER exception on the 2nd attempt.
It turned out to be usage of FirebaseAuth.DefaultInstance instead of instantiating a static instance thusly:
In class definition:
private readonly FirebaseApp _firebase;
In class constructor:
_firebase = FirebaseAdmin.FirebaseApp.Create();
In function:
var auth = FirebaseAuth.GetAuth(_firebase);
var actionCodeSettings = new ActionCodeSettings()
{
...
};
var link = await auth.GenerateEmailVerificationLinkAsync(email, actionCodeSettings);
return link;
In addition to the answer mentioned in https://stackoverflow.com/a/54782967/5515861, I want to add another solution if you found this issue while trying to create custom email verification.
Inspired by the response in this GitHub isssue https://github.com/firebase/firebase-admin-node/issues/458#issuecomment-933161448 .
I am also seeing this issue. I have not ran admin.auth().generateEmailVerificationLink in over 24hrs (from anywhere else or any user at all) and called it just now only one time (while deployed in the prod functions environment) and got this 400 TOO_MANY_ATTEMPTS_TRY_LATER error ...
But, the client did also call the Firebase.auth.currentUser.sendEmailVerification() method around same time (obviously different IP).
Could that be the issue?
My solution to this issue is by adding a retry. e.g.
exports.sendWelcomeEmail = functions.runWith({failurePolicy: true}).auth.user().onCreate(async (user) => {
functions.logger.log("Running email...");
const email = user.email;
const displayName = user.displayName;
const link = await auth.generateEmailVerificationLink(email, {
url: 'https://mpj.io',
});
await sendWelcomeEmail(email, displayName, link);
});
The .runWith({failurePolicy: true}) is key.
It s giving you an error because your cloud functions/backend call the generateEmailVerificationLink while at the same time the default behaviour of the Firebase is also doing the same and it is counted as 20QPS. It some weird Google Rate Limit accounting rule. So my solution is just to add a retry.
The Downside is, it is calling twice, so if the call is billable, it might be billable twice.
I have a fairly simple Meteor application.
I tried to send newsletter to about 3000 users in my list and things went wrong. A random set of users got multiple emails (between 41 to 1).
I shut the server down as soon I noticed this behavior. around 1300 emails were sent to 210 users. I am trying to figure out what happened and why.
Here is the code flow:
SendNow (client clode) --> SendNow (server method) --> populateQue (server function) --> processQue(server function) --> sendEmails (server method)
Client side code :
'click .sendNow': function(){
/* code that forms data object */
Meteor.call('sendNow',data);
}
Server code : server/method.js
Meteor.methods({
'sendNow' : function(data){
if(userWithPermission()){
var done = populateQue(data);
if(done)
processQue();
return {'method':'sendNow','status':'ok'}
},
'sendEmails': function(data){
this.unblock();
var result = Mandrill.messages('send', data);// using external library
SentEmails.insert(data);//Save sent emails in a collection
}
});
Function on server : server/utils.js
populateQue = function(data) {
/* code to get all users in to array */
MessageQue.remove();//Remove all documents from the Que
for (var i=0; i<users.length; i++) {
MessageQue.insert({userId: users[i]._id});
}
return true;
}
processQue = function(){
var messageQue = MessageQue.find({}).fetch();
for(i=0; i < messageQue.length; i++){
Meteor.call('sendEmails', data);
MessageQue.remove({_id: messageQue[i]._id});//Remove sent emails from the Que
}
}
My first hunch was MessageQue got messed up as I am removing items while processQue is using it but i was wrong. I am unable to simulate this behavior again after few tests
Test 1 : replaced Mandrill.message('send',data) with Meteor._sleepForMs(1000); - Only one email/person was seen in SentEmails collection.
Test 2 : Put Mandrill in Test mode (had to use different API key) and re ran the code with couple of log statements. - Only one email/person was seen in SentEMails and also in Mandrill's interface.
It's definitely not external library. its somewhere in my code or in the way I understood meteor to work.
Only one thing I noticed is an error occurred while accessing SentEmails collection through another view code. I have a view that displays SentEmails on the client with date as filter.
Here is the error :
Exception from sub sentEmailDocs id 9LTq6mMD4xNcre4YX Error:
Exception while polling query
{
"collectionName":"sent_emails",
"selector":{"date":{"$gt":"2015-07-09T05:00:00.000Z","$lt":"2015-07-11T05:00:00.000Z"}},
"options":{"transform":null,"sort":{"date":-1}}
}:
Runner error: Overflow sort stage buffered data usage of 33565660 bytes exceeds internal limit of 33554432 bytes
Is this the smoking gun? Would this have caused the random behavior?
I have put couple checks to prevent this from happening but I am puzzled on what might have caused and why? I will be happy to provide more information. Thanks in advance to who ever is willing to spend few mins on this.
Shot in the dark here, but the remove method takes an object, otherwise it doesn't do anything. MessageQue.remove() probably didn't clear the queue. You need MessageQue.remove({}). Test the theory by doing an if (MessageQue.find().count() > 0)... after the remove.
If you're set on having a separate collection for the queue, and I'm not saying that's a bad thing, I'd set the _id to be the userId. That way you can't possibly send someone the same message twice.
I got an unexpected behaviour while trying to update all the entries on an anonymous collection (client side). Only one entry is updated instead of all. I expected the following code to return true, it does not:
TEST = new Meteor.Collection(null); // create the anonymous collection client side
TEST.insert({"val":true}); // add a minimal entry
TEST.insert({"val":true}); // add a second minimal entry
TEST.update({}, {"val":false}); // I expect to update all the entries to {"val":false}
TEST.find({"val":true}).count() === 0; // the count gives 1, only the first entry was updated (expected 0)
It's either a bug, or something I did not got about updates… could someone clarify ?
Note: Obviously the code must be copy pasted into the console of a browser running Meteor. (Tried on 0.8.0.1)
Ok got it… I just forgot to use the {multi:true} option… shame on me
TEST = new Meteor.Collection(null);
TEST.insert({"val":true});
TEST.insert({"val":true});
TEST.update({}, {"val":false}, {multi:true});
TEST.find({"val":true}).count() === 0; // works