@rackbops/plugin-tracker 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/dist/plugin.js +984 -212
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,9 +5,10 @@ on a rackbops-discord-bot instance. Plan of record: Rackbops/Tooling
|
|
|
5
5
|
`research/city-hall-task-tracker.md` (sections 0, 5.1-5.3, 5.5, 5.8, 5.10). This version is the core
|
|
6
6
|
(rackbops-bot-plugins#78), the Discord surface of the first slice (#79) -- reminders by slash
|
|
7
7
|
command, delivered by DM, answered by button, with history -- the first slice of the web area
|
|
8
|
-
(#80): sign-in by one-time link, my tasks, a task's history, and settings --
|
|
8
|
+
(#80): sign-in by one-time link, my tasks, a task's history, and settings -- renewals and
|
|
9
9
|
the price tracker (#81, plan E6): two more types on the notify side, no model, nothing sent to
|
|
10
|
-
city-hall
|
|
10
|
+
city-hall -- and the web area's task editor (#80, slice 2): make, edit, pause, resume and delete
|
|
11
|
+
your own tasks from the web, under the commands' own rules.
|
|
11
12
|
|
|
12
13
|
## Commands
|
|
13
14
|
|
|
@@ -26,7 +27,7 @@ as in a server (plan 5.5, item 39).
|
|
|
26
27
|
| `/task decide task choice [amount]` | the renewal's owner | Answers the renewal's latest ask -- keep, cancel or renewed -- with the amount actually paid when it changed (a button cannot carry one). Use it instead of the button, not after it: a run is answered once. |
|
|
27
28
|
| `/task history task` | the owner, an accepted recipient, or an admin | Every run -- due, status, how it was answered, text replies -- and the task's changes; for a renewal, what each period cost and the total; for a price, the last check and the readings with the low and the high. |
|
|
28
29
|
| `/task share task user` | the task's owner | Invites an admitted, registered member who can be DMed: they get one consent DM with accept and decline. |
|
|
29
|
-
| `/task resume task` | the task's owner | Resumes a task paused because a recipient could not be DMed
|
|
30
|
+
| `/task resume task` | the task's owner | Resumes a paused task: one paused because a recipient could not be DMed goes on without that recipient; one its owner paused on the web simply goes on. |
|
|
30
31
|
| `/settings hour hour` | registered | The preferred hour; recurring reminders with no time of their own move to it. |
|
|
31
32
|
| `/web` | registered | A one-time link to sign in to the web area (below), good for 10 minutes and one use. Says so when `TRACKER_WEB_URL` is unset. |
|
|
32
33
|
|
|
@@ -58,8 +59,11 @@ served at a hashed path, cached for a year).
|
|
|
58
59
|
|
|
59
60
|
| Path | What |
|
|
60
61
|
|---|---|
|
|
61
|
-
| `/` | My tasks: the same list as `/tasks` (active tasks owned and received, next run in your zone; paused ones and why), each linking to its history. |
|
|
62
|
-
| `/tasks/<id>` | A task's history: the same as `/task history`, for the owner, an accepted recipient or an admin. Anyone else gets the same 404 as an unknown id. |
|
|
62
|
+
| `/` | My tasks: the same list as `/tasks` (active tasks owned and received, next run in your zone; paused ones and why), each linking to its history, and links to make a new one. |
|
|
63
|
+
| `/tasks/<id>` | A task's history: the same as `/task history`, for the owner, an accepted recipient or an admin. Anyone else gets the same 404 as an unknown id. The owner also sees Edit, Pause or Resume, and Delete. |
|
|
64
|
+
| `/new/reminder`, `/new/renewal`, `/new/price` | The editor's new-task forms (GET), and making one (POST). |
|
|
65
|
+
| `/tasks/<id>/edit` | The owner's edit form (GET) and saving it (POST). |
|
|
66
|
+
| `/tasks/<id>/pause`, `/resume`, `/delete` | POST only. Delete answers a confirmation first; only a second post carrying `confirm=yes` deletes. |
|
|
63
67
|
| `/settings` | Preferred hour and time zone, checked as `/register` checks them. |
|
|
64
68
|
| `/signin` | Where a request that is not signed in is sent: says to run `/web`. |
|
|
65
69
|
| `/login?t=...` | The link `/web` gives. |
|
|
@@ -83,6 +87,33 @@ than `TRACKER_WEB_URL`'s is refused. The sign-in post has no session yet, so it
|
|
|
83
87
|
double-submit token from a `SameSite=Strict` cookie the link's page sets: another site cannot sign
|
|
84
88
|
you in as someone else. Sign out is a POST too.
|
|
85
89
|
|
|
90
|
+
**The task editor** (slice 2). The forms take the options of `/remind`, `/renewal` and `/price`,
|
|
91
|
+
and the same functions check and make them: the same defaults, limits and messages, the page read
|
|
92
|
+
once before a price tracker is made, at most 20 price trackers per person wherever they were made,
|
|
93
|
+
and `near` turned into the same bounded pattern; a person has one new price tracker's page read in
|
|
94
|
+
flight at a time. Every person may have at most 200 active or paused tasks of all types together,
|
|
95
|
+
made anywhere. The length limits are one set (`src/limits.ts`) for the slash options, the forms
|
|
96
|
+
and the server's checks. An edit changes a reminder's text, time and repeat
|
|
97
|
+
(an empty time keeps the one it has, unless the repeat changes); a renewal's name, amount,
|
|
98
|
+
currency, date, unit, every, lead and note (a new amount is the one the next ask quotes; a new date
|
|
99
|
+
gets `/renewal`'s first-ask rule, but a date a run already asked about is not asked again); a price's name, interval,
|
|
100
|
+
drop and baseline, never its page. A schedule change cancels and replaces what is queued and keeps
|
|
101
|
+
snoozes, as a zone move does, and each change is in the task's history. An empty field keeps what
|
|
102
|
+
the task has (an empty note clears it; an empty price name is the page's address). A renewal date
|
|
103
|
+
that is one of the schedule's own period dates keeps the schedule and its anchor as they are, so the
|
|
104
|
+
31st stays the 31st; only a different date re-anchors. A finished task cannot be
|
|
105
|
+
edited. Pause holds everything the task would send until Resume, and a person's delivery resuming
|
|
106
|
+
does not undo it; a run missed while paused fires once, late, on resume, and a zone or hour change
|
|
107
|
+
re-times the run a paused task holds -- same period date or local day, new zone or hour, still one
|
|
108
|
+
run -- so it is neither lost nor sent at the old time. Resume on a task paused
|
|
109
|
+
for failed DMs is `/task resume`. Delete archives the task: it leaves every list, its queued runs
|
|
110
|
+
are dropped and nothing more is sent, but its history is kept (an admin can see every task) and
|
|
111
|
+
its page stays for the owner. Every editor post passes the session, CSRF and `Origin` checks; the
|
|
112
|
+
task is loaded by the id in the path and must be the viewer's own, checked again in the write
|
|
113
|
+
queue, and anyone else's -- even one an admin can see -- answers the same 404 as an unknown id. A
|
|
114
|
+
refused form comes back with what was typed and why. Form bodies are capped at 32 KiB, read no
|
|
115
|
+
further than that.
|
|
116
|
+
|
|
86
117
|
**Headers.** `Content-Security-Policy: default-src 'none'; style-src 'self'; img-src 'none';
|
|
87
118
|
form-action 'self'; frame-ancestors 'none'; base-uri 'none'`, `X-Content-Type-Options: nosniff`,
|
|
88
119
|
`X-Frame-Options: DENY`. Every value on a page is HTML-escaped. An unknown path is 404; a method
|
|
@@ -114,13 +145,14 @@ The `__Host-` prefix, which would stop that, requires `Path=/` and so cannot be
|
|
|
114
145
|
| Store | `src/store.ts`, `src/schema.ts` | docket's `Store` port on `bun:sqlite`, in `<dataDir>/tracker/tracker.sqlite` (WAL). A Discord id belongs to at most one user. Schema versioned by `PRAGMA user_version`; a shipped migration is never edited. `store.test.ts` runs docket's `STORE_CONTRACT` against it. |
|
|
115
146
|
| Notify lane | `src/notify-lane.ts` | docket's `Lanes.tickNotify` on two host ticks, every 60 s, through a view of the store that skips a paused task's due runs: `notify` runs every type but the page readers, `poll` runs only them (`price`) with the Fetch port, so a slow page never holds up a reminder. Registers `reminder`, `renewal` and `price`. The execute lane is not ticked. |
|
|
116
147
|
| Page reads | `src/fetch.ts` | docket's `Fetch` port for `price`: http or https on the default port, no credentials, every resolved address public (no loopback, private, link-local, CGNAT, multicast or reserved range, IPv4 or IPv6), redirects followed by hand and re-checked (at most 5), 15 s including the name lookup, at most 3 MB kept. The body is then rebuilt in linear time (`src/page.ts`) to just what extraction reads -- JSON-LD, meta tags, and the page with every `<` blanked, so `near` still reads text, attributes and script data -- because docket's extraction patterns take quadratic time on a page of unclosed tags. A read the tick's abort cuts short requeues its run instead of counting a miss. A DNS answer that changes between the check and the read is not caught here. |
|
|
117
|
-
| Renewals, prices | `src/tracked.ts`, `src/series.ts`, `src/page.ts` | `/renewal`, `/price` and `/task decide`; the series lines of `/task history`. The series (docket's `series` table, schema 1) holds a renewal's paid amounts and a price's readings. |
|
|
148
|
+
| Renewals, prices | `src/tracked.ts`, `src/price.ts`, `src/series.ts`, `src/page.ts` | `/renewal`, `/price` and `/task decide`; the series lines of `/task history`. The series (docket's `series` table, schema 1) holds a renewal's paid amounts and a price's readings. |
|
|
118
149
|
| Delivery | `src/notifier.ts`, `src/claims.ts`, `src/buttons.ts` | docket's `Notifier` over `host.dm`, with the buttons. Each (occurrence, person) is claimed in `delivery_claims` before the DM is sent and settled after; a claim never settled is not resent -- it is logged once, as unconfirmed. |
|
|
119
150
|
| Pause | `src/delivery-health.ts` | The per-person failure count, the pause and the resume. |
|
|
120
151
|
| People | `src/people.ts`, `src/admissions.ts` | Discord id, time zone, preferred hour, admin flag -- in the tracker's store, never usr; who admitted each person and when they registered. `TRACKER_ADMIN_DISCORD_IDS` only ever grants admin. |
|
|
121
152
|
| Commands | `src/discord.ts`, `src/interactions.ts`, `src/discord-common.ts` (discord.js); `src/actions.ts`, `src/press.ts`, `src/history.ts`, `src/access.ts` | The discord.js files read options and render; the rest is Discord-free over the injected store, clock and notifier. Store writes are handled one at a time; Discord lookups and DMs run outside that queue. |
|
|
122
153
|
| Health | `src/health.ts` | `GET /tracker/healthz` (through the bot's HTTP router): `200` `ok`/`starting`, `503` `inactive`/`stale`/`blocked`. |
|
|
123
|
-
| Web area | `src/web/` | `app.ts`
|
|
154
|
+
| Web area | `src/web/` | `app.ts` authenticates and dispatches (pure over the injected store and clock), `routes.ts` names the paths; `pages.ts`, `editor-pages.ts` and `html.ts` render; `editor.ts` reads the editor's forms and calls the shared rules; `signin-link.ts` and `sessions.ts` hold the sign-in; `theme.ts` the stylesheet; `command.ts` is `/web`. |
|
|
155
|
+
| Task rules | `src/reminders.ts`, `src/tracked.ts`, `src/price.ts`, `src/edit.ts`, `src/manage.ts`, `src/limits.ts` | What makes, edits, pauses, resumes and deletes a task, and the limits on it, Discord-free, for the commands and the web editor alike. |
|
|
124
156
|
|
|
125
157
|
## Configuration
|
|
126
158
|
|
|
@@ -134,11 +166,11 @@ The `__Host-` prefix, which would stop that, requires `Path=/` and so cannot be
|
|
|
134
166
|
|
|
135
167
|
## Not yet
|
|
136
168
|
|
|
137
|
-
- The rest of the web area (E5):
|
|
138
|
-
and the JSON task API. Until forget-me and the admin view exist, nobody can be taken off the
|
|
169
|
+
- The rest of the web area (E5): the admin view, lifting a decline block, forget-me (which would
|
|
170
|
+
erase a deleted task), sharing a task from the web, and the JSON task API. Until forget-me and the admin view exist, nobody can be taken off the
|
|
139
171
|
tracker, so a web session ends only by sign-out, expiry, or the membership re-check. Discord OAuth2 as a second sign-in method, if chosen (plan item 41).
|
|
140
172
|
- The city-hall Executor adapter (the execute lane).
|
|
141
|
-
- Editing
|
|
173
|
+
- Editing in Discord (the editor is on the web only), and changing a price tracker's page or `near` after it is made (make a new one); a free-form pattern for `price` (`near` is the safe subset: a user's regular expression run on a large page could hang the bot).
|
|
142
174
|
- An optional Discord-role gate (plan 5.5), and showing unconfirmed deliveries to admins anywhere but the log.
|
|
143
175
|
|
|
144
176
|
docket-core, docket-types and `@rackbops/styles` are `devDependencies`: `bun build` bundles them
|