Joining a standalone VoxImplant conference - voximplant

I have a simple VoxImplant scenario which creates a standalone conference and calls its participants:
require(Modules.Conference);
VoxEngine.addEventListener(AppEvents.Started, () => {
let conf = VoxEngine.createConference();
let { users } = JSON.parse(VoxEngine.customData());
users.forEach((username) => {
let call = VoxEngine.callUser({username: username, callerid: "root", displayName: "Whatever" });
call.addEventListener(CallEvents.Connected, (e) => { VoxEngine.sendMediaBetween(conf, e.call); });
});
});
I use StartScenarios endpoint to initiate the conference.
The calls arrive, users can answer them and hear each other. But what if they decline and decide to join later? I can see callConference methods in SDKs but they require conferenceId, how can I get it? Conference object doesn't seem to have id property, neither do ConferenceParameters.
VoxImplant conference sample has create_conference endpoint which inserts a record into a database and returns primary key but it seems to be unused.

Should use StartConference endpoint instead of StartScenario, it has conference_name parameter. Generate and pass conference name there, and pass the same name to callConference in order to join a running conference.

Related

Show private messages list of multiple users logged in at the same time

I've created a private messaging app where users login and can view their private messages. The problem that I am having is that is only shows the message list of one logged in user at a time. So let's say User A is logged in, it will show the chat list of user A. But then User B logs in, then both User A and User B will see the chat list of User B.
This is my server side call to fetch chats by user id:
Im using express for the backend
io.on('connection', socket => {
socket.on('findAllChatsByUserId', (userId) => {
socket.userId = userId
socket.join(socket.userId)
ChatModel.aggregate([{$match: {$or:[{senderId: userId},{receiver: userId}]}}, {$group: {_id: '$chatId', 'data': {$last: '$$ROOT'}}}]).exec(function(error, data) {
if (error) {
return error
} else {
data.sort(function (a, b) {
return b.data.date - a.data.date;
});
io.to(socket.userId).emit('findAllChatsByUserId', data);
}
})
})
});
And on the client side I do:
I am using VueJs on the FE
mounted () {
this.loading = true
this.socket.emit('findAllChatsByUserId', this.getUserId) // this calls the socket to get the chats for the given user Id
this.loading = false
},
I tried creating rooms by userId to make sure that only the data for a given user ID is passed in but it seems like only one user can use the socket at a time. I thought the rooms would solve this issue for me. Do I have to create a separate socket for each user? If so, how do I do that? I've followed the socket.io private messaging tutorial but they use 2 users talking to each other to explain the problem.
So I ended up solving this by doing:
io.to(socket.id).emit('findAllChatsByUserId', data);
instead of:
io.to(socket.userId).emit('findAllChatsByUserId', data);
So you use the "to" attribute to make sure the data you're sending is going to a particular socket, and you can find your specific socket by just calling socket.id (you don't have to set this, it gets set on its own. And the data will get emitted to whomever is on that specific socket.

RxJs: How to conditionally chain observable of BehaviorSubject?

I've got an observable data service (UserService) that returns the currently logged in user. I followed this tutorial - https://coryrylan.com/blog/angular-observable-data-services, which describes using a BehaviorSubject to return a default currentUser immediately, then emit the real currentUser once it's loaded or altered. The service is basically like this...
private _currentUser: BehaviorSubject<User> = new BehaviorSubject(new User());
public currentUser: Observable<User> = this._currentUser.asObservable();
constructor(private http: Http) {}
loadUser() { // app.component onInit and login component call this
return this.http.get('someapi.com/getcurrentuser')
.map(response => <User>this.extractData(response))
.do(
(user) => {
this.dataStore.currentUser = user;
this._currentUser.next(Object.assign(new User(), this.dataStore).currentUser);
},
(error) => this.handleError(error)
)
.catch(error -> this.handleError(error));
}
I'm having problems whenever a user hits F5 to reload the entire spa. When a consuming component subscribes to the currentUser on the UserService, it immediately receives a default user while the UserService waits for an api call to receive the actual user. The moment that api call finishes, the real user is emitted by UserService and all the subscribers get the real user. The first value emitted by the BehaviorSubject, however, is the default value and it always has an id of "undefined", so we can't make our next api call yet. In fact, when the real user comes through and I CAN make a valid call using the user.id, the chained subscription never happens and I don't get the values out of the response.
I know I'm doing something stupid, but I haven't figured out exactly what yet. I just stumbled across concatMap, but I'm not sure how to use it. While I pursue that, I'd like to know why the below code doesn't work. I particularly want to know why the subscribe never fires, even after the real user comes in, just to help my newbie understanding of Observables.
this.userService.currentUser
.flatMap((user) => {
this.user = user;
// Need to NOT call this if the user does not have an id!!!
this.someOtherService.getSomethingElse(user.id); // user.id is always undefined the first time
})
.subscribe((somethingElse) => {
// This never gets called, even after the real user is emitted by the UserService
// and I see the getSomethingElse() call above get executed with a valid user.id
this.somethingElse = somethingElse;
});
If you want to ignore user instances that do not have an id, use the filter operator:
import 'rxjs/add/operator/filter';
this.userService.currentUser
.filter((user) => Boolean(user.id))
.flatMap((user) => {
this.user = user;
this.someOtherService.getSomethingElse(user.id);
})
.subscribe((somethingElse) => {
this.somethingElse = somethingElse;
});
Regarding "why the subscribe never fires", it's likely due to an error arising from the undefined id. You only pass a next function to subscribe, so any errors will be unhandled. And if an error occurs, the observable will terminate and will unsubscribe any subscribers - as that is how observables behave - so any subsequent users with defined id properties will not be received.

Node.js client for wit.ai calls multiple custom actions

I'm trying to write an example app in wit.ai. I followed the quickstart app using node.js client that is shown at https://wit.ai/docs/quickstart.
The example shown there has only one custom action. But when I try to add a new story and a new action, I see that the context is being shared between the stories. This is causing wrong behaviour(a custom action from another story is being executed).
I cannot find any example with multiple custom actions and stories. Are there any node.js or python examples other than the ones from wit.ai websites?
You need to create a context for each session, and this is a quick example (from https://github.com/wit-ai/node-wit/blob/master/examples/messenger.js):
const findOrCreateSession = (fbid) => {
let sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {
fbid: fbid,
context: { // New context per session id.
_fbid_: fbid
}
}; // set context, _fid_
}
return sessionId;
};
You can find a working example at https://github.com/hunkim/Wit-Facebook.
I suppose wit engine don't store context on their side.
You 'merge' function must merge entities in different ways, depending on your app logic.
But if you story is completed, you need to clear context for next stories.
I added a built-in function clear-context and call this function from wit as action.
Check out my example.
It's not an official api, but you can understand how wit http api works.

Limit Google Sign-In to .edu accounts in Meteor

I'm trying to limit my Google + Sign-In Button to only allow #something.edu accounts to sign in. How would I go about doing this. This is my code so far:
Template.googleLogin.events({
'click #gLogin': function(event) {
Meteor.loginWithGoogle({}, function(err){
if (err) {
throw new Meteor.Error("Google login didn't work!");
}
else {
Router.go('/home')
}
});
}
})
Template.primaryLayout.events({
'click #gLogout': function(event) {
Meteor.logout(function(err){
if (err) {
throw new Meteor.Error("Hmm looks like your logout failed. ");
}
else {
Router.go('/')
}
})
}
})
You can accomplish this using Accounts.config (in the root directory, so it runs on both the client and server)
Accounts.config({ restrictCreationByEmailDomain: 'something.edu' })
If you need something more custom, you can replace something.edu with a method if you need to fine grain your requirement, i.e for any .edu domain:
Accounts.config({ restrictCreationByEmailDomain: function(address) {
return new RegExp('\\.edu$', 'i')).test(address)
}
});
The accounts package allows configuring account creation domain through:
Accounts.config({
restrictCreationByEmailDomain: 'something.edu'
})
But this has some limitations in case of google:
This is only client side and only allows for the login form to get properly styled to represent the domain's logo etc. But it can be very easily overcome by crafting the google oauth signin url by hand
In case you need to configure extra options like allowing multiple domains or a domain and some outside users (perhaps third party contractors or support from a software company etc) this does not work. In case of accounts-google, the package checks if restrictCreationByEmailDomain is a String and if it is instead a function, it just discards it.
Therefore, to be able to properly and securely utilize such functionality, you need to use the official Accounts.validateNewUser callback:
Accounts.validateNewUser(function(newUser) {
var newUserEmail = newUser.services.google.email;
if (!newUserEmail) throw new Meteor.Error(403,'You need a valid email address to sign up.');
if (!checkEmailAgainstAllowed(newUserEmail)) throw new Meteor.Error(403,'You need an accepted organization email address to sign up.');
return true;
});
var checkEmailAgainstAllowed = function(email) {
var allowedDomains = ['something.edu'];
var allowedEmails = ['someone#example.com'];
var domain = email.replace(/.*#/,'').toLowerCase();
return _.contains(allowedEmails, email) || _.contains(allowedDomains, domain);
};
If you want to be extra cautious, you can implement the same for the Accounts.validateLoginAttempt and Accounts.onCreateUser callbacks as well.

Stripe, is it possible to search a customer by their email?

Update: Since around January 2018, it is now possible to search using the email parameter on Stripe. See the accepted answer.
I was wondering if it was possible to search a customer only by their email address when using the Stripe API.
The documentation only indicates searching by:
created,
ending_before,
limit,
starting_after
But not email.
I'd like to avoid having to list over all my customers to find which ones have the same email addresses.
Stripe now allows you to filter customers by email.
https://stripe.com/docs/api#list_customers
Map<String, Object> options = new HashMap<>();
options.put("email", email);
List<Customer> customers = Customer.list(options).getData();
if (customers.size() > 0) {
Customer customer = customers.get(0);
...
This is important to help ensure you don't create duplicate customers. Because you can't put creating a customer in Stripe and the storage of the Stripe customer ID in your system inside a single transaction you need to build in some fail safes that check to see if a particular customer exists before you create a new one. Searching customers by email is important in that regard.
I did this by using the following API request. This was not available in stripe docs.I got this by tracking down their search in the dashboard area using Browser Developer Tools.
url :https://api.stripe.com/v1/search?query="+email+"&prefix=false",
method: GET
headers: {
"authorization": "Bearer Your_seceret Key",
"content-type": "application/x-www-form-urlencoded",
}
Warning This uses an undocumented API that is specific to the dashboard. While it might work today, there is no guarantee it will continue to work in the future.
You need to retrieve and store the Stripe customer ID along with the other customer details in your database. You can then search for the email address in your database and retrieve the customer record from Stripe by using the Stripe customer ID.
UPDATE: Stripe now allows searching via email
https://stripe.com/docs/api/php#list_customers
/**
* Remember that Stripe unfortunately allows multiple customers to have the same email address.
* #see https://stackoverflow.com/a/38492724/470749
*
* #param string $emailAddress
* #return array
*/
public function getCustomersByEmailAddress($emailAddress) {
try {
$matchingCustomers = [];
$lastResult = null;
$hasMoreResults = true;
while ($hasMoreResults) {
$searchResults = \Stripe\Customer::all([
"email" => $emailAddress,
"limit" => 100,
"starting_after" => $lastResult
]);
$hasMoreResults = $searchResults->has_more;
foreach ($searchResults->autoPagingIterator() as $customer) {
$matchingCustomers[] = $customer;
}
$lastResult = end($searchResults->data);
}
return $matchingCustomers;
} catch (\Exception $e) {
Log::error($e);
return [];
}
}
You can't directly search by email.
However, you can hack a little bit to list all users, and look after your email.
Here's my code (PHP) :
$last_customer = NULL;
$email = "EmailYou#AreLooking.for";
while (true) {
$customers = \Stripe\Customer::all(array("limit" => 100, "starting_after" => $last_customer));
foreach ($customers->autoPagingIterator() as $customer) {
if ($customer->email == $email) {
$customerIamLookingFor = $customer;
break 2;
}
}
if (!$customers->has_more) {
break;
}
$last_customer = end($customers->data);
}
You only need to write this line
\Stripe\Customer::all(["email" => "YourDesiredEmail"]);
In NodeJs we can search for our desired customer with their email address like the following:
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
const stripe = require('stripe')(stripeSecretKey);
const findCustomerByEmail = async (email) => {
try {
const customer = await stripe.customers.list( {
email: email,
limit: 1
});
if(customer.data.length !== 0){
return customer.data[0].id;
}
} catch (e) {
return (e);
}
};
The actual call to stripe is using the stripe.customers.list. If the email exists in our stripe account then the returned object will contain an element called data.
Using "list()" and "search()", you can get customers by email with these Python code below:
customers = stripe.Customer.list(
email="example#gmail.com",
)
customer = stripe.Customer.search(
query="email:'example#gmail.com'"
)
You can also limit customers to get with "limit" parameter as shown below:
customers = stripe.Customer.list(
email="example#gmail.com",
limit=3
)
customer = stripe.Customer.search(
query="email:'example#gmail.com'",
limit=3
)
Since you specified that
The documentation only indicate to search by created, ending_before, limit and starting_after, but no "email".
You are right, you can't search using emails.
If you still wish to do that, What you can do instead is to get a list of all the customer and filter on the response you get using email.
For Example, in ruby you can do it as follows:
customers = Stripe::Customer.all
customer_i_need = customers.select do |c|
c.email == "foo#bar.com"
end
PS: Stripe can have multiple customers associated with one email address.
Please bear in mind when using Stripe API that it is case sensitive email (which is a bit stupid). Hopefully they change this.
Stripe API does not supports any search-by-email feature. They have this search in their dashboard but not released any to API; from the architectural concept it seems that there is no possibility or plan from stripe to include this in API; every object in their API is retrievable only by that specific objects id given by stripe while its created. May be, they have kept it as a scope for third party application developers involvement!!
So, the obvious solution is to store the customers in your own database that you want to be searchable in future - as Simeon Visser has said above
btw, for a workaround, if you already have used the stripe API a lot and there are many customer data which you now need to be searchable - the only way is to go thru the 'List all customers' functionality of API & build the database for your own purpose; ofcourse, you've to use pagination shortcut to iterate thru the whole list for doing so.
$customers = \Stripe\Customer::all(array("limit" => 3));
foreach ($customers->autoPagingIterator() as $customer) {
// Do something with $customer
}
You can try this. It worked for me. Below code will return empty list if not found data matching with email.
$stripe = new \Stripe\StripeClient("YOUR_STRIPE_SECRET");
$customers = $stripe->customers->all(['email'=>'jane.doe#example.com',
'limit' => 15,
]);
Stripe Search API Beta now is available
youtube link
Here is The Async- Await Way This Method can Be Used For All Third Party Hits with Nodejs Particularly
const configuration = {
headers: {
"authorization": `Bearer ${Your stripe test key}`,
"content-type": "application/x-www-form-urlencoded",
}
};
const requestUrl = `https://api.stripe.com/v1/search?
query=${'email you are to use'} &prefix=false`
const emailPayment = await axios.get(requestUrl,
configuration)
Axiom is Npm for creating http requests... very cool and dead simple
Stripe allows the ability to have more than once customer with the same email. That said, if you wanted to, you can pass a filters hash param to the list method to return a single customer or array of customers.
Stripe::Customer.list(email: user.email).data
The above will return an array of Stripe::Customer instances with the email