+function getRules() {
+ STATIC $rules;
+ return $rules = $rules ?? require 'rules.php';
+}
+
+function getString($identifier, ...$vars) {
+ STATIC $strings;
+ $strings = $strings ?? require 'strings.php';
+
+ return sprintf($strings[$identifier], ...$vars);
+}
+
+function formatDate($date) {
+ return $date->format(DATE_FORMAT);
+}
+
+const sendToGroupChat = 'sendToGroupChat';
+function sendToGroupChat(string $message) {
+ return getTelegram()->sendMessage(['chat_id' => CHAT_ID, 'text' => $message]);
+}
+
+const generateReminderText = 'generateReminderText';
+function generateReminderText($message) {
+ return getString('billreminder', REMIND_THRESHOLD, $message['service'], splitBill($message['amount']), formatDate($message['due']));
+}
+
+const generateNewBillText = 'generateNewBillText';
+function generateNewBillText($message) {
+ return getString('newbill', $message['service'], splitBill($message['amount']), formatDate($message['due']));
+}
+
+const messageNeedsReminder = 'messageNeedsReminder';
+function messageNeedsReminder($message) {
+ return $message['due']->diff(new DateTimeImmutable)->d == REMIND_THRESHOLD;
+}
+
+
+function ∘(...$fs) {
+ return function($arg) use ($fs) {
+ return array_reduce(array_reverse($fs), function($c, $f) {
+ return $f($c);
+ }, $arg);
+ };
+}
+
+function map($callable) {
+ return function($list) use ($callable) {
+ return array_map($callable, $list);
+ };
+}
+
+function filter($callable) {
+ return function($list) use ($callable) {
+ return array_filter($list, $callable);
+ };
+}
+
+function ∪($a, $b) {
+ return array_merge($a, $b);
+}
+