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 {
2020-11-13 00:28:46 +00:00
const result = await app . client . conversations . history ( {
token : token ,
channel : id ,
2020-12-31 01:26:22 +00:00
limit : 100 ,
2020-11-13 00:28:46 +00:00
} ) ;
2020-12-31 01:26:22 +00:00
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 {
2020-11-13 00:28:46 +00:00
const result = await app . client . conversations . history ( {
token : token ,
channel : id ,
2020-12-31 01:26:22 +00:00
oldest : Math . floor ( Date . now ( ) / 1000 ) - 86400 , //debug: 1609295166, actual: Math.floor(Date.now() / 1000) - 86400
inclusive : false ,
2020-11-13 00:28:46 +00:00
} ) ;
2020-12-31 01:26:22 +00:00
let number ;
2023-02-22 00:13:35 +00:00
for ( let x = result . messages . length - 2 ; x >= 0 ; x -- ) {
2020-12-31 01:26:22 +00:00
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 ( {
2020-10-19 04:15:19 +00:00
token : token ,
channel : id ,
text : text
2020-11-13 00:28:46 +00:00
} ) ;
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 )
}
}
2020-08-18 01:15:00 +00:00
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 ;
}
2020-08-18 01:15:00 +00:00
} )
}
2020-12-31 00:33:50 +00:00
async function getAverage ( ) {
2020-08-18 01:15:00 +00:00
try {
2020-12-31 00:33:50 +00:00
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 ;
2020-08-18 01:15:00 +00:00
} catch ( error ) {
2020-12-31 00:33:50 +00:00
console . error ( error ) ;
2020-08-18 01:15:00 +00:00
}
}
2020-11-13 00:28:46 +00:00
async function report ( ) {
2023-09-04 00:18:06 +00:00
console . log ( "Writing daily report..." ) ;
2020-08-18 01:15:00 +00:00
let oldest = await fetchOldest ( channel ) ;
2020-09-29 00:13:31 +00:00
let latest = await fetchLatest ( channel ) ;
2020-08-18 01:15:00 +00:00
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 ;
2021-02-18 21:36:41 +00:00
let goals = predictSpeed ( goalDate , goalNumber , latest ) ;
2020-08-14 22:31:55 +00:00
let message =
2021-02-18 21:36:41 +00:00
` Today we've went from * ${ oldest } * to * ${ latest } *!
2020-12-10 00:15:45 +00:00
- : arrow _upper _right : The day ' s progress : * + $ { diff } *
2020-12-31 00:33:50 +00:00
- : chart _with _upwards _trend : Average daily speed : * $ { Math . round ( averageSpeed ) } *
2021-02-18 21:36:41 +00:00
- : 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 ] ) ) } * ` ;
2020-09-29 00:13:31 +00:00
if ( pastThousandsGoal > oldest && pastThousandsGoal <= latest ) {
2021-02-18 21:36:41 +00:00
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 {
2021-02-18 21:36:41 +00:00
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-18 01:15:00 +00:00
} ;
2020-08-14 22:31:55 +00:00
2021-02-18 21:36:41 +00:00
function predictSpeed ( goalDate , goalNumber , currentNumber ) {
let today = new Date ( ) ;
let goal = new Date ( goalDate ) ;
2024-08-16 01:29:20 +00:00
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 ) ) ;
2024-08-16 01:29:20 +00:00
}
let neededSpeed = ( goalNumber - currentNumber ) / Math . abs ( daysRemaining ) ;
2021-02-18 21:36:41 +00:00
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
} ) ( ) ;