Implemented templates and general improvements
This commit is contained in:
parent
d476d77340
commit
2ce66f0a56
7 changed files with 304 additions and 50 deletions
41
README.md
41
README.md
|
@ -8,11 +8,17 @@ The status can only be updated with a valid secret key. The current status is st
|
|||
## Setup
|
||||
|
||||
Make sure the config file contains a good key, that the key is usable in a GET request and that the specified storage
|
||||
file is writable.
|
||||
files are writable.
|
||||
|
||||
The storage file is created if it does not exist. Since the storage file does not contain any sensitive information, it
|
||||
The storage files are created if they do not exist. Since the activity file does not contain any sensitive information,
|
||||
it
|
||||
can be made publicly accessible. At least it contains nothing that is not already available over the status itself.
|
||||
|
||||
The templates file can also be publicly accessible. When it is first created, default templates are added, which are
|
||||
specified in `Templates.loadDefaultTemplates()`. The templates can be modified or new ones can be added. The templates
|
||||
are stored as json objects. It is recommended to let it create the templates file on the first request, and then go in
|
||||
and modify it to your liking.
|
||||
|
||||
## Usage
|
||||
|
||||
### Get status
|
||||
|
@ -30,12 +36,29 @@ your liking.
|
|||
|
||||
Here is an overview:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|---------------|-------------------------------------------------------------------|-----------------------------------------------------|
|
||||
| `title` | The title of the status. | `title=Working` |
|
||||
| `description` | A description of the status. | `description=Making progress on personal projects.` |
|
||||
| `duration` | The expected duration of the status in seconds. | `duration=3600` |
|
||||
| `available` | Whether you are available to other people. | `available=0` |
|
||||
| `working` | Whether you are working, as opposed to having some personal time. | `working=1` |
|
||||
| Parameter | Description | Example |
|
||||
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|
|
||||
| `title` | The title of the status. | `title=Working` |
|
||||
| `description` | A description of the status. | `description=Making progress on personal projects.` |
|
||||
| `expectedDuration` | The expected duration of the status in seconds. | `duration=3600` |
|
||||
| `available` | Whether you are available to other people. | `available=0` |
|
||||
| `working` | Whether you are working, as opposed to having some personal time. | `working=1` |
|
||||
| `template` | If a valid template id is specified, the template is applied to the activity. If other parameters are set, they will overwrite the template parameters. | `template=work` |
|
||||
|
||||
## Templates
|
||||
|
||||
Templates are a way to easily set often used status. They can either be specified as `template` parameter for specific
|
||||
activity, or using simple rules and automations.
|
||||
|
||||
A template overwrites all parameters of an activity, only the `startTime` and `template` parameters are preserved. If
|
||||
the template is applied via the `template` paramter, other specified parameters will overwrite the template parameters.
|
||||
|
||||
### Rules
|
||||
|
||||
Rules are checked every time the status is requested. If all rules of a template match, the template is applied. Each
|
||||
template has a priority. The valid template with the highest priority is applied.
|
||||
|
||||
| Rule | Description | Further parameters |
|
||||
|---------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------|
|
||||
| `triggerOnlyOnEmptyTitle` | Only apply the template if the title is empty. | |
|
||||
| `triggerAfterTimeout` | Applies the template if the current activity is older than the specified timeout. | `timeout` specifies the required timeout in seconds. |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class Activity
|
||||
{
|
||||
public int $startTime; // Unix timestamp in seconds
|
||||
public int $expectedDuration; // In seconds, 0 if open-ended
|
||||
public int $expectedDuration; // Expected duration in seconds, 0 if open-ended
|
||||
|
||||
public string $title;
|
||||
public string $description; // Additional information, but might be kept empty
|
||||
|
@ -11,6 +11,23 @@ class Activity
|
|||
public bool $available; // Am I available for requests and to talk?
|
||||
public bool $working; // Am I working on something?
|
||||
|
||||
public string $template; // ID of the template that is supposed to be applied to this activity
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->startTime = time();
|
||||
$this->expectedDuration = 0;
|
||||
$this->title = "";
|
||||
$this->description = "";
|
||||
$this->available = false;
|
||||
$this->working = false;
|
||||
$this->template = "";
|
||||
}
|
||||
|
||||
public function initialLoad(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function getCurrentDuration(): int
|
||||
{
|
||||
return time() - $this->startTime;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
$SECRET_KEY = "ENTER_SECRET_HERE";
|
||||
$STORAGE_FILE = "last_activity.json";
|
||||
$ACTIVITY_FILE = "last_activity.json";
|
||||
$TEMPLATES_FILE = "templates.json";
|
||||
|
|
|
@ -4,6 +4,7 @@ function respondWithJson($data): void
|
|||
{
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
die();
|
||||
}
|
||||
|
||||
function respondAndDie(string $message): void
|
||||
|
|
|
@ -1,47 +1,74 @@
|
|||
<?php
|
||||
// Temporary error reporting
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
require_once("config.php");
|
||||
require_once("storage.php");
|
||||
require_once("activity.php");
|
||||
require_once("http.php");
|
||||
|
||||
function loadGivenParameters(Activity $activity): void
|
||||
{
|
||||
if (isset($_GET["title"])) {
|
||||
$activity->title = $_GET["title"];
|
||||
}
|
||||
if (isset($_GET["description"])) {
|
||||
$activity->description = $_GET["description"];
|
||||
}
|
||||
if (isset($_GET["duration"])) {
|
||||
$activity->expectedDuration = (int)$_GET["duration"];
|
||||
}
|
||||
if (isset($_GET["available"])) {
|
||||
$activity->available = filter_var($_GET["available"], FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
if (isset($_GET["working"])) {
|
||||
$activity->working = filter_var($_GET["working"], FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
if (isset($_GET["template"])) {
|
||||
$activity->template = $_GET["template"];
|
||||
}
|
||||
}
|
||||
|
||||
function handleTemplate(Activity $activity)
|
||||
{
|
||||
// Load and find template
|
||||
try {
|
||||
$templates = loadTemplates();
|
||||
} catch (Exception $e) {
|
||||
respondAndDie($e->getMessage());
|
||||
}
|
||||
|
||||
$template = $templates->findValidTemplate($activity);
|
||||
if ($template == null) {
|
||||
// No applicable template found
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply template
|
||||
$template->applyTo($activity);
|
||||
|
||||
// Overwrite parameters if template was specified on activity
|
||||
if ($activity->template != "") {
|
||||
loadGivenParameters($activity);
|
||||
}
|
||||
}
|
||||
|
||||
// If no secret key is provided, respond with the current activity
|
||||
global $SECRET_KEY;
|
||||
if (!isset($_GET["secret"]) || $_GET["secret"] != $SECRET_KEY) {
|
||||
// Respond with the current activity
|
||||
$activity = loadActivity();
|
||||
try {
|
||||
$activity = loadActivity();
|
||||
} catch (Exception $e) {
|
||||
respondAndDie($e->getMessage());
|
||||
}
|
||||
handleTemplate($activity);
|
||||
respondWithJson($activity->getPublicData());
|
||||
}
|
||||
|
||||
// Update activity
|
||||
$activity = new Activity();
|
||||
$activity->startTime = time();
|
||||
|
||||
|
||||
if (isset($_GET["title"])) {
|
||||
$activity->title = $_GET["title"];
|
||||
} else {
|
||||
$activity->title = "Awake";
|
||||
}
|
||||
if (isset($_GET["description"])) {
|
||||
$activity->description = $_GET["description"];
|
||||
} else {
|
||||
$activity->description = "";
|
||||
}
|
||||
if (isset($_GET["duration"])) {
|
||||
$activity->expectedDuration = (int)$_GET["duration"];
|
||||
} else {
|
||||
$activity->expectedDuration = 0;
|
||||
}
|
||||
if (isset($_GET["available"])) {
|
||||
$activity->available = filter_var($_GET["available"], FILTER_VALIDATE_BOOLEAN);
|
||||
} else {
|
||||
$activity->available = false;
|
||||
}
|
||||
if (isset($_GET["working"])) {
|
||||
$activity->working = filter_var($_GET["working"], FILTER_VALIDATE_BOOLEAN);
|
||||
} else {
|
||||
$activity->working = false;
|
||||
}
|
||||
|
||||
loadGivenParameters($activity);
|
||||
storeActivity($activity);
|
||||
respondAndDie("Activity updated");
|
||||
|
|
|
@ -1,19 +1,76 @@
|
|||
<?php
|
||||
require_once("config.php");
|
||||
require_once("templates.php");
|
||||
require_once("activity.php");
|
||||
|
||||
function storeActivity(Activity $activity): void
|
||||
{
|
||||
global $STORAGE_FILE;
|
||||
$file = fopen($STORAGE_FILE, "w");
|
||||
fwrite($file, json_encode($activity));
|
||||
fclose($file);
|
||||
global $ACTIVITY_FILE;
|
||||
storeObject($activity, $ACTIVITY_FILE);
|
||||
}
|
||||
|
||||
function loadActivity(): Activity
|
||||
{
|
||||
global $STORAGE_FILE;
|
||||
$file = fopen($STORAGE_FILE, "r");
|
||||
$line = fgets($file);
|
||||
fclose($file);
|
||||
return json_decode($line);
|
||||
global $ACTIVITY_FILE;
|
||||
return loadObject($ACTIVITY_FILE, Activity::class);
|
||||
}
|
||||
|
||||
function storeTemplates(Templates $templates): void
|
||||
{
|
||||
global $TEMPLATES_FILE;
|
||||
storeObject($templates, $TEMPLATES_FILE);
|
||||
}
|
||||
|
||||
function loadTemplates(): Templates
|
||||
{
|
||||
global $TEMPLATES_FILE;
|
||||
if (!file_exists($TEMPLATES_FILE)) {
|
||||
// Create initial template file
|
||||
$templates = new Templates();
|
||||
$templates->loadDefaultTemplates();
|
||||
storeTemplates($templates);
|
||||
return $templates;
|
||||
}
|
||||
|
||||
return loadObject($TEMPLATES_FILE, Templates::class);
|
||||
}
|
||||
|
||||
function storeObject(object $object, string $file): void
|
||||
{
|
||||
$file = fopen($file, "w");
|
||||
fwrite($file, json_encode($object, JSON_PRETTY_PRINT));
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
function loadObject(string $file, string $class): object
|
||||
{
|
||||
$json = file_get_contents($file);
|
||||
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
||||
return parseObject($data, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
function parseObject(array $data, string $class): object
|
||||
{
|
||||
$reflection = new ReflectionClass($class);
|
||||
$instance = $reflection->newInstanceWithoutConstructor();
|
||||
foreach ($data as $property => $value) {
|
||||
$propertyReflection = $reflection->getProperty($property);
|
||||
$propertyReflection->setAccessible(true);
|
||||
$propertyReflection->setValue($instance, $value);
|
||||
}
|
||||
|
||||
try {
|
||||
$instance->initialLoad();
|
||||
} catch (Exception $e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
|
128
src/templates.php
Normal file
128
src/templates.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
require_once("storage.php");
|
||||
|
||||
class Templates
|
||||
{
|
||||
public array $templates;
|
||||
|
||||
public function initialLoad()
|
||||
{
|
||||
foreach ($this->templates as $templateId => $template) {
|
||||
$this->templates[$templateId] = parseObject($template, Template::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function existsTemplate(string $templateId): bool
|
||||
{
|
||||
// Does key in array exist?
|
||||
if (array_key_exists($templateId, $this->templates)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function findValidTemplate(Activity $activity): ?Template
|
||||
{
|
||||
if ($activity->template != "") {
|
||||
if ($this->existsTemplate($activity->template)) {
|
||||
return $this->templates[$activity->template];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for other validity conditions
|
||||
$validTemplate = null;
|
||||
foreach ($this->templates as $templateId => $template) {
|
||||
if ($template->isValidFor($activity) && ($validTemplate == null || $template->priority > $validTemplate->priority)) {
|
||||
$validTemplate = $template;
|
||||
}
|
||||
}
|
||||
|
||||
return $validTemplate;
|
||||
}
|
||||
|
||||
public function loadDefaultTemplates(): void
|
||||
{
|
||||
$awakeTemplate = new Template();
|
||||
$awakeTemplate->title = "Awake";
|
||||
$awakeTemplate->description = "";
|
||||
$awakeTemplate->expectedDuration = 0;
|
||||
$awakeTemplate->available = false;
|
||||
$awakeTemplate->working = false;
|
||||
$awakeTemplate->priority = 0;
|
||||
$awakeTemplate->triggerOnlyOnEmptyTitle = true;
|
||||
|
||||
$this->templates["awake"] = $awakeTemplate;
|
||||
|
||||
$sleepingTemplate = new Template();
|
||||
$sleepingTemplate->title = "Sleeping";
|
||||
$sleepingTemplate->description = "";
|
||||
$sleepingTemplate->expectedDuration = 0;
|
||||
$sleepingTemplate->available = false;
|
||||
$sleepingTemplate->working = false;
|
||||
$sleepingTemplate->priority = 10;
|
||||
$sleepingTemplate->triggerOnlyOnEmptyTitle = true;
|
||||
$sleepingTemplate->triggerAfterTimeout = true;
|
||||
$sleepingTemplate->timeout = 60 * 60 * 4; // 4 hours
|
||||
|
||||
$this->templates["sleeping"] = $sleepingTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
class Template
|
||||
{
|
||||
public string $title;
|
||||
public string $description;
|
||||
public int $expectedDuration;
|
||||
public bool $available;
|
||||
public bool $working;
|
||||
|
||||
// Meta template information
|
||||
public int $priority; // Higher priority templates are applied first
|
||||
|
||||
public bool $triggerOnlyOnEmptyTitle; // If true, the template will be only applied if the activity title is empty
|
||||
|
||||
public bool $triggerAfterTimeout; // If true, the template will be applied after the timeout
|
||||
public int $timeout; // Timeout for trigger in seconds
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->title = "";
|
||||
$this->description = "";
|
||||
$this->expectedDuration = 0;
|
||||
$this->available = false;
|
||||
$this->working = false;
|
||||
$this->priority = 0;
|
||||
$this->triggerOnlyOnEmptyTitle = false;
|
||||
$this->triggerAfterTimeout = false;
|
||||
$this->timeout = 0;
|
||||
}
|
||||
|
||||
public function applyTo(Activity $activity): void
|
||||
{
|
||||
$activity->title = $this->title;
|
||||
$activity->description = $this->description;
|
||||
$activity->expectedDuration = $this->expectedDuration;
|
||||
$activity->available = $this->available;
|
||||
$activity->working = $this->working;
|
||||
}
|
||||
|
||||
public function isValidFor(Activity $activity): bool
|
||||
{
|
||||
if ($this->triggerOnlyOnEmptyTitle && $activity->title != "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->triggerAfterTimeout && $activity->getCurrentDuration() < $this->timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function initialLoad(): void
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue