@rackbops/plugin-tracker 0.8.0 → 0.10.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.
Files changed (3) hide show
  1. package/README.md +40 -11
  2. package/dist/plugin.js +1473 -564
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -46,11 +46,27 @@ A pressed DM is edited to say what happened.
46
46
 
47
47
  **Pause after failed DMs** (plan 5.5). Three DMs in a row that the host says cannot be delivered
48
48
  pause the person's delivery and every active task that would DM them, theirs and the ones they
49
- receive (a `paused` event in each task's history); their other runs due in that tick are held, not
50
- failed. The owner of a task paused for a recipient is DMed once and sees why in `/tasks`; the task
49
+ receive (a `paused` event in each task's history); a DM to them still owed is deferred, not failed.
50
+ The owner of a task paused for a recipient is DMed once and sees why in `/tasks`; the task
51
51
  resumes when the recipient next uses the tracker, or `/task resume` goes on without them. A person
52
52
  paused for their own DMs is told, and resumed, the next time they use a command or button. A DM
53
- that goes through clears the count.
53
+ that goes through clears the count. Only a DM the host says cannot reach that person (Discord's
54
+ 50007, or 10013 for an account that is gone) counts, once per run: docket fails it for good at once. A DM refused for
55
+ its content is retried by docket (three tries, a minute apart and doubling) and does not count; nor
56
+ does one whose outcome is unknown (see Delivery below).
57
+
58
+ **Delivery** (docket 0.4.0). docket records each DM in the store's `deliveries` table before it is
59
+ sent (a claim) and settles it after: sent, failed (for good, or owed and retried later), or
60
+ unconfirmed -- the send may or may not have reached Discord, so it is never resent, and a warning is
61
+ logged. A run has fired once its record is written; the other recipients of a run still get their
62
+ copies when one of them cannot be reached, and the run is `done`, not `failed`, whatever a DM did.
63
+ At start the plugin runs docket's `recover()`: a run left running is requeued, and a claim left open
64
+ by a stop mid-send is settled unconfirmed and logged.
65
+
66
+ **One task at a time.** A task's runs, the answers to them (buttons, `/task done`, Reply) and its
67
+ edits (the editor, pause, resume, delete, a zone or hour change) take that task's lock
68
+ (`src/locks.ts`), so none of them overlap. The notify and poll ticks run one pass per task that has
69
+ work; an answer that lands while its own task runs waits for that pass.
54
70
 
55
71
  ## Web area
56
72
 
@@ -72,6 +88,7 @@ served at a hashed path, cached for a year).
72
88
  | `/forget` | Forget me (below): what it deletes (GET); a POST asks for the confirmation; a POST with `confirm=yes` and the word `forget` erases. Linked from Settings. |
73
89
  | `/admin` | Admins only: everyone on the list, the decline blocks in force (each with Lift), and a form to allow a person by Discord id. |
74
90
  | `/admin/tasks` | Admins only: every task in the store, with its owner, status and how many receive it, each linking to its page. |
91
+ | `/admin/deliveries` | Admins only: the DMs that did not arrive (below), newest first. |
75
92
  | `/admin/people/<id>` | Admins only: one person -- Discord id, registration, zone and hour, delivery, task counts, their tasks -- with Make admin or Revoke admin, Resume delivery (when paused), and Remove from the tracker. |
76
93
  | `/admin/allow`, `/admin/people/<id>/grant`, `/revoke`, `/resume-delivery`, `/forget`, `/admin/blocks/<id>/lift`, `/admin/tokens/<id>/revoke` | Admins only, POST only: the acts below. |
77
94
  | `/api/v1/...` | The JSON task API (below): bearer tokens only, never the cookie. |
@@ -170,6 +187,15 @@ the function the command path runs:
170
187
  secret). It stops working at once; the owner is not told. Logged by token id.
171
188
  - **Remove from the tracker**: forget-me for that person (below), with the same confirmation.
172
189
 
190
+ **Deliveries** (0.10.0). `/admin/deliveries`, linked from every admin page, lists the DMs that
191
+ settled `failed`, `unconfirmed` or `deferred` in the last 30 days, newest first, at most 200 rows
192
+ (the page says how many matched when the bound cuts it). Each row: the task (its title, linking to
193
+ its page, its id and owner), the run's due time, the recipient (linking to their admin page), the
194
+ status, attempts and deferrals, the error text, and when it settled. `unconfirmed` means the DM may
195
+ have gone out and is never resent; `deferred` is still owed (a recipient whose delivery is paused,
196
+ say). Before 0.10.0 these showed only in the bot log, where the warnings still go. The page is
197
+ read-only, and like every admin page answers anyone who is not an admin with the unknown page's 404.
198
+
173
199
  Not here yet, though plan 5.10 lists them: grants, budgets and retry (they belong to the execute
174
200
  lane, which is not built) and an admin pause of someone else's task.
175
201
 
@@ -179,10 +205,11 @@ area). Two posts: the first asks, the second must carry `confirm=yes` and the wo
179
205
  in one SQLite transaction in the write queue, **deleted, not archived**:
180
206
 
181
207
  - every task they own, archived ones too, with everything under it: runs, run events, replies
182
- (anyone's), history, series, recipients, delivery claims and pauses;
208
+ (anyone's), history, series, recipients, deliveries (anyone's), charges and pauses;
183
209
  - on everyone else's tasks: their recipient rows, their replies and answers, the history rows they
184
- made or that name them (an invitation of them, a pause for them), the run events and delivery
185
- claims of DMs to them, and their pause rows;
210
+ made or that name them (an invitation of them, a pause for them), the run events and deliveries
211
+ of DMs to them (docket's `deleteDeliveries`), charges made for them, and their pause rows;
212
+ - the budget notices kept for them (`budget:person:<id>:...`);
186
213
  - every decline block they are either side of;
187
214
  - their admission, delivery health, web sessions, unused sign-in links and API tokens, and their
188
215
  person row.
@@ -193,7 +220,8 @@ id -- `admitted_by` of someone they admitted, and `lifted_by` of a block they li
193
220
  `forgotten` ("an admin since forgotten"; null already means "the configuration"). Text is matched
194
221
  only in the forms the code writes a person's id into -- docket's consent rows (`u5`, `u5 24h`), the
195
222
  tracker's `u5: ...` removals and `delivery to {u5} paused` pauses, docket's `u5 <message>` delivery
196
- events, and the notifier's error phrases (`recipient u5 cannot be messaged`, ...) -- never as any
223
+ events, and the notifier's error phrases (`recipient u5 cannot be messaged`, ...; both written only
224
+ before 0.9.0, when a run's own row recorded its DMs) -- never as any
197
225
  id-like word, so an address with `/u5/` in someone else's text is left alone. A history row or a
198
226
  delivery event of theirs is deleted; the error of someone else's run keeps its row and has the id
199
227
  replaced with `(forgotten)`. A pause for a recipient now names them by id and shows their current
@@ -359,11 +387,12 @@ $ curl -s https://clerk.example.com/tracker/api/v1/tasks/t9 -H "Authorization: B
359
387
 
360
388
  | Piece | File | Notes |
361
389
  |---|---|---|
362
- | 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. |
363
- | 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. |
390
+ | 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. Schema 5 (0.9.0) is docket 0.4.0's: a run's `record`, a series point's `key`, `deliveries` (from `delivery_claims`), `usage` and `notices`. `store.test.ts` runs docket's `STORE_CONTRACT` against it. |
391
+ | Notify lane | `src/notify-lane.ts` | docket's `Lanes.tickNotify` on two host ticks, every 60 s, one pass per task with work (a due run, a run in flight, a DM owed or claimed), each under that task's lock through a view of the store limited to that task: `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. |
364
392
  | 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. |
365
393
  | 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. |
366
- | 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. |
394
+ | Delivery | `src/notifier.ts`, `src/buttons.ts` | docket's `Notifier` over `host.dm`, with the buttons. docket claims each (run, person) in the store's `deliveries` before the DM and settles it after. The notifier maps the host's answers to docket's errors: cannot be messaged (50007) or an unknown user is `DeliveryFailedError(msg, true)` (failed for good; 50007 and 10013 count toward the pause); a message refused for its content is a plain `DeliveryFailedError` (retried); a paused person is `ExecutorUnavailableError` (deferred); anything else is rethrown, so docket settles it unconfirmed and never resends it. |
395
+ | Task lock | `src/locks.ts` | One task at a time: its runs, answers and edits never overlap (docket 0.4.0 asks the host to serialize per task). |
367
396
  | Pause | `src/delivery-health.ts` | The per-person failure count, the pause and the resume. |
368
397
  | 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. |
369
398
  | 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. |
@@ -392,7 +421,7 @@ $ curl -s https://clerk.example.com/tracker/api/v1/tasks/t9 -H "Authorization: B
392
421
  method, if chosen (plan item 41).
393
422
  - The city-hall Executor adapter (the execute lane).
394
423
  - 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).
395
- - An optional Discord-role gate (plan 5.5), and showing unconfirmed deliveries to admins anywhere but the log.
424
+ - An optional Discord-role gate (plan 5.5), and showing unconfirmed deliveries to admins anywhere but the log, or a run's deliveries in its history.
396
425
 
397
426
  docket-core, docket-types and `@rackbops/styles` are `devDependencies`: `bun build` bundles them
398
427
  into `dist/plugin.js` (the theme's CSS as text), and the bot loads that file without installing