gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit3251488a3251488atracker#7: /my/shows (followed shows, newest follow first, poster, title, last watched SxxEyy); gate can take screenshots (TRACKER_GATE_SHOTS)mre3251488a/plugins/smtp/Mailer.hl

3.4 KB

  1. \* hl:smtp — the SMTP submission client.
  2. import { Mailer } from 'hl:smtp'
  3. m = new Mailer('mail.example.com', 587, {
  4. user = '[email protected]'
  5. pass = '…'
  6. from = '[email protected]'
  7. })
  8. m.send({ to = '[email protected]' subject = 'Hello' text = 'Hi' })
  9. Non-blocking: send() answers a job id, the OUTCOME arrives later — drain
  10. it with `for (r of m.results())`, or deliver() it as the declared events
  11. below while the program keeps running. *\
  12. /* The first three declared properties ARE the positional signature (SPEC.md
  13. "The file root is the constructor"), so `new Mailer(host, port, options)`
  14. reads as written. */
  15. \* One send was ACCEPTED by the server. r = { id, ok, code, stage, message,
  16. secure, to }. *\
  17. emit sent(
  18. Hybrid r \\ the result record for one message
  19. )
  20. \* One send FAILED — refused, TLS problem, timeout. Same record shape;
  21. `stage` names where it died. *\
  22. emit failed(
  23. Hybrid r \\ the result record for one message
  24. )
  25. String host = "127.0.0.1" \\ the submission server
  26. Number port = 587 \\ 587 STARTTLS, 465 implicit TLS
  27. Hybrid options = null \\ { user, pass, from, … } — credentials and defaults
  28. \* The plugin's handle for this mailer — set by the run body; a send() before
  29. construction finished is impossible, the root IS the constructor. *\
  30. Number handle = 0
  31. \* SEND — non-blocking. Validated and rendered HERE, on this thread, so a
  32. refusal (a line break in to/subject, a missing recipient, an address
  33. without an @) is a located error at YOUR line, through `on Error` like any
  34. plugin failure. Answers the job id; the outcome arrives on the result
  35. source. msg: { to (String or list), subject, text, html, from }. *\
  36. send(
  37. Hybrid msg \\ the message — to/subject/text/html/from
  38. ) {
  39. return __native("smtp.send", handle, msg)
  40. }
  41. \* THE RESULT SOURCE, drained: `for (r of m.results())` yields one result per
  42. accepted send and ENDS when every send is answered — which is what lets a
  43. script that only sends mail terminate. *\
  44. results() {
  45. return __native("smtp.results", handle)
  46. }
  47. \* THE RESULT SOURCE, delivered: results arrive as the declared sent/failed
  48. events while the program goes on — a server keeps serving with mail in
  49. flight. Use this OR results(), never both: they take from one queue. *\
  50. deliver() {
  51. __native("smtp.mark_registered", handle)
  52. __native("eventloop.register", __native("smtp.results", handle), "mail")
  53. }
  54. \* How many sends have not been answered yet. *\
  55. pending() {
  56. return __native("smtp.pending", handle)
  57. }
  58. \* Stop the sender thread. A job already in conversation finishes. *\
  59. close() {
  60. __native("smtp.close", handle)
  61. handle = 0
  62. }
  63. // The split every caller of deliver() wants: one event per outcome. `mail`
  64. // is delivered at THIS instance by the loop, and the re-emit dispatches at
  65. // it too, so an instance-scoped `on m.sent(r)` in the constructing program
  66. // hears it.
  67. on mail(r) {
  68. if (r.ok == true) {
  69. emit sent(r)
  70. } else {
  71. emit failed(r)
  72. }
  73. }
  74. // Open the mailer and start its sender thread. A METHOD, called from the run
  75. // body — never a root assignment: a bare `handle = …` at the root would be a
  76. // property INITIALIZER, and initializers run BEFORE positional args bind
  77. // (construction order 1 vs 4) — it would open a mailer for the DEFAULT host.
  78. // Same shape as hl:http1's listen().
  79. open() {
  80. handle = __native("smtp.open", host, port, options)
  81. }
  82. // Run body — the constructor's last act.
  83. open()

Branches

Latest commits

  • 3251488atracker#7: /my/shows (followed shows, newest follow first, poster, title, last watched SxxEyy); gate can take screenshots (TRACKER_GATE_SHOTS)mre
  • 44b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre
  • 91c9fc8ctracker#5: /unwatched — unwatched released episodes of followed shows, newest firstmre
  • a97c0295tracker#4: show page /shows/:slug (header, seasons, episodes, watch checks) + tools/relink-episode-seasons.hlmre
  • 05f407c5tracker: no border on any button except inverted ones (Log out, ident status and identities too); header brand weight 100mre
  • 17375427tracker#2: tools/migrate.hl + tools/verify.hl — old MongoDB data into mpackdb with new idsmre
  • 31aac936tracker#3: no border on the header and on filled buttons; inverted buttons keep theirsmre
  • 2ad9d29ctracker#1: login exactly like calendar (identity selector in the header, empty homepage)mre
  • 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre