million-stats/api/app.js

247 lines
6.2 KiB
JavaScript
Raw Permalink Normal View History

2020-08-14 22:31:55 +00:00
const { App } = require("@slack/bolt");
2020-12-10 00:15:45 +00:00
const addQuotes = require('./quotes.js');
2020-08-14 22:31:55 +00:00
const schedule = require('node-schedule');
const moment = require('moment');
const Airtable = require('airtable');
2023-09-04 00:18:06 +00:00
require('dotenv').config();
2020-08-14 22:31:55 +00:00
Airtable.configure({
endpointUrl: 'https://api.airtable.com',
apiKey: process.env.AIRTABLE_API_KEY
});
2023-09-04 00:18:06 +00:00
const base = Airtable.base(process.env.AIRTABLE_BASE_ID);
2020-08-14 22:31:55 +00:00
2023-09-04 00:18:06 +00:00
const token = process.env.SLACK_BOT_TOKEN;
const channel = process.env.SLACK_MILLION_CHANNEL;
const port = process.env.PORT ?? 3000;
2024-11-08 00:19:02 +00:00
const goalDate = '2/1/2025';
2023-02-22 00:13:35 +00:00
const goalNumber = 300000;
2020-12-10 00:15:45 +00:00
2020-08-14 22:31:55 +00:00
const app = new App({
token: token,
signingSecret: process.env.SLACK_SIGNING_SECRET
});
function extractNumber(txt) {
2024-08-16 01:00:12 +00:00
let array = ["-", " ", "\n"]
2020-08-14 22:31:55 +00:00
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: 100,
});
let number;
for (let x = 0; x < result.messages.length; x++) {
number = extractNumber(
result.messages[x].text,
);
if (!isNaN(number)) break;
}
2020-08-14 22:31:55 +00:00
return number;
} catch (error) {
console.error(error);
}
}
async function fetchOldest(id) {
try {
const result = await app.client.conversations.history({
token: token,
channel: id,
oldest: Math.floor(Date.now() / 1000) - 86400, //debug: 1609295166, actual: Math.floor(Date.now() / 1000) - 86400
inclusive: false,
});
let number;
2023-02-22 00:13:35 +00:00
for (let x = result.messages.length - 2; x >= 0; x--) {
number = extractNumber(
result.messages[x].text,
);
if (!isNaN(number)) break;
}
2020-08-14 22:31:55 +00:00
return number - 1;
} catch (error) {
console.error(error);
}
}
async function publishMessage(id, text) {
try {
2023-09-04 00:18:06 +00:00
await app.client.chat.postMessage({
token: token,
channel: id,
text: text
});
2020-08-14 22:31:55 +00:00
} catch (error) {
console.error(error);
}
}
async function postReaction(id, emoji, ts) {
try {
2023-09-04 00:18:06 +00:00
await app.client.reactions.add({
2020-08-14 22:31:55 +00:00
token: token,
channel: id,
name: emoji,
timestamp: ts
});
} catch (error) {
console.error(error)
}
}
async function pinMessage(id, ts) {
try {
2023-09-04 00:18:06 +00:00
await app.client.pins.add({
2020-08-14 22:31:55 +00:00
token: token,
channel: id,
timestamp: ts
})
} catch (error) {
console.error(error)
}
}
async function addData(db, object) {
2023-02-22 00:13:35 +00:00
base(db).create(object, function(err, record) {
2020-08-17 23:15:49 +00:00
if (err) {
console.error(err);
return;
}
})
}
async function getAverage() {
try {
const obj = await base('increase')
.select({
maxRecords: 30,
sort: [{ field: 'Date', direction: 'desc' }]
})
.firstPage();
let sum = 0;
obj.forEach((item) => (sum += item.fields.increase));
2023-09-04 00:18:06 +00:00
return sum / obj.length;
} catch (error) {
console.error(error);
}
}
async function report() {
2023-09-04 00:18:06 +00:00
console.log("Writing daily report...");
let oldest = await fetchOldest(channel);
let latest = await fetchLatest(channel);
let diff = latest - oldest;
2020-11-13 00:30:43 +00:00
addData('increase', {
"Date": moment().subtract(1, "days").format("YYYY-MM-DD"),
"increase": diff,
2023-02-22 00:13:35 +00:00
"start": oldest,
2020-11-13 00:30:43 +00:00
})
2024-11-09 00:04:19 +00:00
let averageSpeed = Math.max(0, await getAverage());
2020-08-14 22:31:55 +00:00
let pastThousandsGoal = Math.floor(latest / 1000) * 1000;
let goals = predictSpeed(goalDate, goalNumber, latest);
2020-08-14 22:31:55 +00:00
let message =
`Today we've went from *${oldest}* to *${latest}*!
2020-12-10 00:15:45 +00:00
- :arrow_upper_right: The day's progress: *+${diff}*
- :chart_with_upwards_trend: Average daily speed: *${Math.round(averageSpeed)}*
- :round_pushpin: Our current goal is to reach *${goalNumber}* by *${moment(goalDate).format('MMMM D')}.*
2021-03-22 02:01:48 +00:00
- :calendar: If we want to get there on time, we need to count by at least *+${Math.ceil(goals[1])}* a day.
- :1234: Here's a number to aim for today: *${Math.ceil(parseInt(latest) + parseInt(goals[1]))}*`;
if (pastThousandsGoal > oldest && pastThousandsGoal <= latest) {
let messageWithCelebration = `:tada: Congratulations! We've went past ${pastThousandsGoal}! :tada: \n` + message;
2023-02-22 00:13:35 +00:00
publishMessage(channel, addQuotes(messageWithCelebration, goals, averageSpeed));
2020-08-17 23:15:49 +00:00
} else {
publishMessage(channel, addQuotes(message, goals, averageSpeed));
2020-08-17 23:15:49 +00:00
}
2023-09-04 00:18:06 +00:00
console.log("Sent daily report.");
};
2020-08-14 22:31:55 +00:00
function predictSpeed(goalDate, goalNumber, currentNumber) {
let today = new Date();
let goal = new Date(goalDate);
let timeRemaining = goal - today;
let daysRemaining
if (timeRemaining >= 0) {
daysRemaining = Math.ceil(timeRemaining / (1000 * 60 * 60 * 24));
2024-08-16 02:12:16 +00:00
} else {
2024-10-01 02:38:15 +00:00
daysRemaining = Math.floor(timeRemaining / (1000 * 60 * 60 * 24));
}
let neededSpeed = (goalNumber - currentNumber) / Math.abs(daysRemaining);
return [daysRemaining, neededSpeed];
}
2020-08-14 22:31:55 +00:00
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);
}
2021-03-22 02:01:48 +00:00
if (number.slice(-2) === '69') {
2020-08-14 22:31:55 +00:00
postReaction(c, "ok_hand", ts);
}
2021-03-22 02:01:48 +00:00
if (number.slice(-3) === '666') {
postReaction(c, "smiling_imp", ts)
2021-05-11 20:54:28 +00:00
} if (number.slice(-3) === number.slice(0, 3).split("").reverse().join("")) {
postReaction(c, "tacocat", ts)
2021-03-22 02:01:48 +00:00
}
2020-08-14 22:31:55 +00:00
}
} catch (err) {
console.error(err);
}
});
2021-03-22 02:20:13 +00:00
app.event('app_mention', async (body) => {
try {
let e = body.event;
let c = e.channel;
2023-02-22 00:13:35 +00:00
let choose = Math.floor(Math.random() * 8);
let messageArray = [
"DO NOT BOTHER ME. I AM SLEEPING.",
"AAAAAAAA!!! THE SUN! *pulls curtains closed* I nearly got _burnt_ that time, you pathetic little minions! Next time, DO NOT WAKE ME.",
"What do you want, human weakling?",
"Hrmh? Is Count von Corgo pissing on the lawn _again_?",
"What is it? Are you going too slow that you need another supernatural being to help you _speed-count_? If so, you've found the wrong one, because this supernatural being is _trying to sleep!_",
"HISSSSSSSSSSS!",
"Minions, I had _three hours_ of sleep yesterday, and I am trying to catch up. Please, _leave me alone to sleep._",
2023-09-04 00:18:06 +00:00
];
2023-02-22 00:13:35 +00:00
2023-09-04 00:18:06 +00:00
publishMessage(c, messageArray[choose]);
console.log("App mentioned.");
2021-03-22 02:20:13 +00:00
} catch (err) {
console.error(err)
}
});
2023-09-04 00:18:06 +00:00
(async () => {
2020-08-14 22:31:55 +00:00
try {
2023-09-04 00:18:06 +00:00
await app.start(port);
schedule.scheduleJob('0 0 * * *', report);
console.log(`Started bot, listening on port ${port}`)
2020-08-14 22:31:55 +00:00
} catch (error) {
console.error(error);
}
2024-08-16 02:12:16 +00:00
})();