Implement reminders for house maintenance
authorCameron Ball <cameron@cameron1729.xyz>
Tue, 27 Nov 2018 07:41:36 +0000 (15:41 +0800)
committerCameron Ball <cameron@cameron1729.xyz>
Wed, 19 Dec 2018 03:48:37 +0000 (11:48 +0800)
.gitignore
common.php
purjolok.php
strings.php
taskMatrix.php [new file with mode: 0644]
tasks.php [new file with mode: 0644]
unfinished.php [new file with mode: 0644]

index 65216c5..17d2f3f 100644 (file)
@@ -1,2 +1,3 @@
 config.php
 .ac-php-conf.json
+tasks
index 904c28a..1d996ef 100644 (file)
@@ -18,6 +18,11 @@ function identity($x) {
     return $x;
 }
 
+const notEmpty = 'notEmpty';
+function notEmpty($value) {
+    return !empty($value);
+}
+
 function getMessageSender($update) {
     return PARTICIPANT_IDS[$update->get('message')->get('from')->get('id')];
 }
@@ -36,6 +41,17 @@ function debug($whatever) {
     echo '</pre>';
 }
 
+function partition(int $numPartitions, $array) {
+    $partitionSize = (int)ceil(count($array) / $numPartitions);
+
+    return
+        filter(notEmpty)(
+            map(function($p) use ($array, $partitionSize) {
+                return array_slice($array, $p*$partitionSize, $partitionSize);
+            })(range(0, $numPartitions-1))
+        );
+}
+
 function getInbox($inbox) {
     STATIC $inboxes;
 
@@ -55,11 +71,12 @@ function getRules() {
     return $rules = $rules ?? require 'rules.php';
 }
 
+const getString = 'getString';
 function getString($identifier, ...$vars) {
     STATIC $strings;
     $strings = $strings ?? require 'strings.php';
 
-    return sprintf($strings[$identifier], ...$vars);
+    return isset($strings[$identifier]) ? sprintf($strings[$identifier], ...$vars) : "[[$identifier]]";
 }
 
 function formatDate($date) {
@@ -116,6 +133,16 @@ function unlines($lines) {
     return implode("\n", $lines);
 }
 
+const ununlines = 'ununlines';
+function ununlines($lines) {
+    return implode("\n\n", $lines);
+}
+
+const zipWith = 'zipWith';
+function zipWith(callable $zipper, array $a, array $b) {
+    return array_map($zipper, $a, $b);
+}
+
 function field($field) {
     return function($array) use ($field) {
         return $array[$field];
@@ -184,6 +211,71 @@ function ∪($a, $b) {
     return array_merge($a, $b);
 }
 
+function getSeason($monthNum) {
+    return ['summer', 'autumn', 'winter', 'spring'][floor(($monthNum)%12/3)];
+}
+
+function getMonthName($monthNum) {
+    return ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'][$monthNum-1];
+}
+
+function isStartOfSeason($monthNum, $dayNum) {
+    return ($monthNum)%3 == 0 && isStartOfMonth($dayNum);
+}
+
+function isStartOfMonth($dayNum) {
+    return $dayNum < 8;
+}
+
+function getTasksForTheSeason($season, $taskMatrix) {
+    return array_unique(
+        array_reduce(
+            $taskMatrix['annualy'][$season],
+            function($c, $v) {
+                return array_merge(
+                    $c,
+                    array_reduce($v, function($c, $v) {
+                        return array_merge($c, is_array($v) ? $v : [$v]);
+                    }, [])
+                );
+            },
+            []
+        )
+    );
+}
+
+function getTasksForTheMonth($monthNum, $taskMatrix) {
+    return array_merge(
+        $taskMatrix['monthly'],
+        $monthNum % 6 == 0 ? $taskMatrix['biannualy'] : [],
+        $monthNum % 3 == 0 ? $taskMatrix['quadriannualy'] : [],
+        array_filter(
+            $taskMatrix['annualy'][getSeason($monthNum)][getMonthName($monthNum)],
+            function($v) {
+                return !is_array($v);
+            }
+        )
+    );
+}
+
+// NB weeknum is 1-4 (the week of the month, not consistent with other things)
+function getTasksForTheWeek($weekNum, $monthNum, $taskMatrix) {
+    return  array_merge(
+        $weekNum % 2 == 0 ? $taskMatrix['bimonthly'] : [],
+        $taskMatrix['annualy'][getSeason($monthNum)][getMonthName($monthNum)]['weekly'] ?? [],
+        partition(4, getTasksForTheMonth($monthNum, $taskMatrix))[$weekNum]
+    );
+}
+
+function closest($n, $list) {
+    $a = array_filter($list, function($value) use ($n) {
+        return $value <= $n;
+    });
+
+    arsort($a);
+    return array_values($a)[0];
+}
+
 function getMessagesFromInbox($inbox, array $rules, $unseenOnly = true) {
     return array_filter(
         array_map(
index eeb2e6d..395f41d 100644 (file)
@@ -118,4 +118,105 @@ getTelegram()->addCommand(
 
 );
 
+getTelegram()->addCommand(
+    new class extends Command {
+        protected $name = 'tasks';
+        protected $description = 'List tasks for this week';
+
+        public function handle($arguments) {
+            $dt = new DateTimeImmutable();
+            $directory = sprintf(
+                "tasks/%s/%s/%s",
+                $dt->format('Y'),
+                $dt->format('F'),
+                $dt->format('W')
+            );
+
+            $mondays = [
+                (int)(new DateTimeImmutable('first monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('second monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('third monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('fourth monday of this month'))->format('d'),
+            ];
+            $closestMonday = closest($dt->format('d'), $mondays);
+
+            $tasksForTheWeek = getTasksForTheWeek(
+                array_search($closestMonday, $mondays),
+                $dt->format('m'),
+                require 'taskMatrix.php'
+            );
+
+            $completedTasksFile = "$directory" . "/completed.txt";
+            $completedTasks = file_exists($completedTasksFile) ? lines(trim(file_get_contents($completedTasksFile))) : [];
+
+            $stringAndCode = function($string) {
+                return getString($string) . " (" . $string . ")";
+            };
+
+            $this->replyWithMessage([
+                'text' => ununlines([
+                    getString('tasksForTheWeek'),
+                    unlines(map($stringAndCode)(array_diff($tasksForTheWeek, $completedTasks)))
+                ])
+            ]);
+        }
+    }
+);
+
+getTelegram()->addCommand(
+    new class extends Command {
+        protected $name = 'completetask';
+        protected $description = 'Mark a task as completed';
+
+        public function handle($arguments) {
+            $dt = new DateTimeImmutable();
+            $mondays = [
+                (int)(new DateTimeImmutable('first monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('second monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('third monday of this month'))->format('d'),
+                (int)(new DateTimeImmutable('fourth monday of this month'))->format('d'),
+            ];
+            $closestMonday = closest($dt->format('d'), $mondays);
+
+            $directory = sprintf(
+                "tasks/%s/%s/%s",
+                $dt->format('Y'),
+                $dt->format('F'),
+                $dt->format('W')
+            );
+
+            $tasksForTheWeek = getTasksForTheWeek(
+                array_search($closestMonday, $mondays),
+                $dt->format('m'),
+                require 'taskMatrix.php'
+            );
+
+            $completedTasksFile = "$directory" . "/completed.txt";
+            $completedTasks = file_exists($completedTasksFile) ? lines(trim(file_get_contents($completedTasksFile))) : [];
+
+            if (!is_dir($directory)) {
+                mkdir($directory, 0777, true);
+            }
+
+            if (in_array($arguments, $completedTasks)) {
+                $this->replyWithMessage(['text' => getString('taskAlreadyCompleted')]);
+                return;
+            }
+
+            if (!in_array($arguments, $tasksForTheWeek)) {
+                $this->replyWithMessage(['text' => getString('unknownTask')]);
+                return;
+            }
+
+            file_put_contents(
+                $completedTasksFile,
+                "$arguments\n",
+                FILE_APPEND
+            );
+
+            $this->replyWithMessage(['text' => getString('taskCompleted')]);
+        }
+    }
+);
+
 getTelegram()->commandsHandler(true); //must come afterwards because lolzer
index 8f41824..a4e03c1 100644 (file)
@@ -7,5 +7,58 @@ return [
     'drinksomewater' => '🚰 Consider drinking some water! 💦 #HydrationNation',
     'goodnightWearShorts' => 'I\'m signing off for the night. Before I go; tomorrow it\'s going to be about %s when you get up and %s later in the day. So consider wearing summer clothes. ☀️☀️☀️',
     'goodnightNormal' => 'I\'m signing off for the night. Before I go; tomorrow it\'s going to be about %s when you get up and %s later in the day. Jumper and trousers, maybe?',
-    'goodnight' => 'Goodnight!'
+    'goodnight' => 'Goodnight!',
+
+    'beginningOfSummer' => 'It\'s the start of summer! To get you prepared, here are the unique things that will happen this season (in addition to regular weekly(ish) shit)...',
+    'beginningOfWinter' => 'It\'s the start of winter (at last, fuck summer). Heads up lads, here are the unique tasks that are wanting doing this season (in addition to regular junk)...',
+
+    'beginningOfDecember' => 'Welcome to December! Oh boy!',
+    'beginningOfJanuary' => 'Welcome to January! Oh boy.',
+
+    'anywayHeresTheMonth' => 'Anyway, here\'s the normal start of the month breakdown...',
+    'anywayHeresTheWeek' => 'Anyway, like normal here\'s some stuff I think you guys should do this week...',
+    'finallyHeresTheWeek' => 'And finally... Like always, here\'s some stuff I think you guys should do this week:',
+    'happyMonday' => 'Happy Monday! Here are some tasks I think should get nutted out this week:',
+
+    'thisMonthThereAre' => 'This month there are %d tasks that need completing (as well as the regular tasks). I\'ll try my best to space them out over the coming weeks. As a heads up, here they are:',
+    'seasonalMeme' => 'Please also enjoy this seasonal meme I picked for you: %s',
+    'tasksForTheWeek' => 'Here are the incomplete tasks for this week...',
+    'taskAlreadyCompleted' => 'Hmm... It looks as though that task was already done this week.',
+    'taskCompleted' => 'Marked as completed, nice one!',
+    'unknownTask' => 'That doesn\'t look like a task I asked you to do this week... Use the listtasks command to see all the tasks.',
+
+    'rangehoodfilters' => 'Clean the rangehood filters in the kitchen',
+    'poolwater' => 'Check the level of the pool, and fill if needed',
+    'weeds' => 'Inspect pavers for weeds (front and back), and spray/pull out as needed',
+    'ants' => 'Inspect the pavers for ant hills and apply sand/back fill as needed',
+    'leaves' => 'Sweep up leaves in the front yard',
+    'cobwebs' => 'Inspect the exterior of the house for cobwebs (in the windows and shit)',
+    'fireextinguisher' => 'Inspect fire extinguisher',
+    'mowlawn' => 'Mow the lawn',
+    'vacuum' => 'Vacuum the house',
+    'mop' => 'Mop the tiles',
+    'kitchen' => 'Clean the kitchen (wipe all surfaces/cupboards and clean the stove)',
+    'cleanhouse' => 'Do a big cleanout of the house',
+    'cleangarage' => 'Do a big cleanout of the garage',
+    'testreliefevalve' => 'Test the relief valves on the water heater',
+    'testsmokealarms' => 'Test smoke alarms',
+    'smokealarmbatteries' => 'Replace smoke alarm batteries',
+    'fridgecoils' => 'Clean the fridge coils',
+    'chlorinatorplates' => 'Inspect and descale chlorinator if needed',
+    'treeflowers' => 'Clean up the flowers from that stupid tree next door',
+    'bulkgreens' => 'Prepare for bulk greens collection',
+    'windowdebris' => 'Clean junk out of windows (inside and outside)',
+    'cleangutters' => 'Clean out gutters to prepare for rain',
+    'checkdrains' => 'Check down pipe drains for blockages',
+    'coverpool' => 'Cover the pool',
+    'cleanchimeny' => 'Clean soot out of the chimney',
+    'tightenfixings' => 'Inspect and tighten (if needed) any fixings',
+    'fanfilters' => 'Clean extractor fan filters (bathroom and air conditioner)',
+    'bulkrubbish' => 'Prepare for bulk rubbish collection',
+    'caulking' => 'Inspect and repair caulking',
+    'showerheads' => 'Inspect and descale showerheads and taps',
+    'shrubs' => 'Prune shrubs',
+    'palmtrees' => 'Remove dead branches and seeds from palm trees',
+    'uncoverpool' => 'Uncover the pool',
+    'checktaps' => 'Inspect taps for leaks and repair as needed'
 ];
diff --git a/taskMatrix.php b/taskMatrix.php
new file mode 100644 (file)
index 0000000..408e845
--- /dev/null
@@ -0,0 +1,47 @@
+<?php declare(strict_types=1);
+
+// look in to termites cibtract
+
+return [
+    'monthly' => ['rangehoodfilters', 'poolwater', 'weeds', 'ants', 'leaves', 'cobwebs', 'fireextinguisher', 'mowlawn'],
+    'bimonthly' => ['vacuum', 'mop', 'kitchen'],
+    'biannualy' => ['cleanhouse', 'cleangarage', 'testreliefevalve', 'smokealarmstest', 'smokealarmbatteries', 'fridgecoils'],
+    'quadriannualy' => ['chlorinatorplates'],
+    'annualy' => [
+        'summer' => [
+            'december' => [
+                'weekly' => ['treeflowers']
+            ],
+            'january' => [
+                'bulkgreens',
+                'weekly' => ['treeflowers']
+            ],
+            'february' => []
+        ],
+        'autumn' => [
+            'march' => ['windowdebris'],
+            'april' => ['cleangutters', 'checkdrains'],
+            'may' => ['coverpool', 'cleanchimney']
+        ],
+        'winter' => [
+            'june' => ['tightenfixings', 'fanfilters'],
+            'july' => ['bulkrubbish'],
+            'august' => ['caulking', 'showerheads']
+        ],
+        'spring' => [
+            'september' => ['shrubs'],
+            'october' => ['palmtrees', 'uncoverpool'],
+            'november' => ['checktaps']
+        ]
+    ]
+];
+
+/*
+  clean computer files
+  clean car
+  vacuum bedroom
+  change bed sheets
+  clean bathroom
+  dust bathroom
+  clean shower
+*/
diff --git a/tasks.php b/tasks.php
new file mode 100644 (file)
index 0000000..b03ff5a
--- /dev/null
+++ b/tasks.php
@@ -0,0 +1,61 @@
+<?php declare(strict_types=1);
+
+require_once('common.php');
+
+$taskMatrix = require 'taskMatrix.php';
+$mondays = [
+    (int)(new DateTimeImmutable('first monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('second monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('third monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('fourth monday of this month'))->format('d'),
+];
+$currentMonth = (new DateTimeImmutable())->format('m');
+$currentDayOfMonth = closest((new DateTimeImmutable())->format('d'), $mondays);
+$currentWeekOfMonth = array_search($currentDayOfMonth, $mondays);
+
+$taskLists = array_merge(
+    isStartOfSeason($currentMonth, $currentDayOfMonth) ? [unlines(map(getString)(getTasksForTheSeason(getSeason($currentMonth), $taskMatrix)))] : [],
+    isStartOfMonth($currentDayOfMonth) ? [unlines(map(getString)(getTasksForTheMonth($currentMonth, $taskMatrix)))] : [],
+    [unlines(map(getString)(getTasksForTheWeek($currentWeekOfMonth, $currentMonth, $taskMatrix)))]
+);
+
+$taskMessages = [
+    [getString('happyMonday')],
+    [
+        [getString('beginningOf'. ucfirst(getMonthName($currentMonth))), getString('thisMonthThereAre', count(getTasksForTheMonth($currentMonth, $taskMatrix)))],
+         getString('anywayHeresTheWeek')
+    ],
+    [
+        getString('beginningOf' . ucfirst(getSeason($currentMonth))),
+        [getString('anywayHeresTheMonth'), getString('thisMonthThereAre', count(getTasksForTheMonth($currentMonth, $taskMatrix)))],
+        getString('finallyHeresTheWeek')
+    ]
+];
+
+$messages = zipWith(
+    function($message, $list) {
+        return ununlines(
+            array_merge(
+                is_array($message) ? $message : [$message],
+                [$list]
+            )
+        );
+    },
+    // Magic. startOfSeason implis startofMonth so we get 2, start of month without start of season gives 1 and
+    // a regular week (not the start of a season or month) gives 0. And this is how the indicies are ordered in the array.
+    $taskMessages[isStartOfSeason($currentMonth, $currentDayOfMonth) + isStartOfMonth($currentDayOfMonth)],
+    $taskLists
+);
+
+foreach ($messages as $message) {
+    sendToGroupChat($message);
+    sleep(rand(2,4));
+}
+
+$seasonalMemes = [
+    'summer' => ['https://www.youtube.com/watch?v=NqktmrKB3ko']
+];
+
+if (isStartOfSeason($currentMonth, $currentDayOfMonth)) {
+    sendToGroupChat(getString('seasonalMeme', $seasonalMemes[getSeason($currentMonth)][array_rand($seasonalMemes[getSeason($currentMonth)])]));
+}
diff --git a/unfinished.php b/unfinished.php
new file mode 100644 (file)
index 0000000..e637ba9
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+
+require_once('common.php');
+
+$dt = new DateTimeImmutable();
+
+$mondays = [
+    (int)(new DateTimeImmutable('first monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('second monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('third monday of this month'))->format('d'),
+    (int)(new DateTimeImmutable('fourth monday of this month'))->format('d'),
+];
+
+$directory = sprintf(
+    "tasks/%s/%s/%s",
+    $dt->format('Y'),
+    $dt->format('F'),
+    $dt->format('W')
+);
+$completedTasksFile = "$directory" . "/completed.txt";
+$completedTasks = file_exists($completedTasksFile) ? lines(trim(file_get_contents($completedTasksFile))) : [];
+
+$closestMonday = closest($dt->format('d'), $mondays);
+
+$tasksForTheWeek = getTasksForTheWeek(
+    array_search($closestMonday, $mondays),
+    $dt->format('m'),
+    require 'taskMatrix.php'
+);
+
+$unfinished = array_diff($tasksForTheWeek, $completedTasks);
+
+print_r(
+    array_diff(['treeflowers', 'someothershit'], ['treeflowers'])
+);