giobonvi / googlecontactseventsnotifier Goto Github PK
View Code? Open in Web Editor NEWReceive automatic email notifications before your Google Contacts birthday and other events!
License: MIT License
Receive automatic email notifications before your Google Contacts birthday and other events!
License: MIT License
This happens at run-test
Hi guys,
Just wondering what/where I have to insert in order to add a line of info in the notification emails that would be pulled from a custom field in Google Contacts?
Bit of context: I'm setting up notifications for staff anniversaries and we give out a bottle of Red/White/Beers/Rose - I'd like to add a custom field in Google Contacts that specifies this, and for the email notification to show it as well.
Thanks in advance!
S
I have had this idea some time ago, I have tried fixing some rough edges, but there is still something to work out.
My idea would be to deploy a google script (as a web app) which the iser could use to configure GCEN.
This is a rough draft of the concept:
This would mean:
code.gs
into smaller, more manageable filesMoved here the message @lmatias posted in #2.
Hi GioBonvi
I have your ode running and I thanks for it.
I personalized your code with translation where you indicate.I noticed that in the code you refer to age notification but after adding the age variable to the line
var body = bodyPrefix + bodyBuilder.join('') + bodySuffix + age;
I get the error "ReferenceError: "age" not defined. (line 157, file "Código")"Is it possible to do what I want or not?
@lmatias Please open up a new issue instead of jumping on another one which does not concern your problem.
When notification.eventTypes
is set to ignore a certain type of event (e.g. ANNNIVERSARY
) and a user has an ANNIVERSARY event on a certain day the notificiation for that day will not contain the event in teh text, but will contain the name of the contact in the subject.
There are some other cases where similar problems happen related to teh new "blacklisting" option.
I wrote a patch which fixes this, I'llopen a PR once I've tested it some more. I'm leaving this here so that if anyone else notices this problem noone wastes time working on a fix which already exists.
Maybe some users will prefer having all label-types listed in their notifications (e.g. Work fax: w, Main phone: x - y, Garden shed morse-code device: z
- instead of all being under Other phone: w - x - y - z
), so we could add a verbose
(or similar) variable to the settings (default to false
) which doesn't group number/email labels into "other" at all, but still groups the numbers/emails within each label. Most of that functionality can be reinstated from previous commits which removed it when in favour of "other", but wrapped in a conditional. Just an idea, so if you disagree feel free to close this.
According to the Google documentation it is possible to sync latest commits to the scripts on Google Drive via the REST interface, so it would be nice to document that (at least for maintainer/collaborators-use) so we can avoid the whole copy-paste-update-vars tango whenever we want to test things, and users who want to do it that way can avoid the same tango whenever they want to sync an updated version from master. What would be even nicer would be to add a git-hook (which the user must copy into place if they want that, so other users don't get scared by automatic-background activity happening when they pull from github) with the following logic:
if [master-has-changed] (or if [development-has-changed] for us) {
curl -X PUT --upload-file "{code-customized.gs,unit-tests.gs}" http://blahblahblah
}
or for that matter the hook could be a nodejs script like what they show at the nodejs google drive api quickstart - but that would require having nodejs installed (maybe it would be better for the hook to be in a more commonly-installed language like perl or python... of course using curl-invocations in a shell is probably the least dependency-heavy - for non-windows people anyway)
Hey everyone!
Now that #24 has been succesfully been closed the new release is almost ready to be deployed: as @rowanthorpe pointed out in #22, however, it could be a good idea to change the name of the project as the script does not manage just birthdays anymore.
@rowanthorpe proposed GoogleContactsEventsNotifier
and I quite like it, but before proceeding I wanted to know if anyone else had any idea for a name:
Thank you again for the collaboration, everyone!
I am trying to implement what we discuessd in #87 and got stuck into a decision loop which I need some help to exit from.
calendarId
setting overwrites whichever setting you have chosen in the Google Calendar setting page (e.g. if your birthday calendar has "Contacts only" but you use addressbook#...
in the script you will actually receive notifications about your google plus contacts)#[email protected]
and addressbook#[email protected]
@group.v.calendar.google.com
it will match the holyday calendar as well (and possibly other calendars I am not aware of)If it was for me I would do this:
calendarId
with eventSource
, which has these valid values: '', 'CONTACTS_ONLY', 'CONTACTS_AND_GPLUS'
. The default value would be ''
eventSource
is empty -> the script defaults to the value set in Google Calendar.#[email protected]
and addressbook#[email protected]
. Whichever one is in the list will be used (this would be the "heuristic" method).eventSource
is 'CONTACTS_ONLY'
or 'CONTACTS_AND_GPLUS'
: the correct id is built and used by the scriptBut I'd like to hear your opinion on this.
This is a brilliant idea and I'd like to use this script.
Sorry to bother you but I simply do not fully understand the instructions.
What am I supposed to do with the "Mandatory customization"?
It says "variables that you must initialize correctly" but I do not understand what that means. What exactly should I do?
https://github.com/GioBonvi/GoogleContactsEventsNotifier/blob/master/docs/install-and-setup.md#mandatory-customization
What action or series of actions is the cause of the issue?
What should happen?
What happens instead?
version: 'w.x.y-z',
Try to describe the problem in the most complete way. You can add images,
error messages, hypothesis and observations regarding the problem here.
If you think you know what causes the problem or if you know a solution for it
write it here.
I think the current logo could really benefit from a redesign: if anyone wants to give it a try please do so! It's not necessary to open a PR for every proposal, just link the image in an answer to this issue.
Problems which should be solved:
Guidelines:
Thank you in advance to anyone who wants to give a shot at this!
A user who did not want to register on GitHub just to report a single issue sent me this bug report via email:
When maxEmailsCount: 0
still the "other email" is appended to the name. I quess same happens to
the "other" phone numbers.
I have identified the issue: the script actually "counts" the email addresses only while extracting them from a Google Contact. This means that the email in the "raw event" (which is classified as OTHER_EMAIL
) is not included in the count and is therefore displayed even when maxEmailsCount
is set to 0
.
I'm already working on a quick fix for this moving the "email counting and filtering" process from the data collection phase to the getLines()
function, so that all the email addresses are collected, but only some are displayed, as it should be.
Hi:
I check your code and granted all permissions. Didn't received any notification.
Any clue?
Thanks
Maybe I set too many reminders : anticipateDays: [0, 1, 7], and I have a lot of contacts with birthdays. But I don't know for sure what is causing this.
The script should not run longer than the max time allowed by google app scripts !
On some days when an email reminder is sent to me, it is also followed by an email from [email protected] saying basically :
Start | Function | Error Message | Trigger | End |
---|---|---|---|---|
3/16/19 10:19 AM | normal | Exceeded maximum execution time | time-based | 3/16/19 10:26 AM |
version: '4.1.0',
repoName: 'GioBonvi/GoogleContactsEventsNotifier',
gitHubBranch: 'development'
The script doesn't use google +, and the config code will be below.
The exact mail from google app scripts is :
Subject : Summary of failures for Google Apps Script: Contact birthdays custom mail notifications
Your script, Contact birthdays custom mail notifications, has recently failed to finish successfully. A summary of the failure(s) is shown below. To configure the triggers for this script, or change your setting for receiving future failure notifications, click here.
Start | Function | Error Message | Trigger | End |
---|---|---|---|---|
3/16/19 10:19 AM | normal | Exceeded maximum execution time | time-based | 3/16/19 10:26 AM |
Sincerely, Google Apps Script Need help? Visit the Google Apps Script documentation. Please do not reply to this message. (c) 2019 Google
Today, the email I received before the notice of failure was a notice for one contact, which had five email addresses and three phone numbers.
I don't exactly know what the problem is since I do receive a notification email for the birthdays, before I get the notification that the script took too long to run. I will leave the code of my setup below.
I have read somewhere that the max run time for google app scripts is 6 minutes, and the only solution is having a script run partially while keeping track of time, then stop before 6 minutes saving the current state somewhere (ex in a spreadsheet), then start again reading the last state etc...
var settings = {
user: {
googleEmail: '[email protected]',
notificationEmail: '[email protected]',
eventSource: 'CONTACTS_ONLY',
emailSenderName: 'Contacts Events Notifications',
lang: 'en',
accessGooglePlus: false
},
notifications: {
hour: 10,
timeZone: 'Europe/Paris',
anticipateDays: [0, 1, 7],
eventTypes: {
BIRTHDAY: true,
ANNIVERSARY: false,
CUSTOM: false
},
maxEmailsCount: -1,
maxPhonesCount: -1,
indentSize: 4,
compactGrouping: true
},
debug: {
log: {
filterLevel: 'INFO',
sendTrigger: 'ERROR'
},
testDate: new Date('2017/08/01 06:00:00')
},
developer: {
version: '4.1.0',
repoName: 'GioBonvi/GoogleContactsEventsNotifier',
gitHubBranch: 'development'
}
};
Hi,
When I try to grant the permissions (run -> run function -> notifStart), it says "This app isn't verified". I know how to work around this, but I thought I'd let you know – you might want to say something about that in the README
-Sergi
Sadly it seems that noone else had this issue before (open/closed issues)...
I cross-checked the set-up and it seems that I have a strange case with the script trying to access the Calendar (yeah it feels like deja-vu)
What action or series of actions is the cause of the issue?
Receiving a notification
Error: Contacts events calendar not found! Please follow the instructions (step "Enable the calendar"). (line 551, file "Code")
When script in being edited : menu Resources -> Advanced Google Services
Calendar API (v3) in enabled
Going to Google API Console shows "Google Calendar API" enabled, with no activity.
Entering the Google Calendar API settings, the drop-down "all API identifiers" shows the "oauth" script URL enabled under the title "Apps Script" type "ID for Web App" with a callback URL of https://script.google.com/oauthcallback
BUT there is a banner on top of the Calendar API page that says "to use this API you need to create identifiers" and the drop-down documentation says it needs an API key...
I tried to create an API key and enable it for the Calendar API, but it doesn't seem I can have the script use it...
I tried to enable the Contacts API for a comparison, and it takes the oauth key by default (like the Calendar API) but doesn't display the "you need an API key" banner. I disabled it afterwards because it doesn't seem to be required to run the script.
Thanks for any help !!
install-and-setup.md mentions "Google API Console" but this is now called "Google Cloud Platform API Dashboard". you also need to click the "Library" link before the "Google Calendar API" search works.
it is also unclear, to me, if I need to create credentials in the Google Calendar API as the text says "To use this API, you may need credentials. Click 'Create credentials' to get started."
What action or series of actions is the cause of the issue?
What should happen?
I receive an email.
What happens instead?
No email no error.
When I copied the newest development
branch version of code.gs
into the google script editor (and commented out the entire unit-tests.gs
file), and ensured I had accessGooglePlus
set to false
, and tried to run notifStatus()
, it asked me to authorize the script (like what happened on first-run, after activating the API). At first I thought it was the Google+ handling triggering a new request for authorization despite me having not enabled the Google+ API, but after much experimenting I saw that had nothing to do with it. It seems for some reason (maybe Google triggers a renew-auth request every X months?) it wants me to reauthorize its use of Calendar API. The problem is that, in addition to the permissions you mention in the README, it is now also asking for this:
Publish this application as a web app or a service that may share your data
which the info-box says the following for:
Convert this application into a web app or service
Share ongoing access to your account (as needed to run this service) with others on the web
I can't give any hint how to specifically reproduce this (as I had to do nothing for it to happen to me), but perhaps revoking the script's authorization, disabling the API, then re-enabling & reauthorizing may spring that request back up.
What action or series of actions is the cause of the issue?
I expect the real age to be displayed in the email.
I see age as "-1871", which obviously is wrong.
Google Contacts Events Notifier (Version 5.0.1)
Birthday of the contact for testing purposes: 8. April 1991.
not applicable.
The line 251 right now looks like that: if (this.gplusProfileId === null && eventData['goo.contactsProfileId']) {
.
However everytime it's run this.gplusProfileId
is undefined
in my case, not null
and the line 252 is not called this.getInfoFromGPlus(eventData['goo.contactsProfileId']);
.
I've done a quick workaround for it in 45aca7b, but I know it has to be done differently.
I have also question about G+ API. Is there info about activating it in the instruction of setting up the script? I didn't know I have to manually turn it on :/
To @lboullo0, @muzavan, @DrKrakower, @cezarylaksa, @baatochan, @rowanthorpe and anyone else who wants to help with the translations.
I have just pushed some commits introducing two new features (default profile images and notifications for outdated scripts) and the latter introduced tow new strings in the translation table which have to be updated.
The code can be found in the new-features
branch: if you want to contribute you can submit a Pull Request on that branch (not on master
) with the updated translation.
You'll find which strings are missing and which have to be modified directly in the code.gs
file.
Example
Before (current state of the code):
'it': {
'Age': 'Età',
'Years': 'Anni',
'Events': 'Eventi',
'Birthdays today': 'Compleanni oggi',
'Birthdays tomorrow': 'Compleanni domani',
'Birthdays in {0} days': 'Compleanni fra {0} giorni',
'Anniversaries today': 'Anniversari oggi',
'Anniversaries tomorrow': 'Anniversari domani',
'Anniversaries in {0} days': 'Anniversari fra {0} giorni',
'Custom events today': 'Eventi personalizzati oggi',
'Custom events tomorrow': 'Eventi personalizzati domani',
'Custom events in {0} days': 'Eventi personalizzati fra {0} giorni',
'Hey! Don\'t forget these events': 'Hey! Non dimenticare questi eventi',
'version': 'versione',
'by': 'by',
'dd-MM-yyyy': 'dd-MM-yyyy',
'Mobile phone': 'Cellulare',
'Work phone': 'Telefono di lavoro',
'Home phone': 'Telefono di casa',
'Main phone': 'Telefono principale',
// TODO: 'It looks like you are using an outdated version of this script': '',
// TODO: 'You can find the latest one here': '',
}
After (fully translated code):
'it': {
'Age': 'Età',
'Years': 'Anni',
'Events': 'Eventi',
'Birthdays today': 'Compleanni oggi',
'Birthdays tomorrow': 'Compleanni domani',
'Birthdays in {0} days': 'Compleanni fra {0} giorni',
'Anniversaries today': 'Anniversari oggi',
'Anniversaries tomorrow': 'Anniversari domani',
'Anniversaries in {0} days': 'Anniversari fra {0} giorni',
'Custom events today': 'Eventi personalizzati oggi',
'Custom events tomorrow': 'Eventi personalizzati domani',
'Custom events in {0} days': 'Eventi personalizzati fra {0} giorni',
'Hey! Don\'t forget these events': 'Hey! Non dimenticare questi eventi',
'version': 'versione',
'by': 'by',
'dd-MM-yyyy': 'dd-MM-yyyy',
'Mobile phone': 'Cellulare',
'Work phone': 'Telefono di lavoro',
'Home phone': 'Telefono di casa',
'Main phone': 'Telefono principale',
'It looks like you are using an outdated version of this script': 'Sembra che tu stia usando una vecchia versione di questo script.',
'You can find the latest one here': 'Puoi trovare l\'ultima qui',
}
As you can probably see the whole sentence is 'It looks like you are using an outdated version of this script. You can find the latest one here', but it was spliced into two because:
Thanks to everyone and sorry for bothering you: feel absolutely free to contribute or not to; I am paging you because you've all been so nice and available in the past 😉.
What action or series of actions is the cause of the issue?
What should happen?
Defaults should not get touched by this script
When I had this script enabled the default notification settings get wiped when I create a new calendar event.
What happens instead?
var version = 'x.x.x';
);Try to describe the problem in the most complete way you can add images,
error messages, hypothesis and observations regarding the problem here.
If you think you know what causes the problem or if you know a solution for it
write it here.
I am hoping (but now doubting) it is possible to also include other events which the Google "Birthdays" calendar displays, like anniversaries and custom events (and indicate them as such, not as "birthdays"). In the case of custom events the name of the event-type could be retrieved and injected, too.
So far I've not found any useful documentation, and from reading the script's code and testing it against some anniversaries it seems even though the calendar displays the anniversaries, etc the API seems to only provide info about birthdays. If that turns out to be the case then this issue should be closed here and instead reported at Google. I am opening this issue anyway, in case someone knows or found something which I didn't. It would be very helpful to also get notifications for these things if it is at all possible.
Hi, I copied your code but got this error when i Ran the script "checkBirthdays"
ReferenceError: "Calendar" is not defined. (line 237, file "Code")
the line is:
birthdays = Calendar.Events.list(calendarId, optionalArgs).items;
I also received an email about the error.
FYI, I deleted all the languages you had in there (didn't think it was necessary for it to be in there for me) but don't think this caused the issue? This is how the language section looks like in my code:
var i18n = {
// For all languages, if a translation is not present the untranslated string
// is returned, so just leave out translations which are the same as the English.
// An entry for 'en' marks it as a valid lang config-option, but leave it empty
// to just return unaltered phrases.
'en': {},
/* To add a language:
'[lang-code]': {
'[first phrase]': '[translation here]',
'[second phrase]': '[translation here]',
...
}
*/
};
Hey,
first of all thank you very much for finding a solution to a problem that has been nagging me for months now. I was extremly happy when I found your script, alas, I can't get it to work.
I followed your instructions from here to the letter, but unfortunately received the following mail a couple of hours later:
Don't really know what to do with this and could use some help. In my file, the lines around line 307 are as follows:
In case you need any more information, please let me know. Thank you!
Before reporting a new issue please double check that:
If this issue is not about a bug or a problem you can delete all of this and write whatever you want.
Script stopped working a month ago. I receive an email from Google with this message:
Inicio | Función | Mensaje de error | Activación | Fin |
---|---|---|---|---|
20/07/17 10:14 | checkBirthdays | TypeError: No se puede encontrar la función getTime en el objeto [object Object]. (línea 339, archivo "birthday-notifications") | time-based | 20/07/17 10:14 |
What action or series of actions is the cause of the issue?
The email with birthdays of the day IS sent
The email with birthdays of the day is NOT sent, and error email from Google is sent instead
?
Hello there,
First of all, let me thank you for your tremendous helpful script. I managed to get my script to run the first time successfully around 10 days ago. However, around 5 days ago I got an email notifying me about an error in the script and it has been inactive ever since. I will attach the image of the email for you to check. I hope you can help me as soon as possible.
Regards.
I have two contacts born on 29 February (leap day).
Does GoogleContactsEventNotifier pick them up on non-leap years, triggering an email on 28 Feb or 1 Mar?
If not: is this a feature which can maybe be added?
Google has recently announced that "Google+ for consumers" is going to close (complete shutdown is scheduled for August 2019) following some security problems and given the extremely low usage of the platform (This Google Blog post has some more details).
This almost certainly means that we'll have to remove Google+ integration from the script, possibly even before August 2019 as a complete shutdown of the service might cause unexpected and unhandled errors which could potentially break the script for some users.
August 2019 is quite far in the future at the moment, but I think we should keep this in mind. In the meantime let's wait for more details like API deprecations dates.
First and foremost: thanks for this script. Really strange how Google does not offer something similar as a basic functionality out of the box for their Birthday Calendar.
I copied the script and followed the setup instructions: did the necessary configurations and activations, then executed the test case. I received the email triggered by the test case, but I noticed:
The subject of the email has:
Events: [Name 1] - [Name 2] - [Name 3] - [Name 4]
This is fine, the names match with the chosen test date + the day after.
The body text however has:
Hey! Don't forget these events:
Birthdays today ([my test date])
Birthdays tomorrow ([my test date+1])
I'd expect the names to be filled in in the body text as well?
Using version 3.1.1
Hi everyone: I'm opening this issue especially because of Hacktoberfest, a DigitalOceans's initiative which is a great way to introduce coders from all over the world to contributing to open source projects.
This script has some translations included in the source code, but at the moment the German and Indonesian ones are incomplete (some strings need translations, while others have already been translated): if you know any of these two languages and want to help us by completing the translations please do so! You could obviuosly add any other laguage as well.
This is a comprehensive guide on how to add or edit a translation, but please: feel free to ask for help in this thread if you need anything.
I noticed in the docs for the latest (3.0) API for Contacts at the top there is the following note:
Note: For read and write access to users' contacts, use the People API, which provides
both contact and profile information using JSON instead of the older GData protocol.
which links to the People API, which seems to be a merged datasource for contact info and profile data from various sources (I'm guessing primarily Google+). I haven't dived deeply into it (I am not registered for anything other than Contacts, and am hesitant to start registering for other things just to test this API). It would be nice if someone who already has access to Google+ etc wants to test whether this API is relevant and adds any value, or is just a red-herring...
What action or series of actions is the cause of the issue?
What should happen?
Lets say Birthday is 5. Oct. so I should get a notification on 4. Oct at 5pm NZ time and on 5. Oct 5pm NZ time
What happens instead?
I get a notification on 3. Oct and on 4. Oct. The birthday is shown to me to be on 4. Oct instead of 5. Oct.
Try to describe the problem in the most complete way you can add images,
error messages, hypothesis and observations regarding the problem here.
There is probably an error in the calculation between time zones.
What action or series of actions is the cause of the issue?
Send email notification
Print Error and stopped.
Error message was similar with below.
"WORK_FAX object doesn't have replace."
I simply change "return label;" to "return String(label);" of beautifyLabel function as workaround.
I had done all the settings as you described at Link. But at the end, when i click on the menu Run->start it displays an error that "Bad value (line 497, file "Code")". Google Scriprt which I used is posted herein below,
/* global Logger CalendarApp ScriptApp ContactsApp Utilities Calendar UrlFetchApp MailApp */
/*
* Thanks to this script you are going to receive an email before the birthday of each of your contacts.
* The script is easily customizable via some variables listed below.
*/
// START MANDATORY CUSTOMIZATION
// You need to personalize these values, otherwise the script won't work.
/*
* GOOGLE EMAIL ADDRESS
*
* First of all specify the gmail address of your Google Account.
* This is needed to retrieve informations about your contacts.
*/
var myGoogleEmail = REDACTED;
/*
* NOTIFICATION EMAIL ADDRESS
*
* Now specify to which email address the notifications should be sent.
* This can be the same email address of the previous line or any other email address.
*/
var myEmail = REDACTED;
/*
* ID OF THE BIRTHDAY CALENDAR
*
* Open up https://calendar.google.com, in the menu on the left click on the arrow next to the birthday calendar
* and choose 'Calendar settings', finally look for a the "Calendar ID field" (it should be something similar to
* #[email protected]): copy and paste it between the quotes in the next line.
*/
var calendarId = REDACTED;
/*
* YOUR TIMEZIONE
*
* If you need to adjust the timezone of the email notifications use this variable.
*
* Accepted values:
* GMT/UTC - examples: 'UTC+2' 'GMT-4'
* regional timezones: 'Europe/Berlin' (See here for a complete list: http://joda-time.sourceforge.net/timezones.html)
*/
var myTimeZone = 'UTC+05:30';
/*
* HOUR OF THE NOTIFICATION
*
* Specify at which hour of the day would you like to receive the email notifications.
* This must be a number between 0 and 23.
*/
var notificationHour = 9;
/*
* HOW MANY DAYS BEFORE BIRTHDAY
*
* Here you have to decide when you want to receive the email notification.
* Insert between the square brackets a comma-separated list of numbers, where each number
* represents how many day before a birthday you want to be notified.
* If you want to be notified only once then enter a single number between the brackets.
*
* Examples:
* [0] means "Notify me the day of the birthday";
* [0, 7] means "Notify me the day of the birthday and 7 days before";
* [0, 1, 7] means "Notify me the day of the birthday, the day before and 7 days before";
*
* Note: in any case you will receive one email per day: all the notifications will be grouped
* together in that email.
*/
var anticipateDays = [0];
/*
* LANGUAGE
*
* For internationalization (translation) enter the two-digit lang-code here (to add your language just fill in the
* 'i18n' hash below and change lang here to match that).
*/
var lang = 'en';
// For places where an indent is used for display reasons (in plaintext email), this number of spaces is used.
var indentSize = 2;
// END MANDATORY CUSTOMIZATION
// START DEBUGGING OPTIONS
// When debugging is not wanted you can set this true to disable debugging calls, for a slight speedup.
var noLog = false;
// When debugging (noLog == false) and you want the logs emailed too, set this to true.
var sendLog = false;
/*
* The test() function can be run on a specified date as if it is "today". Specify that date here in the format
* YEAR/MONTH/DAY HOUR:MINUTE:SECOND
* Choose a date you know should trigger a birthday notification.
*/
var fakeTestDate = '2017/02/14 06:00:00';
// END DEBUGGING OPTIONS
/*
* There is no need to edit anything below this line.
* The script will work if you inserted valid values up until here, however feel free to take a peek at my code ;)
*/
var version = '2.1';
// Merge an array at the end of an existing array.
if (typeof Array.prototype.extend === 'undefined') {
Array.prototype.extend = function (array) {
var i;
for (i = 0; i < array.length; ++i) {
this.push(array[i]);
}
return this;
};
}
if (typeof String.prototype.format === 'undefined') {
String.prototype.format = function () {
var args;
args = arguments;
return this.replace(/\{(\d+)\}/g, function (match, number) {
return typeof args[number] !== 'undefined'
? args[number]
: match
;
});
};
}
var indent = Array(indentSize + 1).join(' ');
var i18n = {
// For all languages, if a translation is not present the untranslated string
// is returned, so just leave out translations which are the same as the English.
// An entry for 'en' marks it as a valid lang config-option, but leave it empty
// to just return unaltered phrases.
'en': {},
'el': {
'UNKNOWN': 'ΑΓΝΩΣΤΟ',
'Age': 'Ηλικία',
'Birthday': 'Γενέθλια',
'Birthday today': 'Γενέθλια σήμερα',
'Birthday tomorrow': 'Γενέθλια αύριο',
'Birthday in {0} days': 'Γενέθλια σε {0} ημέρες',
'Hey! Don\'t forget these birthdays': 'Μην ξεχάσετε αυτά τα γενέθλια',
'Google Calendar Contacts Birthday Notification': 'Ενημερώσεις Γενεθλίων του Ημερολογίου Google',
'version': 'εκδοχή',
'by': 'από τον', // τον=masculine,την=feminine (using the masculine, in one place, for now but may need more context in future)
'dd-MM-yyyy': 'dd-MM-yyyy',
'send email now': 'στείλτε email τώρα',
'Mobile phone': 'Κινητό',
'Work phone': 'Τηλέφωνο εργασίας',
'Home phone': 'Τηλέφωνο οικίας',
'Main phone': 'Κύριο τηλέφωνο',
},
'it': {
'UNKNOWN': 'SCONOSCIUTO',
'Age': 'Età',
'Birthday': 'Compleanno',
'Birthday today': 'Compleanno oggi',
'Birthday tomorrow': 'Compleanno domani',
'Birthday in {0} days': 'Compleanno fra {0} giorni',
'Hey! Don\'t forget these birthdays': 'Hey! Non dimenticare questi compleanni',
'version': 'versione',
'by': 'by',
'dd-MM-yyyy': 'dd-MM-yyyy',
'send email now': 'invia email ora',
'Mobile phone': 'Cellulare',
'Work phone': 'Telefono di lavoro',
'Home phone': 'Telefono di casa',
'Main phone': 'Telefono principale',
},
/* To add a language:
'[lang-code]': {
'[first phrase]': '[translation here]',
'[second phrase]': '[translation here]',
...
}
*/
};
var birthdayCalendar = CalendarApp.getCalendarById(calendarId);
var calendarTimeZone = birthdayCalendar ? birthdayCalendar.getTimeZone() : null;
var inlineImages;
// Replace a Field.Label object with its "beautified" text representation.
function beautifyLabel (label) {
switch (label) {
case ContactsApp.Field.MOBILE_PHONE:
return _('Mobile phone');
case ContactsApp.Field.WORK_PHONE:
return _('Work phone');
case ContactsApp.Field.HOME_PHONE:
return _('Home phone');
case ContactsApp.Field.MAIN_PHONE:
return _('Main phone');
default:
return label;
}
}
/*
* Get the translation of a string.
* If the language or the chosen string is invalid return the string itself.
*/
function _ (string) {
return i18n[lang][string] || string;
}
function doLog (arg) {
noLog || Logger.log(arg);
}
/*
* Look for birthdays on a certain date.
* If testDate is not specified Date.now() will be used.
*/
function checkBirthdays (testDate) {
var anticipate, subjectPrefix, subjectBuilder,
bodyPrefix, bodySuffix1, bodySuffix2, bodyBuilder, htmlBodyBuilder, now, subject, body, htmlBody;
doLog('Starting run of GoogleCalendarBirthdayNotifications version ' + version + '.');
// The script needs this value in milliseconds, but the user entered it in days.
anticipate = anticipateDays.map(function (n) { return 1000 * 60 * 60 * 24 * n; });
// Verify that the birthday calendar exists.
if (!birthdayCalendar) {
doLog('Error: Birthday calendar not found!');
doLog('Please follow the instructions at this page to activate it: https://support.google.com/calendar/answer/6084659?hl=en');
return;
}
// Start building the email notification text.
subjectPrefix = _('Birthday') + ': ';
subjectBuilder = [];
bodyPrefix = _('Hey! Don\'t forget these birthdays') + ':';
bodySuffix1 = _('Google Calendar Contacts Birthday Notification') + ' (' + _('version') + ' ' + version + ')';
bodySuffix2 = _('by ') + 'Giorgio Bonvicini';
// The email is built both with plain text and HTML text.
bodyBuilder = [];
htmlBodyBuilder = [];
// Use the testDate if specified, otherwise use todays' date.
now = testDate || new Date();
doLog('Date used: ' + now);
inlineImages = {};
/*
* Look for birthdays on each of the days specified by the user.
* timeInterval represents how many milliseconds in the future to check.
*/
anticipate.forEach(
function (timeInterval) {
var optionalArgs, birthdays, formattedDate, whenIsIt;
// Set the search filter to include only events happening 'timeInterval' milliseconds after now.
optionalArgs = {
// Filter only events happening between 'now + timeInterval'...
timeMin: Utilities.formatDate(new Date(now.getTime() + timeInterval), calendarTimeZone, 'yyyy-MM-dd\'T\'HH:mm:ss\'Z\''),
// ... and 'now + timeInterval + 1 sec'.
timeMax: Utilities.formatDate(new Date(now.getTime() + timeInterval + 1000), calendarTimeZone, 'yyyy-MM-dd\'T\'HH:mm:ss\'Z\''),
// Treat recurring (like birthdays) events as single events.
singleEvents: true
};
doLog('Checking birthdays from ' + optionalArgs.timeMin + ' to ' + optionalArgs.timeMax);
// Get all the matching events.
birthdays = Calendar.Events.list(calendarId, optionalArgs).items;
doLog('Found ' + birthdays.length + ' birthdays in this time range.');
// If no event is found for this particular timeInterval skip it.
if (birthdays.length < 1) {
return;
}
formattedDate = Utilities.formatDate(new Date(now.getTime() + timeInterval), calendarTimeZone, _('dd-MM-yyyy'));
// Build the headers of birthday grouping by date.
bodyBuilder.push(' * ');
htmlBodyBuilder.push('<dt style="margin-left:0.8em;font-style:italic">');
switch (timeInterval / (24 * 60 * 60 * 1000)) {
case 0:
whenIsIt = _('Birthday today') + ' (' + formattedDate + ')';
break;
case 1:
whenIsIt = _('Birthday tomorrow') + ' (' + formattedDate + ')';
break;
default:
whenIsIt = _('Birthday in {0} days').format(timeInterval / (24 * 60 * 60 * 1000)) + ' (' + formattedDate + ')';
}
bodyBuilder.push(whenIsIt, ':\n');
htmlBodyBuilder.push(whenIsIt, '</dt><dd style="margin-left:0.4em;padding-left:0"><ul style="list-style:none;margin-left:0;padding-left:0;">');
// Add each of the birthdays for this timeInterval.
birthdays.forEach(
function (event, i) {
var contact;
doLog('Contact #' + i);
contact = new Contact(event);
subjectBuilder.push(contact.fullName);
bodyBuilder.extend(contact.getPlainTextLine());
htmlBodyBuilder.extend(contact.getHtmlLine());
}
);
bodyBuilder.push('\n');
htmlBodyBuilder.push('</ul></dd>');
}
);
// If there is an email to send...
if (bodyBuilder.length > 0) {
subject = subjectPrefix + subjectBuilder.join(' - ');
body = [bodyPrefix, '\n\n']
.concat(bodyBuilder)
.concat(['\n\n', indent, bodySuffix1, '\n', indent, bodySuffix2, '\n'])
.join('');
htmlBody = ['<h3>', bodyPrefix, '</h3><dl>']
.concat(htmlBodyBuilder)
.concat(['</dl><hr/><p style="text-align:center;font-size:smaller"><a href="https://github.com/GioBonvi/GoogleCalendarBirthdayNotifications">', bodySuffix1, '</a><br/>', bodySuffix2, '</p>'])
.join('');
// ...send the email notification.
doLog('Sending email...');
MailApp.sendEmail({
to: myEmail,
subject: subject,
body: body,
htmlBody: htmlBody,
inlineImages: inlineImages
});
doLog('Email sent.');
}
// Send the log if the debug options say so.
if (!noLog && sendLog) {
MailApp.sendEmail({
to: myEmail,
subject: 'Logs for birthday-notification run',
body: Logger.getLog()
});
}
}
/*
* Extract contact data from a birthday event and integrate it with additional data
* recovered directly from Google Contact through the contactId field if present.
*/
var Contact = function (event) {
var eventData, googleContact, currentYear, birthdayYear, phoneFields;
// Extract basic data from the event description.
eventData = event.gadget.preferences;
this.id = (typeof eventData['goo.contactsContactId'] === 'undefined') ? '' : eventData['goo.contactsContactId'];
this.fullName = (typeof eventData['goo.contactsFullName'] === 'undefined') ? '' : eventData['goo.contactsFullName'];
this.email = (typeof eventData['goo.contactsEmail'] === 'undefined') ? '' : eventData['goo.contactsEmail'];
this.photo = (typeof eventData['goo.contactsPhotoUrl'] === 'undefined') ? '' : eventData['goo.contactsPhotoUrl'];
this.age = '';
this.phoneFields = [];
if (this.email !== '') {
doLog('Has email.');
}
if (this.fullName !== '') {
doLog('Has full name');
}
if (this.photo !== '') {
doLog('Has photo.');
}
// If the contact has a contactId field try to get the Google Contact corresponding to that contactId.
if (this.id !== '') {
googleContact = ContactsApp.getContactById('http://www.google.com/m8/feeds/contacts/' + encodeURIComponent(myGoogleEmail) + '/base/' + this.id);
}
// If a valid Google Contact exists extract some additional data.
if (googleContact) {
// Extract contact's age if the contact has the birthday year.
if (googleContact.getDates(ContactsApp.Field.BIRTHDAY)[0]) {
doLog('Has birthday year.');
currentYear = Utilities.formatDate(new Date(event.start.date.replace(/-/g, '/')), calendarTimeZone, 'yyyy');
birthdayYear = googleContact.getDates(ContactsApp.Field.BIRTHDAY)[0].getYear();
this.age = birthdayYear !== '' ? (currentYear - birthdayYear).toFixed(0) : '';
}
// Extract contact's phone numbers.
phoneFields = googleContact.getPhones();
if (phoneFields.length > 0) {
this.phoneFields = phoneFields;
doLog('Has phones.');
}
}
/*
* Use the extracted data to build a plain line of text displaying all the
* collected data about the contact.
*/
this.getPlainTextLine = function () {
var line;
line = [];
// Full name.
line.push('\n', indent, this.fullName);
// Age.
if (this.age !== '') {
line.push(' - ', _('Age'), ': ', this.age);
}
if (this.email !== '' || typeof this.phoneFields !== 'undefined') {
line.push(' (');
// Email address.
if (this.email !== '') {
line.push(this.email);
}
// Phone numbers.
this.phoneFields.forEach(function (phoneField, i) {
var label;
if (i !== 0 || this.email !== '') {
line.push(' - ');
}
label = phoneField.getLabel();
if (label !== '') {
line.push('[', beautifyLabel(label), '] ');
}
line.push(phoneField.getPhoneNumber());
});
line.push(')');
}
line.push('\n');
return line;
};
/*
* Use the extracted data to build a line of HTML text displaying all the
* collected data about the contact.
*/
this.getHtmlLine = function () {
var line, imgCount;
line = [];
line.push('<li>');
// Profile photo.
if (this.photo !== '') {
imgCount = Object.keys(inlineImages).length;
inlineImages['contact-img-' + imgCount] = UrlFetchApp.fetch(this.photo).getBlob().setName('contact-img-' + imgCount);
line.push('<img src="cid:contact-img-' + imgCount + '" style="height:1.4em;margin-right:0.4em" />');
}
// Full name.
line.push(this.fullName);
// Age.
if (this.age !== '') {
line.push(' - ', _('Age'), ': ', this.age);
}
if (this.email !== '' || typeof this.phoneFields !== 'undefined') {
line.push(' (');
// Email address.
if (this.email !== '') {
line.push(this.email);
}
// Phone fields.
this.phoneFields.forEach(function (phoneField, i) {
var label;
if (i !== 0 || this.email !== '') {
line.push(' - ');
}
label = phoneField.getLabel();
if (label !== '') {
line.push('[', beautifyLabel(label), '] ');
}
line.push('<a href="tel:', phoneField.getPhoneNumber(), '">', phoneField.getPhoneNumber(), '</a>');
});
line.push(')');
}
// Mailto link.
if (this.email !== '') {
line.push(' <a href="mailto:', this.email, '">', _('send email now'), '</a>');
}
return line;
};
};
// Start the notification service.
function start () {
stop();
ScriptApp.newTrigger('normal')
.timeBased()
.atHour(notificationHour)
.everyDays(1)
.inTimezone(myTimeZone)
.create();
}
// Stop the notification service.
function stop () {
var triggers;
// Delete all the triggers.
triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
// Check if notification service is running.
function status () {
var toLog = 'Notifications are';
if (ScriptApp.getProjectTriggers().length < 1) {
toLog += ' not';
}
toLog += ' running.';
Logger.log(toLog);
if (!noLog && sendLog) {
MailApp.sendEmail({
to: myEmail,
subject: 'Status for birthday-notification',
body: Logger.getLog()
});
}
}
// Normal function call (This function is called by the timed trigger).
function normal () {
checkBirthdays();
}
/*
* Use this function to test the script. Edit the date in the debugging
* configuration above and click "Run"->"test" in the menu at the top
* of the Google script interface.
*/
function test () {
var testDate;
testDate = new Date(fakeTestDate);
doLog('Testing.');
doLog('Test date: ' + testDate);
checkBirthdays(testDate);
}
Please help me and thanks in advance.
regards,
Arjun Chavda
Some repos include a needs-triage
label, to acknowledge the arrival of an issue, but to say no collaborator has had time to evaluate it yet. It means you can just have this link for searching for issues to hack at, instead of this link. I'm not sure I prefer it though, because it introduces a label which is, strictly speaking, redundant - but on the other hand it is visually easier/quicker to grok...
Hey I need to stop this script.. (google calender notifications)
Please help me with some screen shots.
Thank you
Set a proper test date to match a contact having a photo in Google Contacts and run test
email should contain the photo from contacts.
email uses default profile image as contact in question has probably no public photo.
I suggest to read the photo from contacts. API is at https://developers.google.com/contacts/v3#retrieving_a_contacts_photo
I debugged a bit. Reading photo seems possible, but I do not fully understand what data sources get merged to have the current photo URL.
To @rowanthorpe and @baatochan in particular, but also to anyone else who wants to help.
In the last few days I've been working on a way to fix the poor interaction between the various services (Google Calendar, Google Contacts, Google Plus) of this script. The main problem I wanted to solve was: we have lots of information about the various contacts out there, but, at the moment, we collect it sparsely and without a systematic approach (we are even ignoring Google Plus most of the times).
So I started with this, but one thing took to another and in the end I found myself with an almost complete rewriting of the code from the ground up...
I've pushed the commits the major-rewriting
branch: I would like to hear your opinion on this new version, because I am not sure the approach I adopted is the best one and I applied for the first time some concepts I had only read about before, so there could be a lot of problems.
I've tested it and found no errors, but who knows 😉.
The test section needs to be expanded (I spent just a few minutes on that part thinking that if the code still needs to be adjusted it doesn' make sense to write the tests).
If the changes are approved before merging into master
some translation updates will be required, but this is the last problem at the moment.
In the next week I won't be able to work on the code, but will gladly read and answer to any comment and question.
EDIT: for anyone who wants to test this version of the script: you will have to activate the Google+ API exactly like you did for the Google Calendar one. Just repeat the steps in the link with "Google+ API" instead of "Google Calendar API".
This is just an idea which I thought I would braindump while it occurred to me. I haven't thought it through entirely, so maybe this version of the idea could turn out to be a bad idea, but on the other hand it might inspire a better variation on it...
v-- this --v
Perhaps there could be a user-editable object in the config section for skipping certain notifications, of the following form:
var settings = {
user: {
...
exceptions = { // fullName: eventType
'My Favourite-Grandma': 'BIRTHDAY', // if she only died recently :-(
'My Ex-Wife': ['BIRTHDAY', 'ANNIVERSARY'], // if she left you for another guy :-/
'My Arch-Nemesis': 'CUSTOM', // if you recorded the day he got the dream-job you wanted :-|
'Some Company': 'ANNIVERSARY', // maybe just a supplier, you couldn't care less about their anniversary
},
...
The rationale being that perhaps (for the obvious reasons above, or for more subtle or context-specific reasons that I haven't thought of yet), the user may wish to keep certain dates in their Contacts
for reference but does not want (annoying?, painful?) reminders of them each year.
Discuss. Feel free to flame this idea too. I can take it.
I tried test method on few dates and it worked perfectly on all them, but not on the day of my own birthday. On this day I've got 2 events - one of them is 'Happy birthday!' which Google creates automatically when you provide your birthday to Google account and the other one is my other friend birthday.
I guess I should get a mail with at least those friend's birthday
Error 'TypeError: Cannot call method "getLabel" of undefined. (line 666, file "Code")'
Line 666: var dateLabel = eachDate.getLabel();
And no mail is sent.
<p>(...)<a href="">You can find the latest one here</a>.</p>
I updated the script yesterday and did not change the mail in line 26 in code.gs. Luckily a friend has his birthday tomorrow and I got a mail today that the servre did not accept the mail. I thought whut? Is @somemail.com actually a mail addres? If you wisit somemail.com it ridirects you to a login screen. I would suggest to change that mail addres to something that does not actualk exist to minimize stress on that server if someone does not change that mail adress by accident.
I followed the instructions until I got to the step "Once you have done this open Resources->Advanced Google services again and click on the link which says "Google Cloud Platform API Dashboard".
A dialog box appeared, but I found no link labeled Google Cloud Platform API Dashboard. Therefore, no further progress made.
I have no idea.
Paging all the translators: @rowanthorpe, @lboullo0, @muzavan, @DrKrakower, @cezarylaksa,m @baatochan, @jayforce, @biboxay321, @88scythe.
Version 4.0.0 is almost ready for release: the last step, as usual, is to update all the translations: if any of you guys want to contribute with this you are, as always, warmly welcome!
You can find the branch here : please make sure to base your PRs against the v4.0.0
branch and not against master
or development
.
EDIT: v4.0.0 has been released. If anyone wants to contribute with the german and/or indonesian translation please base your PR on master
.
Thank you!
PS: as always, if you have any question about the translation you can freely ask here.
I'm opening this issue to continue the thread started about here on this now-closed pull-request. I won't copy-paste the existing thread here, it is easy enough to click through to read it.
Hi everyone,
in the last few days @rowanthorpe has done some incredible work on the script, essentially adding support for every type of event instead of just birthdays and I cannot wait to release this new improved version.
However the changes in the code are quite extensive and one of the parts which were interested is the translation table: some new strings have been introduced and some of the old ones have been modified, so they have to be fixed and updated before a release of the new version is possible.
The code is in the configurable-events
branch: if you want to contribute you can submit a Pull Request on that branch (not on master
) with the updated translation.
You'll find which strings are missing and which have to be modified directly in the code.gs
file.
Example
Before (current state of the code):
'it': {
'Age': 'Età',
// TODO: 'Years': '',
// TODO: 'Events': '',
'Birthdays today': 'Compleanno oggi', // TODO: Birthday -> Birthdays (plural)
'Birthdays tomorrow': 'Compleanno domani', // TODO: Birthday -> Birthdays (plural)
'Birthdays in {0} days': 'Compleanno fra {0} giorni', // TODO: Birthday -> Birthdays (plural)
// TODO: 'Anniversaries today': '',
// TODO: 'Anniversaries tomorrow': '',
// TODO: 'Anniversaries in {0} days': '',
// TODO: 'Custom events today': '',
// TODO: 'Custom events tomorrow': '',
// TODO: 'Custom events in {0} days': '',
'Hey! Don\'t forget these events': 'Hey! Non dimenticare questi compleanni', // TODO: birthdays -> events
'version': 'versione',
'by': 'by',
'dd-MM-yyyy': 'dd-MM-yyyy',
'Mobile phone': 'Cellulare',
'Work phone': 'Telefono di lavoro',
'Home phone': 'Telefono di casa',
'Main phone': 'Telefono principale',
},
After (fully translated code):
'it': {
'Age': 'Età',
'Years': 'Anni',
'Events': 'Eventi',
'Birthdays today': 'Compleanni oggi',
'Birthdays tomorrow': 'Compleanni domani',
'Birthdays in {0} days': 'Compleanni fra {0} giorni',
'Anniversaries today': 'Anniversari oggi',
'Anniversaries tomorrow': 'Anniversari domani',
'Anniversaries in {0} days': 'Anniversari fra {0} giorni',
'Custom events today': 'Eventi personalizzati oggi',
'Custom events tomorrow': 'Eventi personalizzati domani',
'Custom events in {0} days': 'Eventi personalizzati fra {0} giorni',
'Hey! Don\'t forget these events': 'Hey! Non dimenticare questi eventi',
'version': 'versione',
'by': 'by',
'dd-MM-yyyy': 'dd-MM-yyyy',
'Mobile phone': 'Cellulare',
'Work phone': 'Telefono di lavoro',
'Home phone': 'Telefono di casa',
'Main phone': 'Telefono principale',
},
To @lboullo0, @muzavan, @DrKrakower, @cezarylaksa in particular: I want to seize the opportunity to thank you once more for your past contributions; I would be extremely grateful if you could help with this again, but feel no obligation whatsoever 😃
When a contact has an email address or phone with no label or label 'other' the send mail looks like that:
There is no label in sent email for that.
We could resolve it in a way suggested by me in 87f3976
hi Team.,
its a very silly question from google.,
when you forget your gmail password.,
currenlty i have my password..,
but i just want to know it., 👍
hi team.,
in birthday notifications script.,
im not able to find my calender id.,
please help me with some screenshot.,
Thanks in advance.,
and im not programming geek.,
and i changed some variables., only my mail id only.,
and where to change variables.,
please help me.,
Thanks in advance.,
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.