Before starting
We are going to use Zapier to create a trigger that will call the Timmi Absences API every morning at 7am and post the day's absent list to Slack.
Note: Some of our clients use similar code with Google Cloud Engine.
Zapier is an online service for automating actions on the web. The principle is very simple: "If action X occurs in application A, then action Y occurs in application B"
We will create the following script "If it's 7am, request the day's absent list from Timmi Absences and post the message to Slack"
Connecting to Zapier
You can connect here: https://zapier.com.
Create a new account or use an existing one.
Zap configuration
You need to configure the first step. The "Choose App" button offers you a list of available tools. In the "Built-in apps" tab, you can then choose the clock.
You will then be asked to configure your clock. First by asking yourself how often you want the script to run, e.g. every day.
You can then choose what time of day the script will run, and if you want it to run on weekends.
Once this step has been validated, you can test it using the last tab, "Test this Step". For the second step, i.e. the action step, we will choose the "Code" application.
Choose Javascript
In the next step, you can copy this script into the "Code" section, leaving the "Input" section empty.
var urlBase = "https://xxxxxxxxxxx.ilucca.net/api/v3/leavesOfDay" var appToken = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; fetch(urlBase, {
"headers": { "Authorization": "lucca application=" + appToken } }) .then(function (res) {
return res.json();
}).then(function (data) {
var entries = data.data.items;
var result = [];
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
var res = {
name: entry.user.displayName,
};
if(entry.startsAM && (!entry.endsAM || entry.endsOn != entry.startsOn)) {
res.detail = "toute la journée";
} else if(entry.startsAM && entry.endsAM && entry.endsOn == entry.startsOn) {
res.detail = "ce matin";
} else {
res.detail = "cet après-midi";
}
if (entry.endsOn !== entry.startsOn) {
var endSp = entry.endsOn.split("T")[0].split("-");
res.detail += " et jusqu'au " + endSp[2] + "/" + endSp[1] + "/" + endSp[0] + " inclus";
}
result.push(res);
}
if (result.length === 0) {
result.push({
name: "Personne n'",
detail: "aujourd'hui"
});
}
callback(null, result);
}).catch(function (error) {
console.log(error);
});
To use this script, replace xxxxxxxxxxx in the variable urlBase with the name of your Lucca instance and replace the variable appToken xxxxxxxxxxx-xxxxxx-xxxxxxxx with yours; you can then get this token from the dedicated interface or by contacting the Lucca Help desk.
A paid Zapier account is (sometimes) required to perform this type in zap, otherwise the test may display an Error.
All you have to do now is configure the last action to send the results to one of your Slack channels.
Choose the option "send a message to a channel"
For the next step, log in with your Slack account login details. Then complete the options, starting with the channel name:
Once you have entered the channel name, you need to enter the message you want to display, as shown in the below example.
All you have to do now is validate this step and run the tests to complete the configuration.
Launching zap
You can start zap by pressing the "OFF" button to switch it to "ON".
Every morning at 7am Slack will give you a list of the day's absentees.
Advanced zap (for experts)
You can configure a zap in 5 steps that will notify the team in a dedicated channel a few days before the absence.
In step 2
add 4 Data inputs
URL: https://lucca.ilucca.net/api/v3/leaves?leavePeriod.ownerId=greaterthan,0
appToken: your token
offsetDays: 2 (= be notified 2 days before)
numberDayMinimum: 0 (= be notified if there is a minimum of 0 consecutive leave days)
Add the following code to the code section:
// Function used to process the result retrieved with the request in order to send the results
var getCleanChannelName = function (channelName) {
channelName = channelName.replace(/\s+/g, '');
channelName = 'absence_' + channelName.toLocaleLowerCase();
return channelName;
}
var sendResult = function (results) {
var res = {};
res.list = [];
res.zapierLimitSize = 25;
if (input.validDepartment) {
res.validDepartmentList = input.validDepartment.toLocaleLowerCase().split(',');
for (var i = 0; i < res.validDepartmentList.length; i++) {
res.validDepartmentList[i] = getCleanChannelName(res.validDepartmentList[i]);
}
console.log(res.validDepartmentList);
}
res.channelExist = function (channelName) {
channelName = channelName.toLocaleLowerCase();
for (var index = 0; index < this.list.length; index++) {
if (channelName && this.list[index].channel && channelName === this.list[index].channel) {
return true;
}
}
return false;
}
res.channelAdd = function (channelName) {
channelName = channelName.toLocaleLowerCase();
if (channelName && this.list.length < this.zapierLimitSize && !this.channelExist(channelName)) {
var newElem = {};
newElem.channel = channelName;
newElem.message = '';
this.list.push(newElem);
}
}
res.listAddMessage = function (channelName, message) {
channelName = channelName.toLocaleLowerCase();
if (this.list.length < this.zapierLimitSize) {
this.channelAdd(channelName);
for (var index = 0; index < this.list.length; index++) {
if (channelName && this.list[index].channel === channelName) {
this.list[index].message += message;
break;
}
}
}
}
res.listAddMessageFilter = function (channelName, message) {
channelName = channelName.toLocaleLowerCase();
if (this.validDepartmentList === undefined || this.validDepartmentList === "") {
this.listAddMessage(channelName, message);
}
else {
for (var index = 0; index < this.validDepartmentList.length; index++) {
if (this.validDepartmentList[index] === channelName) {
this.listAddMessage(channelName, message);
}
}
}
}
if (results !== undefined) {
for (var i = 0; i < results.length; i++) {
if (results[i].numberDay >= input.numberDayMinimum) {
if (results[i].numberDay >= 2) {
var messageTmp = results[i].name + ' : à partir de ' + results[i].detail + '\n';
}
else {
var messageTmp = results[i].name + ' : ' + results[i].detail + '\n';
}
var channelName = getCleanChannelName(results[i].departmentName)
res.listAddMessageFilter(channelName, messageTmp);
}
}
}
callback(null, res.list);
}
var dayDiff = function (d1, d2) {
d1 = d1.getTime() / 86400000;
d2 = d2.getTime() / 86400000;
return new Number(d2 - d1).toFixed(0);
};
var checkIsIn = function (result, name) {
for (var i = 0; i < result.length; i++) {
if (result[i] === name) {
return true;
}
}
return false;
};
// Fichier de demande de requêtes
var request = function (thisDay) {
var results = [];
fetch(input.url + '&date=' + thisDay.todayS + '&fields=isAM,leavePeriod[owner.name,owner.department,endsOn,endsAM,startsOn]', {
'headers': {
'Authorization': 'lucca application=' + input.appToken
}
}).then(function (res) {
return res.json();
}).then(function (data) {
console.log(data);
var leaves = data.data.items;
var hash = {};
// List all absentees at J+input.offsetDays
leaves.map(function (leave) {
var username = leave.leavePeriod.owner.name;
var userleave = hash[username];
if (!userleave) {
userleave = {
morning: false,
afternoon: false,
end: leave.leavePeriod.endsOn.split('T')[0]
};
hash[username] = userleave;
console.log(leave.leavePeriod.startsOn.split('T')[0]);
if (leave.leavePeriod.startsOn.split('T')[0] === thisDay.todayS) {
results.push({
name: username,
leave: userleave,
departmentName: leave.leavePeriod.owner.department.name,
departmentId: leave.leavePeriod.owner.department.id
});
}
}
if (leave.isAM) {
userleave.morning = true;
} else {
userleave.afternoon = true;
}
});
// Create outgoing messages for each absent person
results.map(function (curRes) {
var endSp = curRes.leave.end.split('-');
var endSpDate = new Date(endSp[0], endSp[1] - 1, endSp[2]);
var numberDay = parseInt(dayDiff(thisDay.date, endSpDate)) + 1;
if (curRes.leave.morning && curRes.leave.afternoon) {
curRes.detail = thisDay.formatString;
} else if (curRes.leave.morning) {
curRes.detail = thisDay.formatString + ' matin';
} else {
curRes.detail = thisDay.formatString + ' après-midi';
}
if (curRes.leave.end !== curRes.todayS && numberDay >= 2) {
if (numberDay < 7) {
curRes.detail += ' pendant ' + numberDay;
curRes.detail += numberDay > 1 ? ' jours' : ' jour';
}
else {
var nbrWeek = (numberDay / 7) >> 0;
if ((numberDay / 7 - nbrWeek) > 0.5) {
nbrWeek += 1;
}
curRes.detail += ' pendant ' + nbrWeek;
curRes.detail += nbrWeek > 1 ? ' semaines' : ' semaine';
}
curRes.numberDay = numberDay;
}
else {
curRes.numberDay = 0;
}
});
sendResult(results);
}).catch(function (error) {
console.log(error);
});
}
// Fichier d'initialisation des variables de date.
var getNextWorkingDate = function (offsetDays) {
// Create all date variables.
if (!offsetDays) {
return null;
}
var day = new Date();
var nextDay = {};
for (var i = 0; i < offsetDays; i++) {
day.setDate(day.getDate() + 1);
if (day.toDateString().split(' ')[0] === 'Sat' || day.toDateString().split(' ')[0] === 'Sun') {
day.setDate(day.getDate() + 2);
}
}
var weekDays = {
'Mon': 'lundi',
'Tue': 'mardi',
'Wed': 'mercredi',
'Thu': 'jeudi',
'Fri': 'vendredi',
'Sat': 'samedi',
'Sun': 'dimanche'
};
nextDay.date = day;
nextDay.yearS = '' + day.getFullYear();
nextDay.month = day.getMonth() + 1;
nextDay.monthS = nextDay.month > 9 ? '' + nextDay.month : '0' + nextDay.month;
nextDay.dayS = day.getDate() > 9 ? '' + day.getDate() : '0' + day.getDate();
nextDay.todayS = nextDay.yearS + '-' + nextDay.monthS + '-' + nextDay.dayS;
nextDay.formatString = weekDays[day.toDateString().split(' ')[0]];
return nextDay;
};
// If the variable input exists, check the value of offsetDays and the value of numberDayMinimum
if (input) {
if (input.offsetDays === undefined || input.offsetDays < 0) {
input.offsetDays = 0;
}
if (input.numberDayMinimum === undefined || input.numberDayMinimum < 0) {
input.numberDayMinimum = 0;
}
// Then set the nextDay variable with the next date to be taken into account
var nextDay = getNextWorkingDate(input.offsetDays);
// And finally launch the request
request(nextDay);
}
Step 3 involves creating a Slack channel
We add a Filter for step 4
Step 5 adds the message to the correct channel.