hubot-standup-formstack-cron
Version:
A hubot script that gets Formstack entries and posts the info on a cron
798 lines (749 loc) • 35.2 kB
JavaScript
/*
Description:
Report standup notes that were filled in using formstack
Commands:
hubot standup setup <FORMID> <TIME> <REMINDER> <CRONDAYS> - Setup the script for the first time
FORMID - Formstack Form ID\n`;
TIME - Time of auto post (8:00am or 14:00)
REMINDER - Number of minutes before to send reminder (15) Default 30
CRONDAYS - Days to post in cron format (1-5 or 0,1,2,3) 0 = Sunday. Default 1-5 (weekdays)
Reminder and crondays can be skipped to accept defaults
hubot standup List all results of standup form for today
hubot standup today List all who have filled out the standup form today
hubot standup <person> List <person> results of standup form today (search first and/or last name)
hubot standup randomize Randomize the list of all results of standup form
Author:
theycallmesef
Configuration:
- Formstack form MUST have fields with the following key terms (in any order).
- The fields do not need to be verbatim and are not case sensitive.
- "Date of report" would work for the "Date" field.
Date (REQUIRED) Formstack date the report is for
Yesterday (REQUIRED) Formstack tasks from Yesterday
Today (REQUIRED) Formstack tasks for Today
Blocker or Impeding (REQUIRED) Blockers or items keeping work or tasks from happening
First Name (REQUIRED) Formstack User (first or other) name
Last Name (OPTIONAL) Formstack User Last Name
Environment Variables:
HUBOT_FORMSTACK_TOKEN - (REQUIRED) Formstack API Token
HUBOT_FORMSTACK_PREFIX - (OPTIONAL) set a prefix for multiple standup reports
HUBOT_FORMSTACK_HEAR - (OPTIONAL) Turn on or off hubot hear (default off)
HUBOT_FORMSTACK_SUBMISSIONS_LOOKBACK - (OPTIONAL) Formstack submissions limiter (default 5 days)
HUBOT_FORMSTACK_TIMEZONE - (Optional) Set Timezone for all forms (default timezone of the form)
Notes:
Formstack is an online form builder (https://www.formstack.com/)
Form fields:
- Date Feild
- Yesterday notes
- Today Notes
- Blocker Notes
- First Name
- Last Name (optional)
Dependencies:
"hubot-redis-brain": "",
"cron": ">=1.7.2"
TODO:
*/
const FS_TOKEN = process.env.HUBOT_FORMSTACK_TOKEN; //(Required) Formstack API Token
let PREFIX_A = process.env.HUBOT_FORMSTACK_PREFIX && (PREFIX = `${process.env.HUBOT_FORMSTACK_PREFIX}-`) || ""; //(Optional) set a prefix for multiple standup reports, usually used in combination with "on hear"
const PREFIX = PREFIX_A;
let ONHEAR_A = process.env.HUBOT_FORMSTACK_HEAR || false; //(Optional) Turn on or off hubot hear (default off)
const ONHEAR = ONHEAR_A;
// NOTE: There is some type of limit on the amount of data returned and truncates the most recent data off. I would not set the look back farther that 5 for 4-5 people filling out the form.
let DAYSBACK_A = process.env.HUBOT_FORMSTACK_SUBMISSIONS_LOOKBACK || 3; //(Optional) filter formstack submissions within X day ago
const DAYSBACK = DAYSBACK_A;
module.exports = (robot) => {
// Push Help Commands
robot.commands.push(`hubot ${PREFIX}standup - List results of standup form for today`);
robot.commands.push(`hubot ${PREFIX}standup today - List who has filled out the standup form`);
robot.commands.push(`hubot ${PREFIX}standup <USERNAME> - List results of standup form for today`);
robot.commands.push(`hubot ${PREFIX}standup randomize - Randomize the list of all results of standup form`);
robot.commands.push(`hubot ${PREFIX}standup setup - Setup a form in a room (see help for more)`);
robot.commands.push(`hubot ${PREFIX}standup remove - Remove a form from a room`);
robot.commands.push(`hubot ${PREFIX}standup help - List command help and how to set up chat room`);
// cron module
const CronJob = require('cron').CronJob;
// timezone module
const { DateTime } = require('luxon');
// setup global arrays to store vars for each room
REMINDER_CRON_JOB = [];
STANDUP_REPORT_CRON_JOB = [];
FS_URL = [];
FS_FORMID = [];
DATEFIELD_ID = [];
YDAY_ID = [];
TDAY_ID = [];
BLOCK_ID = [];
USERFN_ID = [];
USERLN_ID = [];
FSAPIURL = [];
TIMEZONE = [];
RANDOMIZE = [];
// Backwards Compatability set up form from global variables
if (process.env.HUBOT_FORMSTACK_FORM_ID && process.env.HUBOT_FORMSTACK_CHAT_ROOM_NAME) {
robot.logger.info("standup-formstack-cron: Setting up Form from env's");
const id = process.env.HUBOT_FORMSTACK_FORM_ID;
const rm = process.env.HUBOT_FORMSTACK_CHAT_ROOM_NAME;
// Auto setup form from env's
SetupForm(rm, id);
// Auto setup cron from env's
if ((process.env.HUBOT_FORMSTACK_REMINDER_CRON || process.env.HUBOT_FORMSTACK_STANDUP_REPORT_CRON) && process.env.HUBOT_FORMSTACK_TIMEZONE) {
robot.logger.info("standup-formstack-cron: Setting up Cron from env's");
const mn = process.env.HUBOT_FORMSTACK_REMINDER_CRON;
const rp = process.env.HUBOT_FORMSTACK_STANDUP_REPORT_CRON;
const tm = process.env.HUBOT_FORMSTACK_TIMEZONE;
SetCron(rm, rp, mn, tm);
};
};
// Load all crons from previous steup rooms from list in redis
robot.brain.on('connected', () => {
let brainrooms=[], rooms;
try {
robot.logger.info("standup-formstack-cron: Gathering rooms with forms setup from redis");
// Create array of rooms or empty array
brainrooms = robot.brain.get(`FS_Rooms`) || [];
if (brainrooms.length > 0) {
// ignore any duplicates
rooms = brainrooms.filter((c, index) => {
return brainrooms.indexOf(c) === index;
});
robot.logger.info(`standup-formstack-cron: Loading ${rooms.length} rooms`);
// loop list of rooms and set up crons that were setup before
for (room of rooms) {
const standup_report_cron = robot.brain.get(`FS_${room}.REPORT_CRON`);
const reminder_cron = robot.brain.get(`FS_${room}.REMIND_CRON`);
const timezone = robot.brain.get(`FS_${room}.TIMEZONE`);
// Check vars and setup cron
if (standup_report_cron && reminder_cron) {
robot.logger.info(`standup-formstack-cron: setting up room ${room}`);
robot.messageRoom(room, `I got rebooted. I'm restoring the reminder and standup schedule from memory...`);
FS_FORMID[room] = robot.brain.get(`FS_${room}.FS_FORMID`);
SetCron(room, standup_report_cron, reminder_cron, timezone);
} else {
// filter out room and remove from list
rooms = rooms.filter(e => e !== room);
};
};
// save any changes back to redis
robot.brain.set(`FS_Rooms`, rooms);
};
} catch(err) {
robot.logger.info(`standup-formstack-cron: Failed to gather rooms with forms setup from redis ${err}`);
};
});
// ---- ad-hoc commands ----
//Regex summery: "standup" maybe any nummber of spaces, then construct for "setup" or a word maybe with another word
const REGX = "standup\\s*(setup (\\d+)\\s*(?:(\\d{1,2}:\\d{2}(?:am|pm)?|\\d{4}) ?(?:(\\d{1,2}) ?([0-6]\\-[0-6]|[0-6](?:,[0-6]){0,6})?)?)?|\\w+(?:\\s+[a-z]+)?)?$";
// Hear command without addressing hubot
if (ONHEAR) {
robot.hear(new RegExp("^" + PREFIX + REGX, 'i'), (msg) => {
msg.finish();
BotRespond(msg);
});
};
// Respond to command by addressing hubot
robot.respond(new RegExp(PREFIX + REGX, 'i'), (msg) => {
msg.finish();
BotRespond(msg);
});
// Parse responce for commands
function BotRespond(msg) {
const room = msg.message.room;
// Ignore the command "standup" and pull remaining command text
const rxmatch = msg.match[1];
robot.logger.info("standup-formstack-cron: Bot responding to command");
// Check formstack token is set
if (!FS_TOKEN) {
robot.messageRoom(room, "Unable to run this plugin\nCannot find the Formstack token");
robot.logger.error("standup-formstack-cron: Missing formstack token. Please add global variable");
return;
};
if (!GetFormInfoRedis(room)) {
if (FS_FORMID[room]) {
GetFormInfo(room);
};
};
if (FS_FORMID[room]){
if (rxmatch) {
// Logic to seperate the commands
if (rxmatch.toLowerCase() === "today" || rxmatch.toLowerCase() === "list") {
// fuction to list all users that filled out the form
FilledItOut(room);
} else if (rxmatch.toLowerCase() === "remove") {
// function to remove standup from room
RemoveStandup(room);
} else if (rxmatch.toLowerCase() === "help") {
// function to list available commands
HelpReply(room);
} else if (rxmatch.toLowerCase() === "randomize") {
// Toggle randomness of full report
RANDOMIZE[room] = !RANDOMIZE[room];
robot.brain.set(`FS_${room}.RANDOMIZE`, RANDOMIZE[room]);
if (RANDOMIZE[room]) {
robot.messageRoom(room, `Full reports will be in random order`);
} else {
robot.messageRoom(room, `Full reports will be in order submitted`);
};
} else if (rxmatch.toLowerCase().substring(0, 5) === "setup") {
// Catch if a form is already setup in room
robot.messageRoom(room, `There seems to be a form already linked to this room (Form ID: ${FS_FORMID[room]})\nIf you would like to replace the current form\nplease run the remove command and then setup the new one.`);
} else if (!["today", "help", "randomize", "setup", "remove"].includes(rxmatch)) {
// If non command, call function to list a single users form for today
SingleReport(room, rxmatch);
};
} else {
// If second part is empty, call function to list all results of form for today
ReportStandup(room);
};
} else {
// look for setup command
if (rxmatch && rxmatch.toLowerCase().substring(0, 5) === "setup") {
// function to setup form to room
if (msg.match[2]){
robot.messageRoom(room, `Setting up connection to form`);
robot.logger.info("standup-formstack-cron: Setting up connection to form");
SetupForm(room, msg.match[2], msg.match[3], msg.match[4], msg.match[5]);
} else {
robot.messageRoom(room, "I don't understand the command\nI think there is something missing, please try again");
HelpReply(room);
};
} else {
robot.messageRoom(room, "A form is not setup for this room\nTo attach a form to this room, please use the 'Setup' command");
HelpReply(room);
};
};
};
// ---- Setup form and cron to room ----
function SetupForm(room, formid, reporttime, remindbeforemin, days) {
let time, date, rooms = [];
// Make sure form id is a number
if (Number.isInteger(Number(formid))) {
FS_FORMID[room] = formid;
robot.brain.set(`FS_${room}.FS_FORMID`, FS_FORMID[room]);
} else {
robot.messageRoom(room, `The form ID entered (${formid}), is not a number\nPlease try again`);
return;
};
// Get form data and "timezone" from Form
GetFormInfo(room).then((result) => {
if (result) {
// Setup cron
if (reporttime) {
// Set date
time = reporttime.split(":");
// Convert to 24h time
if (reporttime.match(/pm$/i)) {
if (parseInt(time[0]) !== 12) {
time[0] = parseInt(time[0]) + 12;
};
time[1] = time[1].slice(0,-2);
}else if (reporttime.match(/am$/i)) {
if (parseInt(time[0]) === 12) {
time[0] = parseInt(time[0]) + 12;
};
time[1] = time[1].slice(0,-2);
};
// Set days if undefined
// !RegExp('\\d\-\\d|\\d|(\\d\,\\s)+\\d').test(days)
if (days === undefined) {
days = "1-5";
};
// Set Reminder if null
if (!Number.isInteger(Number(remindbeforemin)) || remindbeforemin === undefined) {
remindbeforemin = 30;
};
// Set crons
date = new Date();
// set time in date object in order to properlly do time math
date.setHours(time[0]);
date.setMinutes(time[1]);
// Set report cron for time and days
const standup_report_cron = `${date.getMinutes()} ${date.getHours()} * * ${days}`;
// Set Minutes minus X minutes
date.setMinutes( date.getMinutes() - remindbeforemin);
// Set reminder cron for time and days
const reminder_cron = `${date.getMinutes()} ${date.getHours()} * * ${days}`;
//log built crons
robot.logger.info(`standup-formstack-cron: Report Cron Set To - ${standup_report_cron}`);
robot.logger.info(`standup-formstack-cron: Remind Cron Set To - ${reminder_cron}`);
// Save cron time to redis
robot.brain.set(`FS_${room}.REPORT_CRON`, standup_report_cron);
robot.brain.set(`FS_${room}.REMIND_CRON`, reminder_cron);
//Setup Cron
SetCron(room, standup_report_cron, reminder_cron, TIMEZONE[room]);
} else {
robot.messageRoom(room, `No report or reminder cron was set`);
robot.logger.info(`standup-formstack-cron: No report cron was defined`);
};
};
})
.then(result => {
robot.logger.info(`standup-formstack-cron: Adding room ${room} to rooms list in redis`);
//add rooms to list
rooms = robot.brain.get(`FS_Rooms`);
rooms.push(room);
robot.brain.set(`FS_Rooms`, rooms);
robot.logger.info("standup-formstack-cron: Form setup has completed");
return true;
})
.catch(err => {
robot.logger.info(`standup-formstack-cron: SetupForm: Return False ${err}`);
return false;
});
};
// ---- setup auto post and reminder cron ----
function SetCron(room, standup_report_cron, reminder_cron, timezone){
robot.logger.info(`standup-formstack-cron: Running cron setup: Room - ${room}`);
// Reminder with names of those who already filled it out cron
if (room) {
if (reminder_cron) {
robot.logger.info(`standup-formstack-cron: Setting up reminder cron: Room - ${room} | Timezone - ${timezone}`);
// Reminder Cron
REMINDER_CRON_JOB[room] = new CronJob(reminder_cron, function() {
GetFormInfoRedis(room);
robot.messageRoom(room, `@here Time to fill out the <${FS_URL[room]}|stand up report>\n`);
// fuction to list who has filled out the form
return FilledItOut(room);
robot.logger.info("standup-formstack-cron: Finish FilledItOut Cron");
}, null, true, timezone);
robot.logger.info(`standup-formstack-cron: Starting Reminder Cron: Room - ${room}`);
REMINDER_CRON_JOB[room].start();
} else {
robot.logger.error("standup-formstack-cron: Missing variable for reminder cron.");
};
if (standup_report_cron) {
// Report results cron
STANDUP_REPORT_CRON_JOB[room] = new CronJob(standup_report_cron, function() {
GetFormInfoRedis(room);
// fuction to list results of form for today "true" is passed to "CronRun"
return ReportStandup(room, true);
}, null, true, timezone);
robot.logger.info(`standup-formstack-cron: Starting Report Cron: Room - ${room}`);
STANDUP_REPORT_CRON_JOB[room].start();
} else {
robot.logger.error("standup-formstack-cron: Missing variable for standup cron");
};
// Test if cron is Running
if (STANDUP_REPORT_CRON_JOB[room].running && REMINDER_CRON_JOB[room].running) {
robot.messageRoom(room, `Form reminder (${FS_FORMID[room]}) setup was successfull\nNext reminder running at ${REMINDER_CRON_JOB[room].nextDates(1)}\nNext report running at ${STANDUP_REPORT_CRON_JOB[room].nextDates(1)}`);
//return true;
} else {
robot.messageRoom(room, `Form reminder (${FS_FORMID[room]}) failed to be setup\nPlease ask my owner to check the logs`);
//return false;
};
} else {
robot.logger.error("standup-formstack-cron: Missing variable for room");
};
};
// --- test for empty values in an array ---
function TestArrayValues(array) {
robot.logger.info("standup-formstack-cron: Testing array values");
for (i = 0; i < array.length; i++) {
if (!array[i] || array[i] === '' || array[i] == undefined) {
// var does not have a value
return false;
};
};
return true;
};
// ---- Pull Data From Formstack HTTP Webhook ----
// Get info about the form from http webhook (like url and field id's) and then save it to redis (hubot brain)
function GetFormInfo(room) {
let jdata;
FSAPIURL[room] = `https://www.formstack.com/api/v2/form/${FS_FORMID[room]}`;
const fsurl = `${FSAPIURL[room]}.json?oauth_token=${FS_TOKEN}`;
robot.logger.info(`standup-formstack-cron: Calling API to get form data from formstack (${FSAPIURL[room]})`);
// Call FS http get function
return new Promise ((resolve, reject) => {
GetFormSubData(room, fsurl, (jdata) => {
// Get form url
FS_URL[room] = jdata.url;
TIMEZONE[room] = jdata.timezone;
robot.logger.info("standup-formstack-cron: Pulling form data from formstack json");
// Get field id's by keyword search
for (field of jdata.fields) {
let fieldLabel=field.label.toLowerCase();
if (fieldLabel.match(/\bdate\b/i)) {
DATEFIELD_ID[room] = field.id;
} else if (fieldLabel.match(/\byesterday\b|\bprevious\b/i)) {
YDAY_ID[room] = field.id;
} else if (fieldLabel.match(/\btoday\b/i)) {
TDAY_ID[room] = field.id;
} else if (fieldLabel.match(/\bimped|\bblock/i)) {
BLOCK_ID[room] = field.id;
} else if (fieldLabel.match(/\bfirst name\b/i)) {
USERFN_ID[room] = field.id;
} else if (fieldLabel.match(/\blast name\b/i)) {
USERLN_ID[room] = field.id;
};
};
// Array of all required formstack vars
const fsredisarr = [FS_FORMID[room], DATEFIELD_ID[room], YDAY_ID[room], TDAY_ID[room], BLOCK_ID[room], USERFN_ID[room]];
// test array and write vars to redis
if (TestArrayValues(fsredisarr)) {
try {
robot.brain.set(`FS_${room}.FS_URL`, FS_URL[room]);
robot.brain.set(`FS_${room}.FS_FORMID`, FS_FORMID[room]);
robot.brain.set(`FS_${room}.DATEFIELD_ID`, DATEFIELD_ID[room]);
robot.brain.set(`FS_${room}.YDAY_ID`, YDAY_ID[room]);
robot.brain.set(`FS_${room}.TDAY_ID`, TDAY_ID[room]);
robot.brain.set(`FS_${room}.BLOCK_ID`, BLOCK_ID[room]);
robot.brain.set(`FS_${room}.USERFN_ID`, USERFN_ID[room]);
robot.brain.set(`FS_${room}.USERLN_ID`, USERLN_ID[room]);
robot.brain.set(`FS_${room}.FSAPIURL`, FSAPIURL[room]);
robot.brain.set(`FS_${room}.TIMEZONE`, TIMEZONE[room]);
} catch(err) {
robot.logger.error(`standup-formstack-cron: Error saving to redis ${err}`);
reject("error");
};
} else {
robot.logger.error(`standup-formstack-cron: One or more variables are empty ${fsredisarr}`);
robot.messageRoom(room, "I was not able to find the form, Please ask my owner to check the logs");
reject("error");
};
robot.logger.info(`standup-formstack-cron: Form values were successfully pulled and saved to redis`);
resolve("Done");
});
});
};
// ---- Pull Data From Redis ----
// Get info about the form saved in redis (like url and field id's)
function GetFormInfoRedis(room) {
robot.logger.info("standup-formstack-cron: Gathering data from redis");
// Set vars from redis data
try {
FS_URL[room] = robot.brain.get(`FS_${room}.FS_URL`);
FS_FORMID[room] = robot.brain.get(`FS_${room}.FS_FORMID`);
DATEFIELD_ID[room] = robot.brain.get(`FS_${room}.DATEFIELD_ID`);
YDAY_ID[room] = robot.brain.get(`FS_${room}.YDAY_ID`);
TDAY_ID[room] = robot.brain.get(`FS_${room}.TDAY_ID`);
BLOCK_ID[room] = robot.brain.get(`FS_${room}.BLOCK_ID`);
USERFN_ID[room] = robot.brain.get(`FS_${room}.USERFN_ID`);
USERLN_ID[room] = robot.brain.get(`FS_${room}.USERLN_ID`);
FSAPIURL[room] = robot.brain.get(`FS_${room}.FSAPIURL`);
TIMEZONE[room] = robot.brain.get(`FS_${room}.TIMEZONE`);
RANDOMIZE[room] = robot.brain.get(`FS_${room}.RANDOMIZE`);
} catch(err) {
robot.logger.error(`standup-formstack-cron: Error retreving from redis ${err}`);
};
// Array of required formstack vars
const fsvararr = [FS_FORMID[room], DATEFIELD_ID[room], YDAY_ID[room], TDAY_ID[room], BLOCK_ID[room], USERFN_ID[room]];
// test array of vars pulled from redis
if (TestArrayValues(fsvararr)) {
// got the info
robot.logger.info(`standup-formstack-cron: Field Id's gathered from redis (${fsvararr})`);
return true;
} else {
// Did not get all the info
robot.logger.info("standup-formstack-cron: Could not read Field Id's in at least one var from redis" );
return false;
};
};
// ---- remove a standup form from a room ----
function RemoveStandup(room) {
let rooms;
robot.messageRoom(room, `Removing link to form ID ${FS_FORMID[room]} from this room`);
robot.logger.info(`standup-formstack-cron: Removing form link ID ${FS_FORMID[room]} from room ${room}`);
// remove brain entries
try {
robot.brain.remove(`FS_${room}.FS_URL`);
robot.brain.remove(`FS_${room}.FS_FORMID`);
robot.brain.remove(`FS_${room}.DATEFIELD_ID`);
robot.brain.remove(`FS_${room}.YDAY_ID`);
robot.brain.remove(`FS_${room}.TDAY_ID`);
robot.brain.remove(`FS_${room}.BLOCK_ID`);
robot.brain.remove(`FS_${room}.USERFN_ID`);
robot.brain.remove(`FS_${room}.USERLN_ID`);
robot.brain.remove(`FS_${room}.FSAPIURL`);
robot.brain.remove(`FS_${room}.TIMEZONE`);
robot.brain.remove(`FS_${room}.RANDOMIZE`);
} catch(err) {
robot.messageRoom(room, `There was an issue, please have my owner check my logs`);
robot.logger.error(`standup-formstack-cron: Error deleting values in brain ${err}`);
return;
};
// unset vars
delete FS_URL[room];
delete FS_FORMID[room];
delete DATEFIELD_ID[room];
delete YDAY_ID[room];
delete TDAY_ID[room];
delete BLOCK_ID[room];
delete USERFN_ID[room];
delete USERLN_ID[room];
delete FSAPIURL[room];
delete RANDOMIZE[room];
try {
robot.brain.get(`FS_${room}.FS_FORMID`);
} catch(err) {
robot.messageRoom(room, `There was an error when removing`);
robot.logger.info(`standup-formstack-cron: Form failed to be removed from room ${room}. Error: ${err}`);
return;
};
//Stop any crons that were setup
robot.logger.info(`standup-formstack-cron: Removing any cron jobs that were setup`);
if (STANDUP_REPORT_CRON_JOB[room].running) {
STANDUP_REPORT_CRON_JOB[room].stop();
};
if (REMINDER_CRON_JOB[room].running) {
REMINDER_CRON_JOB[room].stop();
};
if (STANDUP_REPORT_CRON_JOB[room].running || REMINDER_CRON_JOB[room].running) {
robot.logger.info(`standup-formstack-cron: One or more cron jobs failed to stop`);
};
// remove room from list in redis
rooms = robot.brain.get(`FS_Rooms`) || [];
// filter out room to remove
rooms = rooms.filter(e => e !== room);
robot.brain.set(`FS_Rooms`, rooms);
robot.messageRoom(room, `Removal Complete`);
robot.logger.info(`standup-formstack-cron: Form has been removed from room ${room}`);
};
// ---- Date calculator and formater ----
// returns formated current date "datefromat" and lookback date "mindate"
function CalcDate(room) {
let today;
robot.logger.info(`standup-formstack-cron: Running calculations for the date`);
//set date time to match timezone var time
if (DateTime.local().setZone(TIMEZONE[room]).isValid) {
today = DateTime.now().setZone(TIMEZONE[room]);
} else {
today = DateTime.now().setZone("America/Los_Angeles");
};
// create lookback date limit to filter submissions results using "min_time" param in url
// formstack api "min_time" param is based on eastern time (any hard coded time is arbitrary)
const mindate = `${DateTime.fromISO(today.minus({ days: DAYSBACK })).toFormat('yyyy-LL-dd')}`;
const datefromat = DateTime.fromISO(today).toFormat('LLL dd, yyyy');
// return formated dates
if (datefromat && mindate) {
return [datefromat, mindate];
} else {
robot.logger.error("standup-formstack-cron: Issue with date formats. One or more is missing");
};
};
// ---- Return json from formstack web request ----
// "room" and "fsurl" are passed in, "jbody" is the return
function GetFormSubData(room, fsurl, callback) {
robot.logger.info("standup-formstack-cron: Gathering json data from form api");
// Get json of form submissions
robot.http(fsurl)
.header('Accept', 'application/json')
.get()((err, res, body) => {
if (err) {
// send error message to room
robot.messageRoom(room, `I was not able to connect to Formstack: ${res}`);
robot.logger.error(`standup-formstack-cron: Error connecting to formstack: ${res}`);
return;
} else {
const jdata = JSON.parse(body);
if (jdata.error) {
robot.messageRoom(room, "Somethings not right, have my owner take a look at my logs");
robot.logger.error(`standup-formstack-cron: Error retreving data: ${jdata.error}`);
};
// send results to return function
robot.logger.info(`standup-formstack-cron: Data retrived from API`);
callback(jdata);
};
});
};
// ---- Format and Clean message data ----
function FormatClean(room, entry, callback) {
let message, userln;
robot.logger.info(`standup-formstack-cron: Cleaning format of text for report`);
// fuction to clean up submission text
function CleanTxt(value) {
// Clean trailing spaces
const cleaned1 = value.replace(/\s+$/g, "");
// Clean Leading spaces and leading hyphans
const cleaned2 = cleaned1.replace(/^\s*\-+\s*|\s*\-+\s*$|^\s+/gm, "");
// Clean trailing spaces..again
const cleaned3 =cleaned2.replace(/\s+$/g, "");
// Add tab and hyphan
return cleaned3.replace(/^/gm, "\t\- ");
};
// set vars for text from json data
const datefield = entry.data[DATEFIELD_ID[room]].value;
const yday = entry.data[YDAY_ID[room]].value;
const tday = entry.data[TDAY_ID[room]].value;
const block = entry.data[BLOCK_ID[room]].value;
const userfn = entry.data[USERFN_ID[room]].value;
if (USERLN_ID[room]) {
userln = entry.data[USERLN_ID[room]].value;
};
// Join first and last if last exist
const usern = [userfn, userln].filter(Boolean).join(" ");
// assemble message
// title with user name and date
message = `*${usern}* - ${datefield}`;
// title section Yesterday with user data below
message += `\n\t*_Previous Stand Up:_*\n${CleanTxt(yday)}`;
// title section today with user data below
message += `\n\t*_Today:_*\n${CleanTxt(tday)}`;
// skip blocker section if empty
if (block !== "Nothing") {
message += `\n\t*_Blockers:_*\n${CleanTxt(block)}`;
};
callback(message);
};
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
}
return array;
}
// ---- Form data Report for all users today ----
function ReportStandup(room, CronRun) {
let entry, message, messgaearray = [];
robot.logger.info(`standup-formstack-cron: Running standard Standup report to room ${room}`);
// Get dates needed
const dates = CalcDate(room);
const dateformat = dates[0];
const mindate = dates[1];
// formstack url with form ID, token (oauth_token) and date range filter (min_time)
const fsurl = `${FSAPIURL[room]}/submission.json?data=true&expand_data=false&min_time=${encodeURI(mindate)}&oauth_token=${FS_TOKEN}`;
// Callback return function of pased json from url
GetFormSubData(room, fsurl, (jdata) => {
// loop filtered submissions
for (entry of jdata.submissions) {
// get date in form
const datefield = entry.data[DATEFIELD_ID[room]].value;
// Parse submissions and match for today
if (datefield === dateformat) {
FormatClean(room, entry, (clnmessage) => {
// Save data to var in higher scope
messgaearray.push(clnmessage);
});
};
};
// post Funny message if no results are found
if (messgaearray.length === 0 && CronRun) {
const gone = [
"Sooooo... Is everyone on holiday?",
"Nothing? Was it something I said?",
"Do you wanna build a snowman?... It doesn't have to be snowman... OK, bye",
":notes:Here I go again on my own!:notes:\n\tGoing down the only road I've ever know!:notes:",
"Bueller? Bueller?... Bueller?....... Bueller?",
"https://media.giphy.com/media/jNH0Bto1xBNwQ/giphy.gif",
"Today was a day off wasn't it?... I wish I had a day off",
"Great! I'm going back to sleep",
`:rotating_light: ${robot.name} dance party!! :rotating_light: \n\thttps://media.giphy.com/media/v0YiARQxj1yc8/giphy.gif`,
`*${robot.name} * - ${dateformat}\n\t*_Previous Stand Up:_*\n\t\- Report Standup\n\t\- Answer Questions\n\t\- Other duties as assigned\n\t*_Today:_*\n\t\- Report Standup\n\t\- Answer Questions\n\t\- Other duties as assigned\n\t*_Blockers:_*\n\t\- No one is here`
];
robot.messageRoom(room, gone[Math.floor(Math.random()*gone.length)]);
};
// Randomize Post order
if (RANDOMIZE[room]){
shuffle(messgaearray);
}
for (message of messgaearray) {
robot.messageRoom(room, message);
};
});
};
// ---- Form data Report for single user ----
function SingleReport(room, user) {
let message, messageList = [];
robot.logger.info(`standup-formstack-cron: Running single Standup report to room ${room}`);
// Get dates needed
const dates = CalcDate(room);
const dateformat = dates[0];
const mindate = dates[1];
const fsurl = `${FSAPIURL[room]}/submission.json?data=true&expand_data=false&min_time=${encodeURI(mindate)}&oauth_token=${FS_TOKEN}`;
// Callback return function of pased json from url
GetFormSubData(room, fsurl, (jdata) => {
// loop filtered submissions
for (entry of jdata.submissions) {
let userfn, datefield, userln;
userfn = entry.data[USERFN_ID[room]].value;
datefield = entry.data[DATEFIELD_ID[room]].value;
if (USERLN_ID[room]) {
userln = entry.data[USERLN_ID[room]].value;
};
// Join first and last if last exist
const usern = [userfn, userln].filter(Boolean).join(" ");
// build array of usernames for today
if (datefield === dateformat) {
// look up user
if (usern.toLowerCase() === user.toLowerCase()
|| userfn.toLowerCase() === user.toLowerCase()
|| userln.toLowerCase() === user.toLowerCase()) {
FormatClean(room, entry, (rtrnmessage) => {
messageList.push(rtrnmessage);
});
};
};
};
message = messageList.join("\n");
if (!message) {
message = `I'm not able to find a report for ${user}, but\n `;
FilledItOut(room);
};
robot.messageRoom(room, message);
});
};
// ---- List users who filled out report today ----
function FilledItOut(room) {
let message, users = [];
robot.logger.info(`standup-formstack-cron: Running list of users that filled out report to room ${room}`);
// Get dates needed
const dates = CalcDate(room);
const dateformat = dates[0];
const mindate = dates[1];
const fsurl = `${FSAPIURL[room]}/submission.json?data=true&expand_data=false&min_time=${encodeURI(mindate)}&oauth_token=${FS_TOKEN}`;
// Callback return function of pased json from url
GetFormSubData(room, fsurl, (jdata) => {
// loop filtered submissions
for (entry of jdata.submissions) {
let userfn, datefield, userln;
userfn = entry.data[USERFN_ID[room]].value;
datefield = entry.data[DATEFIELD_ID[room]].value;
if (USERLN_ID[room]) {
userln = entry.data[USERLN_ID[room]].value;
};
// Join first and last if last exist
const usern = [userfn, userln].filter(Boolean).join(" ");
// build array of usernames for today
if (datefield === dateformat) {
users.push(usern);
};
};
// comma seperate users array and replace last comma with "and"
const userList = users.join(", ").replace(/, ([^,]+)$/, ' and $1') || "No one";
// set message and grammer to fit results
if (users.length <= 1) {
// message for 1 user
message = `${userList} has filled out the report for today`;
} else {
// message for 2+ users
message = `${userList} have filled out the report for today`;
};
// send message to room
robot.messageRoom(room, message);
});
};
function HelpReply(room) {
let message = "";
robot.logger.info(`standup-formstack-cron: Displaying Help to room ${room}`);
if (ONHEAR) {
message += `You can @${robot.name} or I'll listen for *${PREFIX}standup*\n`;
};
message += `${robot.name} ${PREFIX}standup - List results of standup form for today\n`;
message += `${robot.name} ${PREFIX}standup today - List who has filled out the standup form\n`;
message += `${robot.name} ${PREFIX}standup <USERNAME> - List results of standup form for today\n`;
message += `${robot.name} ${PREFIX}standup randomize - Randomize the list of all results of standup form`
message += `${robot.name} ${PREFIX}standup remove - Remove a form from a room\n`;
message += `${robot.name} ${PREFIX}standup setup FORMID TIME REMINDER CRONDAYS - Setup the script for the first time\n`;
message += `\tFORMID - Formstack Form ID\n`;
message += `\tTIME - Time of auto post (8:00am or 14:00)\n`;
message += `\tREMINDER - Number of minutes before to send reminder (15) Default 30\n`;
message += `\tCRONDAYS - Days to post in cron format (1-5 or 0,1,2,3) 0 = Sunday. Default 1-5 (weekdays)\n`;
message += `\tReminder and crondays can be skipped to accept defaults`;
robot.messageRoom(room, message);
};
};