summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oracles/base.php24
-rw-r--r--oracles/calc.php158
-rw-r--r--oracles/encoder.php40
-rw-r--r--oracles/numerics.php54
-rw-r--r--oracles/time.php44
-rw-r--r--web.php29
6 files changed, 349 insertions, 0 deletions
diff --git a/oracles/base.php b/oracles/base.php
new file mode 100644
index 0000000..45747fc
--- /dev/null
+++ b/oracles/base.php
@@ -0,0 +1,24 @@
+<?php
+abstract class oracle {
+ // some info to spit out alongside the result, so the user knows
+ // what exactly is giving out the answer. prevents confusion
+ // about what oracle is answering them for ambiguous queries.
+ public $info = [
+ "name" => "some oracle"
+ ];
+ // this function should take in a query string search from $_GET,
+ // and return a bool determining whether or not it is a question
+ // intended for the oracle.
+ public function check_query($q) {
+ return false;
+ }
+ // produce the correct answer for the query using the oracle.
+ // note: if it becomes apparent /during generation/ that the
+ // query is not in fact for the oracle, returning an empty
+ // string will kill the oracle pane.
+ // answer format: ["ans1 title" => "ans1", ...]
+ public function generate_response($q) {
+ return "";
+ }
+}
+?> \ No newline at end of file
diff --git a/oracles/calc.php b/oracles/calc.php
new file mode 100644
index 0000000..4888b95
--- /dev/null
+++ b/oracles/calc.php
@@ -0,0 +1,158 @@
+<?php
+include_once("oracles/base.php");
+class calculator extends oracle {
+ public $info = [
+ "name" => "calculator"
+ ];
+ public function check_query($q) {
+ // straight numerics should go to that oracle
+ if (is_numeric($q)) {
+ return false;
+ }
+ // all chars should be number-y or operator-y
+ $char_whitelist = str_split("1234567890.+-/*^%() ");
+ foreach (str_split($q) as $char) {
+ if (!in_array($char, $char_whitelist)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ // a custom parser and calculator because FUCK YUO, libraries are
+ // gay.
+ public function generate_response($q)
+ {
+ $nums = str_split("1234567890.");
+ $ops = str_split("+-/*^%;");
+ $grouping = str_split("()");
+
+ $q = str_replace(" ", "", $q);
+
+ // backstop for the parser so it catches the last
+ // numeric token
+ $q .= ";";
+
+ // the following comments refer to this example input:
+ // 21+9*(3+2^9)+1
+
+ // 2-length lists of the following patterns:
+ // ["n" (umeric), <some number>]
+ // ["o" (perator), "<some operator>"]
+ // ["g" (roup explicit), <"(" or ")">]
+ // e.g. [["n", 21], ["o", "+"], ["n", 9], ["o", *],
+ // ["g", "("], ["n", 3], ["o", "+"], ["n", 2],
+ // ["o", "^"], ["n", 9], ["g", ")"], ["o", "+"],
+ // ["n", "1"]]
+ $tokens = array();
+ $dragline = 0;
+ foreach(str_split($q) as $i=>$char) {
+ if (in_array($char, $nums)) {
+ continue;
+ }
+ elseif (in_array($char, $ops) || in_array($char, $grouping)) {
+ // hitting a non-numeric implies everything since the
+ // last hit has been part of a number
+ $capture = substr($q, $dragline, $i - $dragline);
+ // prevent the int cast from creating imaginary
+ // ["n", 0] tokens
+ if ($capture != "") {
+ if (substr_count($capture, ".") > 1) {
+ return "";
+ }
+ array_push($tokens, ["n", (float)$capture]);
+ }
+ // reset to one past the current (non-numeric) char
+ $dragline = $i + 1;
+ // the `;' backstop is not a real token and this should
+ // never be present in the token list
+ if ($char != ";") {
+ array_push($tokens, [
+ ($char == "(" || $char == ")") ? "g" : "o",
+ $char
+ ]);
+ }
+ }
+ else {
+ return "";
+ }
+ }
+
+ // two operators back to back should fail
+ for ($i = 1; $i < count($tokens); $i++) {
+ if ($tokens[$i][0] == "o" && $tokens[$i-1][0] == "o") {
+ return "";
+ }
+ }
+
+ //strategy:
+ // traverse to group open (if there is one)
+ // - return to start with the internals
+ // traverse to ^, attack token previous and after
+ // same but for *, then / then + then -
+ // poppers all teh way down
+ try {
+ return [
+ substr($q, 0, strlen($q)-1)." = " => $this->executeBlock($tokens)[0][1]
+ ];
+ }
+ catch (\Throwable $e) {
+ if (get_class($e) == "DivisionByZeroError") {
+ return [
+ $q." = " => "Division by Zero Error!!"
+ ];
+ }
+ return "";
+ }
+ }
+ public function executeBlock($tokens) {
+ if (count($tokens) >= 2 && $tokens[0][0] == "o" && $tokens[0][1] == "-" && $tokens[1][0] == "n") {
+ array_splice($tokens, 0, 2, [["n", -1 * (float)$tokens[1][1]]]);
+ }
+ if (count($tokens) > 0 && $tokens[0][0] == "o" || $tokens[count($tokens)-1][0] == "o") {
+ throw new Exception("Error Processing Request", 1);
+ }
+ if (in_array(["g", "("], $tokens)) {
+ $first_open = array_search(["g", "("], $tokens);
+ $enclosedality = 1;
+ for ($i = $first_open+1; $i < count($tokens); $i++) {
+ if ($tokens[$i][0] == "g") {
+ $enclosedality += ($tokens[$i][1] == "(") ? 1 : -1;
+ }
+ if ($enclosedality == 0) {
+ array_splice($tokens,
+ $first_open,
+ $i+1 - $first_open,
+ $this->executeBlock(
+ array_slice($tokens, $first_open+1, $i-1 - $first_open)
+ )
+ );
+ break;
+ }
+ }
+ }
+ $operators_in_pemdas_order = [
+ "^" => (fn($x, $y) => $x ** $y),
+ "*" => (fn($x, $y) => $x * $y),
+ "/" => (fn($x, $y) => $x / $y),
+ "%" => (fn($x, $y) => $x % $y),
+ "+" => (fn($x, $y) => $x + $y),
+ "-" => (fn($x, $y) => $x - $y)
+ ];
+ foreach ($operators_in_pemdas_order as $op=>$func) {
+ while (in_array(["o", $op], $tokens)) {
+ for ($i = 0; $i < count($tokens); $i++) {
+ if ($tokens[$i] == ["o", $op]) {
+ array_splice(
+ $tokens,
+ $i-1,
+ 3,
+ [["n", (string)($func((float)$tokens[$i-1][1], (float)$tokens[$i+1][1]))]]
+ );
+ }
+ }
+ }
+ }
+ return $tokens;
+ }
+}
+?> \ No newline at end of file
diff --git a/oracles/encoder.php b/oracles/encoder.php
new file mode 100644
index 0000000..00b5ad0
--- /dev/null
+++ b/oracles/encoder.php
@@ -0,0 +1,40 @@
+<?php
+include_once("oracles/base.php");
+class encoder extends oracle {
+ public $info = [
+ "name" => "text encoder/hasher"
+ ];
+ private $special_types = [
+ "rot13",
+ "base64"
+ ];
+ public function check_query($q) {
+ $types = array_merge($this->special_types, hash_algos());
+ foreach ($types as $type) {
+ $type .= " ";
+ if (str_starts_with($q, $type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ public function generate_response($q)
+ {
+ $type = explode(" ", $q)[0];
+ $victim = substr($q, strlen($type)+1);
+ if (in_array($type, hash_algos())) {
+ return [$type." hash" => hash($type, $victim)];
+ }
+ switch ($type) {
+ case "rot13":
+ return ["rot13 encoded" => str_rot13($victim)];
+ case "base64":
+ return [
+ "base64 encoded" => base64_encode($victim),
+ "base64 decoded" => base64_decode($victim)
+ ];
+ }
+ return "";
+ }
+}
+?> \ No newline at end of file
diff --git a/oracles/numerics.php b/oracles/numerics.php
new file mode 100644
index 0000000..3d428e3
--- /dev/null
+++ b/oracles/numerics.php
@@ -0,0 +1,54 @@
+<?php
+include_once("oracles/base.php");
+class numerics extends oracle {
+ public $info = [
+ "name" => "numeric base conversion"
+ ];
+ public function check_query($q) {
+ if (str_contains($q, " ")) {
+ return false;
+ }
+
+ $q = strtolower($q);
+
+ $profiles = [
+ ["0x", str_split("0123456789abcdef")],
+ ["", str_split("1234567890")],
+ ["b", str_split("10")]
+ ];
+
+ foreach ($profiles as $profile) {
+ $good = true;
+ $good &= str_starts_with($q, $profile[0]);
+ $nq = substr($q, strlen($profile[0]));
+ foreach (str_split($nq) as $c) {
+ $good &= in_array($c, $profile[1]);
+ }
+ if ($good) {
+ return true;
+ }
+ }
+ return false;
+ }
+ public function generate_response($q) {
+ $n = 0;
+ if (str_starts_with($q, "0x")) {
+ $nq = substr($q, strlen("0x"));
+ $n = hexdec($nq);
+ }
+ elseif (str_starts_with($q, "b")) {
+ $nq = substr($q, strlen("b"));
+ $n = bindec($nq);
+ }
+ else {
+ $n = (int)$q;
+ }
+ return [
+ "decimal (base 10)" => (string)$n,
+ "hexadecimal (base 16)" => "0x".(string)dechex($n),
+ "binary (base 2)" => "b".(string)decbin($n),
+ "" => "binary inputs should be prefixed with 'b', hex with '0x'."
+ ];
+ }
+}
+?> \ No newline at end of file
diff --git a/oracles/time.php b/oracles/time.php
new file mode 100644
index 0000000..57af093
--- /dev/null
+++ b/oracles/time.php
@@ -0,0 +1,44 @@
+<?php
+include_once("oracles/base.php");
+class time extends oracle {
+ public $info = [
+ "name" => "what time is it?"
+ ];
+ public function check_query($q) {
+ $prompts = [
+ "what", "time", "is", "it",
+ "right", "now", "the", "current",
+ "get"
+ ];
+ $q = str_replace(",", "", $q);
+ $q = str_replace("?", "", $q);
+ $q = str_replace("what's", "what is", $q);
+ $oq = $q;
+ $q = explode(" ", $q);
+ $count = 0;
+ foreach ($q as $word) {
+ if (in_array($word, $prompts)) {
+ $count++;
+ }
+ }
+ // remove one from total count if a timezone is specified
+ return ($count/(count($q) + (str_contains($oq, "tz:") ? -1 : 0))) > 3/4;
+ }
+ public function generate_response($q) {
+ $timezone = timezone_name_from_abbr("UTC");
+ foreach (explode(" ", $q) as $word) {
+ if (str_starts_with($word, "tz:")) {
+ $decltz = timezone_name_from_abbr(substr($word, 3, 3));
+ if ($decltz) {
+ $timezone = $decltz;
+ }
+ }
+ }
+ date_default_timezone_set($timezone);
+ return [
+ "The time in ".$timezone => date("H:i:s"),
+ "" => "include the string \"tz:XXX\" to use timezone XXX"
+ ];
+ }
+}
+?> \ No newline at end of file
diff --git a/web.php b/web.php
index 48539b9..e34672d 100644
--- a/web.php
+++ b/web.php
@@ -476,6 +476,35 @@ if($c !== 0){
}
/*
+ Prepend Oracle output, if applicable
+*/
+include_once("oracles/encoder.php");
+include_once("oracles/calc.php");
+include_once("oracles/time.php");
+include_once("oracles/numerics.php");
+$oracles = [new calculator(), new encoder(), new time(), new numerics()];
+$fortune = "";
+foreach ($oracles as $oracle) {
+ if ($oracle->check_query($_GET["s"])) {
+ $resp = $oracle->generate_response($_GET["s"]);
+ if ($resp != "") {
+ $fortune .= "<div class=\"infobox\">";
+ foreach ($resp as $title => $r) {
+ if ($title) {
+ $fortune .= "<h3>".htmlspecialchars($title)."</h3><div class=\"code\">".htmlspecialchars($r)."</div>";
+ }
+ else {
+ $fortune .= "<i>".$r."</i><br>";
+ }
+ }
+ $fortune .= "<small>Answer provided by oracle: ".$oracle->info["name"]."</small></div>";
+ }
+ break;
+ }
+}
+$payload["left"] = $fortune . $payload["left"];
+
+/*
Load next page
*/
if($results["npt"] !== null){