This commit is contained in:
+10
-2
@@ -3,14 +3,22 @@ FROM mcr.microsoft.com/powershell:7.5-ubuntu-24.04
|
|||||||
USER root
|
USER root
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends tzdata \
|
&& apt-get install -y --no-install-recommends tzdata wget \
|
||||||
|
&& wget -q https://github.com/aptible/supercronic/releases/latest/download/supercronic-linux-amd64 -O /usr/local/bin/supercronic \
|
||||||
|
&& chmod +x /usr/local/bin/supercronic \
|
||||||
&& apt-get dist-upgrade -y \
|
&& apt-get dist-upgrade -y \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
|
COPY ["crontab", "/data/"]
|
||||||
|
COPY ["main.sh", "/data/"]
|
||||||
COPY ["Start-DoneTickNotifier.ps1", "/data/"]
|
COPY ["Start-DoneTickNotifier.ps1", "/data/"]
|
||||||
|
COPY ["Start-DoneTickConsumer.ps1", "/data/"]
|
||||||
|
|
||||||
ENTRYPOINT ["pwsh", "-Command", "/data/Start-DoneTickNotifier.ps1"]
|
RUN chmod +x /data/main.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["main.sh"]
|
||||||
|
|||||||
@@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
A small PowerShell notifier for [Donetick](https://github.com/donetick/donetick) chores.
|
A small PowerShell notifier for [Donetick](https://github.com/donetick/donetick) chores.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> **AI Assistance Disclosure**
|
|
||||||
>
|
|
||||||
> OpenAI Codex was used to help create this README and the Gitea pipeline files in this repository.
|
|
||||||
>
|
|
||||||
> All released application code is written by the repository owner.
|
|
||||||
|
|
||||||
The notifier checks the Donetick external API for chores, groups tasks that are overdue or due today, and sends summary notifications through an Apprise-compatible webhook.
|
The notifier checks the Donetick external API for chores, groups tasks that are overdue or due today, and sends summary notifications through an Apprise-compatible webhook.
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
[string] $ListenUrl = "http://localhost:8080/"
|
||||||
|
|
||||||
|
function Write-JsonResponse
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[System.Net.HttpListenerResponse]
|
||||||
|
$Response,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[int]
|
||||||
|
$StatusCode,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[hashtable]
|
||||||
|
$Body
|
||||||
|
)
|
||||||
|
|
||||||
|
$json = $Body | ConvertTo-Json -Depth 10
|
||||||
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
|
||||||
|
|
||||||
|
$Response.StatusCode = $StatusCode
|
||||||
|
$Response.ContentType = "application/json"
|
||||||
|
$Response.ContentEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
$Response.ContentLength64 = $bytes.Length
|
||||||
|
$Response.OutputStream.Write($bytes, 0, $bytes.Length)
|
||||||
|
$Response.OutputStream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Read-RequestBody
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[System.Net.HttpListenerRequest]
|
||||||
|
$Request
|
||||||
|
)
|
||||||
|
|
||||||
|
$reader = [System.IO.StreamReader]::new($Request.InputStream, $Request.ContentEncoding)
|
||||||
|
try {
|
||||||
|
return $reader.ReadToEnd()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$reader.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Receive-Webhook
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[System.Net.HttpListenerContext]
|
||||||
|
$Context
|
||||||
|
)
|
||||||
|
|
||||||
|
$request = $Context.Request
|
||||||
|
$response = $Context.Response
|
||||||
|
|
||||||
|
if ($request.HttpMethod -ne "POST") {
|
||||||
|
Write-JsonResponse -Response $response -StatusCode 405 -Body @{
|
||||||
|
ok = $false
|
||||||
|
error = "Only POST requests are supported."
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawBody = Read-RequestBody -Request $request
|
||||||
|
$payload = $null
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($rawBody) {
|
||||||
|
$payload = $rawBody | ConvertFrom-Json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-JsonResponse -Response $response -StatusCode 400 -Body @{
|
||||||
|
ok = $false
|
||||||
|
error = "Request body was not valid JSON."
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Webhook received at $(Get-Date -Format o)"
|
||||||
|
Write-Host "From: $($request.RemoteEndPoint)"
|
||||||
|
Write-Host "Path: $($request.Url.AbsolutePath)"
|
||||||
|
|
||||||
|
if ($payload) {
|
||||||
|
Write-Host "Payload:"
|
||||||
|
Write-Host ($payload | ConvertTo-Json -Depth 20)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host "Payload: <empty>"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add your real webhook handling here.
|
||||||
|
# Example: inspect $payload.event, save data, or call another API.
|
||||||
|
|
||||||
|
Write-JsonResponse -Response $response -StatusCode 200 -Body @{
|
||||||
|
ok = $true
|
||||||
|
message = "Webhook accepted."
|
||||||
|
receivedAt = (Get-Date -Format o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$listener = [System.Net.HttpListener]::new()
|
||||||
|
$listener.Prefixes.Add($ListenUrl)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$listener.Start()
|
||||||
|
Write-Host "Webhook consumer listening on $ListenUrl"
|
||||||
|
Write-Host "Press Ctrl+C to stop."
|
||||||
|
|
||||||
|
while ($listener.IsListening) {
|
||||||
|
$context = $listener.GetContext()
|
||||||
|
Receive-Webhook -Context $context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($listener.IsListening) {
|
||||||
|
$listener.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
$listener.Close()
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Default schedule (overridden at runtime by JOB_SCHEDULE env var)
|
||||||
|
0 * * * * pwsh /data/Start-DoneTickNotifier.ps1
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Build the crontab dynamically from the env variable
|
||||||
|
echo "${JOB_SCHEDULE} pwsh /data/Start-DoneTineNotifier.ps1" > /tmp/crontab-runtime
|
||||||
|
|
||||||
|
echo "Cron schedule set to: ${JOB_SCHEDULE}"
|
||||||
|
|
||||||
|
# Start supercronic in background using the generated crontab
|
||||||
|
supercronic /tmp/crontab-runtime &
|
||||||
|
|
||||||
|
# Start the HTTP listener in foreground
|
||||||
|
exec pwsh /data/Start-DoneTickConsumer.ps1
|
||||||
Reference in New Issue
Block a user