million-stats/api/app.js

214 lines
5 KiB
JavaScript

// Require the Bolt package (github.com/slackapi/bolt)
const { App } = require("@slack/bolt");
const keep_alive = require('./keep_alive.js')
const schedule = require('node-schedule');
const moment = require('moment');
const token = process.env.SLACK_BOT_TOKEN;
const channel = "CDJMS683D";
const Airtable = require('airtable');
Airtable.configure({
endpointUrl: 'https://api.airtable.com',
apiKey: process.env.AIRTABLE_API_KEY
});
const base = Airtable.base('appogmRaVRo5ElVH7');
let speedArr = [];
let latest;
let averageSpeed;
const app = new App({
token: token,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
function extractNumber(txt) {
let array = ["\n", " ", "-"]
for (let i of array) {
if (txt.includes(i)) {
return txt.split(i)[0]
}
}
return txt;
}
async function fetchLatest(id) {
try {
const result = await app.client.conversations.history({
token: token,
channel: id,
limit: 1,
});
const number = extractNumber(result.messages[0].text)
return number;
} catch (error) {
console.error(error);
}
}
async function fetchOldest(id) {
try {
let last24Hrs;
const result = await app.client.conversations.history({
token: token,
channel: id,
oldest: Math.floor(Date.now() / 1000) - 86400, //debug: 1596326400, actual: Math.floor(Date.now() / 1000) - 86400
inclusive: false
});
const number = extractNumber(result.messages[result.messages.length - 2].text);
return number - 1;
} catch (error) {
console.error(error);
}
}
async function publishMessage(id, text) {
try {
const result = await app.client.chat.postMessage({
token: token,
channel: id,
text: text
});
} catch (error) {
console.error(error);
}
}
async function postReaction(id, emoji, ts) {
try {
const result = await app.client.reactions.add({
token: token,
channel: id,
name: emoji,
timestamp: ts
});
} catch (error) {
console.error(error)
}
}
async function pinMessage(id, ts) {
try {
const result = await app.client.pins.add({
token: token,
channel: id,
timestamp: ts
})
} catch (error) {
console.error(error)
}
}
function findMean(arr) {
let totalSum = 0;
for (let i of arr) {
totalSum += i;
}
return totalSum / arr.length;
}
function predictTime(goal) {
let daysLeft = (goal - latest) / averageSpeed
let unix = new Date(Date.now() + daysLeft * 86400000)
return moment(unix).fromNow();
}
async function addData(db, object) {
base(db).create(object, function(err, record){
if (err) {
console.error(err);
return;
}
// console.log(record.getId());
})
}
async function getStats() {
try {
let obj = await base('stats').find('rec2XI8QAsPr7EMVB');
return {
id: obj.id,
fields: obj.fields,
};
} catch (error) {
console.error(error)
}
}
async function report() {
let oldest = await fetchOldest(channel);
let latest = await fetchLatest(channel);
let diff = latest - oldest;
addData('increase', {
"Date": moment().subtract(1, "days").format("YYYY-MM-DD"),
"increase": diff,
"stats": [
"rec2XI8QAsPr7EMVB"
]
})
let newStats = await getStats();
console.log(newStats.fields);
averageSpeed = newStats.fields.average.toFixed(3);
let thousandsGoal = Math.ceil(latest / 1000) * 1000;
let thousandsTime = predictTime(thousandsGoal)
let tenThousandsGoal = Math.ceil(latest / 5000) * 5000;
let pastThousandsGoal = Math.floor(latest / 1000) * 1000;
let tenThousandsTime = predictTime(tenThousandsGoal)
let message =
"Nice! Today we've went from *" +
oldest +
"* to *" +
latest +
"*! \n - :arrow_upper_right: The day's progress: *+" +
diff +
"*\n - :chart_with_upwards_trend: Average daily speed: *" +
averageSpeed +
"*\n - :round_pushpin: At the avg speed, we'll reach " +
thousandsGoal +
" *" +
thousandsTime +
"*\n - :calendar: At the avg speed, we'll reach " +
tenThousandsGoal +
" *" +
tenThousandsTime +
"* \n :fastparrot: KEEP IT GOING GUYS!";
if (pastThousandsGoal > oldest && pastThousandsGoal <= latest) {
let messageWithCelebration = ":tada: YAY! We've went past " + pastThousandsGoal + "! :tada: \n" + message;
publishMessage(channel, messageWithCelebration); //'C017W4PHYKS' for debugging, channel for actual
} else {
publishMessage(channel, message); //'C017W4PHYKS' for debugging, channel for actual
}
};
app.event('message', async (body) => {
try {
let e = body.event;
if (typeof e.subtype === "undefined" && /\d/.test(e.text[0])) {
let number = extractNumber(e.text);
let ts = e.ts;
let c = e.channel;
if (number % 1000 === 0) {
postReaction(c, "tada", ts);
}
if (number % 5000 === 0) {
pinMessage(c, ts);
}
let l = number.length;
if (number[l - 2] == 6 && number[l - 1] == 9) {
postReaction(c, "ok_hand", ts);
}
}
} catch (err) {
console.error(err);
}
});
(async (req, res) => {
// Start your app
try {
await app.start(process.env.PORT || 3000);
let j = schedule.scheduleJob('0 0 * * *', report); // */15 * * * * * for debugging, 0 0 * * * actual
publishMessage('C017W4PHYKS', 'running every midnight!')
} catch (error) {
console.error(error);
}
})();