I'm using Google APIs to get the user's calendar events and contacts.
While fetching the contacts, I get the response in the following manner:-
[
{
'phones': [],
'image_path': '',
'id': 'ID',
'emails': ['email1'],
'name': ABC
},
{
'phones': [],
'image_path': '',
'id': 'ID',
'emails': ['email2'],
'name': DEF
}
]
While fetching the events, I get the follwoing response:-
[
{
'attendees': [{
'organizer': True,
'displayName': 'ABC',
'id': 'Google+ Id',
'responseStatus': 'accepted'
}, {
'self': True,
'displayName': 'DEF',
'id': 'Google+ id',
'responseStatus': 'accepted'
}],
'organizer': {
'displayName': 'ABC',
'id': 'Google+ id'
},
'creator': {
'displayName': 'ABC',
'id': 'Google+ id'
},
},
{
'organizer': {
'self': True,
'displayName': 'DEF',
'email': 'email2'
},
'creator': {
'self': True,
'displayName': 'DEF',
'email': 'email2'
},
}
]
As you can see that while fetching events, (in attendees, organizers, creators) I get Google+ id in some cases and email_ids in other cases. This does not maintain a uniformity in my code.
Since I've fetched the user contacts as well, and I search the contacts via their email_ids. If I don't get an email_id in attendees, organizers, or creators, I won't be able to reference the contact object.
How can I make sure that I get only the email_ids in attendees, not the Google+ id.
According to Google Calendar API docs
Optional query parameters
alwaysIncludeEmail
boolean Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available
(i.e. a generated, non-working value will be provided). The use of
this option is discouraged and should only be used by clients which
cannot handle the absence of an email address value in the mentioned
places. Optional. The default is False.
Anyway it is not encouraged to use it , because sometimes no real email is available.
Work around:
You can use G+ API to fetch user Email through providing his/her email.
email
This scope requests that your app be given access to:
the user's Google account email address. You access the email address by calling people.get, which returns the emails array (or by calling people.getOpenIdConnect, which returns the email property in OIDC-compliant format).
the name of the Google Apps domain, if any, that the user belongs to. The domain name is returned as the domain property from
people.get (or hd property from getOpenIdConnect)
This email scope is equivalent to and replaces the https://www.googleapis.com/auth/userinfo.email scope.
Related
I'm making an async api request with a firebase cloud function to create a campaign within mailchimp for a specific set of users from a list. I read in the documentation that this can be done with tags this way I can build my own structure. I'm building a donation system for a nonprofit and would like the tag to represent the name of a client who is currently being donated to.
Below is my firebase function. I'm stuck at the segment_opts object. I want to define a segment based on whether the list member has a tag equivalent my clients name.
The doc says segment_opts is "An object representing all segmentation options. This object should contain a saved_segment_id to use an existing segment, or you can create a new segment by including both match and conditions options.". I don't have any other segments set up so I figured I'd create a new one that specifies the tags to contain the client's name.
This post helped me get to this point. Stackoverflow post
I now see that condition is supposed to be a Segment Type but in the dropdown I don't see an option for Tags. Here is a link to the documentation reference. Reference
const response = await mailchimp.post('/campaigns', {
type: 'regular',
recipients: {
list_id: functions.config().mailchimp.test,
segment_opts: {
"match": "any",
"conditions": match: 'any',
conditions: [
{
condition_type: 'StaticSegment',
field: 'static_segment',
op: 'static_is',
value: ??? (Int),
},
],
}
},
});
For now I removed segment_opts and will settle on sending campaign to entire list until I figure out how to segment by tags. This version works and creates a campaign within my mailchimp account and from the UI I can see the segment options offered in the documentation but don't see an option to filter by tags
const response = await mailchimp.post('/campaigns', {
type: 'regular',
recipients: {
list_id: functions.config().mailchimp.test,
},
settings: {
subject_line: `${firstName} has been funded!`,
preview_text: `$${goal} has been raised for ${firstName}.`,
title: `${firstName} has been funded`,
from_name: 'Organization name',
reply_to: 'org_email#gmail.com',
},
});
Here is a screenshot of the dropdown options in Mailchimp dashboard.
This is what I have for my campaign segment options. Here I'm checking for two conditions. Is the SITE merge tag = the site variable I pass in, and also does the member belong to the tag/segment called tagName. However, I can't pass a tagName, only a tagId which I lookup beforehand.
'segment_opts':
{
'match': 'all',
'conditions': [
{
'condition_type': 'TextMerge',
'field': 'SITE',
'op': 'is',
'value': site
},
{
'condition_type': 'StaticSegment',
'field': 'static_segment',
'op': 'static_is',
'value': tagId
}
]
}
To get the tagId I use this Python function:
tagId, segments = self.getSegmentIdFromTagName(tagName)
This is the Python code to get the tagId from the tagName, which gets all the Segments/Tags from the system and then looks for the name you pass in:
def getSegmentIdFromTagName(self,reqTagName,segments=None):
audienceId = self.audienceId
reqId = None
if not segments:
segments = self.mcClient.lists.segments.all(list_id=audienceId,get_all=True)
for segment in segments['segments']:
segName = segment['name']
segId = segment['id']
if segName == reqTagName:
reqId = segId
break
return reqId,segments
I have several questions related to google.golang.org/api/chat/v1.
It seems the only available Event object is DeprecatedEvent. So, what is the non-deprecated one to use?
If DeprecatedEvent is indeed the intended one to use, inside the DeprecatedEvent object, there's a User *User field. However, it seems that the User object is different from what I actually got from the responses.
For example:
{
'eventTime': '2019-08-27T06:50:12.391141Z',
'user': {
'name': 'users/112...',
'email': 'iskandar.setiadi#...',
'avatarUrl': 'https://lh3.googleusercontent.com/a-/AAu...',
'displayName': 'Iskandar Setiadi',
'type': 'HUMAN'
},
'type': 'ADDED_TO_SPACE',
'space': {
'name': 'spaces/7ag...',
'type': 'DM'
}
}
In the API, User object only contains displayName, name, and type. It seems that email and avatarUrl are not there. Is v1 outdated or is there any alternatives that I don't know?
Stripe connect accounts are configurable to coalesce payouts in a regular payout schedule, e.g. for monthly payouts in our case. For these monthly payouts we need to explain the account owners which of the transactions on our platform (bookings and refunds in our case) produced the overall amount they receive. We store the stripe charge id (resp. refund id) in the booking (resp refund) objects in our database. Thus the question boils down to:
Given a stripe account id, how can you get the list of stripe charge and refund ids that contributed to the last payout?
I've had an extensive exchange with Stripe's support team and there are several puzzle pieces necessary to get there:
Payouts are scoped by accounts
If you query stripe for a list of payouts, you will only receive the payout objects that you, the platform owner, get from stripe. To get the payout objects of a specific account you can use the normal authentication for the platform, but send the stripe account id as a header. So the code snippet to get the last payout looks like this (I'll use ruby snippets as examples for the rest of the answer):
Stripe::Payout.list({limit: 1}, {stripe_account: 'acct_0000001234567890aBcDeFgH'})
=> #<Stripe::ListObject:0x0123456789ab> JSON: {
"object": "list",
"data": [
{"id":"po_1000001234567890aBcDeFgH",
"object":"payout",
"amount":53102,
"arrival_date":1504000000,
"balance_transaction":"txn_2000001234567890aBcDeFgH",
"created":1504000000,
"currency":"eur",
"description":"STRIPE TRANSFER",
"destination":"ba_3000001234567890aBcDeFgH",
"failure_balance_transaction":null,
"failure_code":null,
"failure_message":null,
"livemode":true,"metadata":{},
"method":"standard",
"source_type":"card",
"statement_descriptor":"[…]",
"status":"paid",
"type":"bank_account"
}
],
"has_more": true,
"url": "/v1/payouts"
}
Having the payout id, we can query the list of balance transactions, scoped to a payout:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
})
Objects viewed as an account are stripped of most information, compared to those viewed as a platform owner
Even though you now have the payout id, the object is still scoped to the account and you cannot retrieve it as platform owner. But viewed as an account, the payout only shows pseudo charge and refund objects like these (notice the second transaction has a py_7000001234567890aBcDeFgH object as a source instead of a regular ch_ charge object):
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
})
=> {
:object => "list",
:data => [
{
:id => "txn_4000001234567890aBcDeFgH",
:object => "balance_transaction",
:amount => -53102,
:available_on => 1504000000,
:created => 1504000000,
:currency => "eur",
:description => "STRIPE TRANSFER",
:fee => 0,
:fee_details => [],
:net => -53102,
:source => "po_5000001234567890aBcDeFgH",
:status => "available",
:type => "payout"
},
{
:id => "txn_6000001234567890aBcDeFgH",
:object => "balance_transaction",
:amount => 513,
:available_on => 1504000000,
:created => 1504000000,
:currency => "eur",
:description => nil,
:fee => 0,
:fee_details => [],
:net => 513,
:source => "py_7000001234567890aBcDeFgH",
:status => "available",
:type => "payment"
}
],
:has_more => true,
:url => "/v1/balance/history"
}
You can let stripe automatically expand objects in the response
As an additional parameter, you can give stripe paths of objects which you want stripe to expand in their response. Thus we can walk from the pseudo objects back to the original charge objects via the transfers:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
expand:['data.source.source_transfer',]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.second.source.source_transfer.source_transaction
=> "ch_8000001234567890aBcDeFgH"
And if you want to process the whole list you need disambiguate between the source.object attribute:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 2,
expand:['data.source.source_transfer',]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.map do |bt|
if bt.source.object == 'charge'
['charge', bt.source.source_transfer.source_transaction]
else
[bt.source.object]
end
end
=> [["payout"], ["charge", "ch_8000001234567890aBcDeFgH"]]
Refunds have no connecting object path back to the original ids
Unfortunately, there is currently no way to get the original re_ objects from the pseudo pyr_ that are returned by the BalanceTransaction list call for refund transactions. The best alternative I've found is to go via the data.source.charge.source_transfer.source_transaction path to get the charge id of the charge on which the refund was issued and use that in combination with the created attribute of the pyr_ to match our database refund object. I'm not sure, though, how stable that method really is. The code to extract that data:
Stripe::BalanceTransaction.all({
payout: 'po_1000001234567890aBcDeFgH',
limit: 100, # max page size, the code to iterate over all pages is TBD
expand: [
'data.source.source_transfer', # For charges
'data.source.charge.source_transfer', # For refunds
]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).data.map do |bt|
res = case bt.source.object
when 'charge'
{
charge_id: bt.source.source_transfer.source_transaction
}
when 'refund'
{
charge_id: bt.source.charge.source_transfer.source_transaction
}
else
{}
end
res.merge(type: bt.source.object, amount: bt.amount, created: bt.created)
end
It is now possible to get the refund ids via a "transfer reversal" object:
Stripe::BalanceTransaction.list({
payout: 'po_1000001234567890aBcDeFgH',
expand: [
'data.source.source_transfer', # For charges
'data.source.transfer_reversal', # For refunds
]
}, {
stripe_account: 'acct_0000001234567890aBcDeFgH'
}).auto_paging_each do |balance_transaction|
case balance_transaction.type
when 'payment'
charge_id = balance_transaction.source.source_transfer.source_transaction
when 'payment_refund'
refund_id = balance_transaction.source.charge.source_transfer.source_transaction
end
end
end
How do I retrieve individual email addresses that are members of a Google Contact Group?
Using the API, I seem to be able to only see which contacts are members of a group - contacts that might have multiple email addresses. However, the Gmail UI lets me create a group with individual email addresses.
To repro:
1) Open https://mail.google.com/mail/u/0/#contacts
2) Create a new contact called John Smith with two email addresses: jsmith#gmail.com and jsmith#work.com
3) Create a new contact group call "my group". Add (only) jsmith#work.com to that group.
4) Now use the Google Contacts v3 API to fetch John Smith's record. It will look like this:
'gd$name': { 'gd$fullName': { '$t': 'John Smith' }, ... },
'gd$email':
[ { address: 'jsmith#gmail.com',
primary: 'true',
rel: 'http://schemas.google.com/g/2005#other' },
{ address: 'jsmith#work.com',
rel: 'http://schemas.google.com/g/2005#work' } ],
'gContact$groupMembershipInfo':
[ { deleted: 'false',
href: 'http://www.google.com/m8/feeds/groups/engtestuser%40mixmax.com/base/6' },
{ deleted: 'false',
href: 'http://www.google.com/m8/feeds/groups/engtestuser%40mixmax.com/base/2a44002d89eb51e3' } ] }
How do I know that only jsmith#work.com was added to "my group" (with id 2a44002d89eb51e3)? Where is the association between email addresses and groups stored?
Entire contacts are added to groups, not just individual emails. You are finding the contact using one of their two email addresses, but the entire contact with both email addresses is added to the group. For this reason it's not possible to make that distinction.
I'm using this Mailchimp 2.0 PHP wrapper:
https://github.com/drewm/mailchimp-api
To send data to my list via the Mailchimp 2.0 API.
I can get email, firstname, and lastname to send successfully from my form to mailchimp.
I set those up as required fields in mailchimp(EMAIL, FNAME, LNAME).
Here is the PHP for that:
$MailChimp = new MailChimp('xxxxxxx');
$result = $MailChimp->call('lists/subscribe', array(
'id' => 'xxxxxx',
//required fields
'email' => array( 'email' => $_POST['email']),
'merge_vars' => array('FNAME' => $_POST['fname'], 'LNAME' => $_POST['lname']),
//mailchimp options
'double_optin' => false,
'update_existing' => true,
'replace_interests' => false
));
But I also have 12 checkboxes for stuff like engine size, type, gas type, color, etc. that are optional.
How can send these to the mailchimp API? I'm hoping someone with experience with Mailchimp API could help out.
Any help would be appreciated.
Thanks!
Here is a clarification to the structure of the groupings that is not in the docs' example. I.e. you supply only the names of the groups that have been selected by the user:
"merge_vars": {
"groupings": [
{
"groups": [
"selection 3",
"selection 7"
],
"id": <group_id>// or "name": <group_name>
}
]
},
In the merge_vars array, define "groupings" which points to an array. This 'groupings' array will then consist of individual arrays that point to a particular grouping of groups. Ex. if you have a grouping of groups titled "gas type" with group options "diesel", "unleaded", etc. this level of the array points to "gas type".
THEN, you define a "groups" array inside of this array to denote membership into the actual subgroups ("diesel", "unleaded").
Here's a code example from the list subscribe MailChimp API 2.0 documentation:
"merge_vars": {
"groupings": [
{
"id": 42,
"name": "example name",
"groups": [
"..."
]
}
]
lists/subscribe: http://apidocs.mailchimp.com/api/2.0/lists/subscribe.php
My personal suggestion: create groups in the web app if you haven't already. Then, use the lists/interest-groupings method to see how the interest groups are formatted and returned to you. This gives you a sense of how to structure it in your own code.
lists/interest-groupings: http://apidocs.mailchimp.com/api/2.0/lists/interest-groupings.php