@tuzelko/opencode-delayed-message 0.1.2 → 0.1.3
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 +9 -5
- package/gui-experimental.cjs +3 -4
- package/index.ts +22 -12
- package/package.json +1 -1
- package/rpc.ts +23 -0
package/README.md
CHANGED
|
@@ -85,9 +85,13 @@ Above the composer, while a message is pending:
|
|
|
85
85
|
⏰ Scheduled · 10:18
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Click it for the list: time, relative countdown, a preview of the text, and
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
Click it for the list: time, relative countdown, a preview of the text, and `✎` to edit
|
|
89
|
+
or `×` to cancel. Edit cancels the pending job and puts the whole command back into the
|
|
90
|
+
composer — `/delayed-message`, the scheduled time, and the full text — with the cursor in
|
|
91
|
+
the prompt. Adjust anything and submit to reschedule. The pill disappears when the
|
|
92
|
+
message is delivered or cancelled.
|
|
93
|
+
|
|
94
|
+
The terminal UI has no pill — there the transcript note is the only trace.
|
|
91
95
|
|
|
92
96
|
The pill follows the app language: English, Russian, German, French, and Chinese are
|
|
93
97
|
included. Transcript notes are English, because they are written for the model.
|
|
@@ -113,8 +117,8 @@ waits for the next time that directory loads.
|
|
|
113
117
|
24.8-day timer cap.
|
|
114
118
|
- On fire: `ctx.session.prompt({ sessionID, text })` — a real user prompt, so the session
|
|
115
119
|
wakes.
|
|
116
|
-
- The GUI talks to the server through an RPC: `pending`, `cancel`, `deliver`, and
|
|
117
|
-
events `scheduled`, `fired`, `cancelled`.
|
|
120
|
+
- The GUI talks to the server through an RPC: `pending`, `cancel`, `edit`, `deliver`, and
|
|
121
|
+
the events `scheduled`, `fired`, `cancelled`.
|
|
118
122
|
- The GUI main process mirrors `scheduled` jobs into its own storage and arms Node timers.
|
|
119
123
|
When one is due it calls `deliver` with the job's directory, which boots the location
|
|
120
124
|
and delivers the message even though the server-side timer is gone.
|
package/gui-experimental.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("solid-js/web"),t=require("solid-js"),n=require("effect"),r=require("@opencode/plugin/gui");var i=require("@opencode/plugin/rpc").Rpc.define({id:`delayed-message`,methods:{pending:{input:{type:`object`,properties:{sessionID:{type:`string`}},required:[`sessionID`],additionalProperties:!1},output:{type:`object`,properties:{jobs:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},fireAt:{type:`number`},preview:{type:`string`}},required:[`id`,`fireAt`,`preview`],additionalProperties:!1}}},required:[`jobs`],additionalProperties:!1}},cancel:{input:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1},output:{type:`object`,properties:{ok:{type:`boolean`}},required:[`ok`],additionalProperties:!1}},deliver:{input:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1},output:{type:`object`,properties:{status:{type:`string`}},required:[`status`],additionalProperties:!1}}},events:{scheduled:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`},fireAt:{type:`number`},preview:{type:`string`},directory:{type:`string`}},required:[`sessionID`,`id`,`fireAt`,`preview`,`directory`],additionalProperties:!1}},fired:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1}},cancelled:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1}}}}),a=(0,e.template)(`<div class=dm>`),o=(0,e.template)(`<div class=dm-row><div class=dm-body><div class=dm-when><span></span><span class=dm-left></span></div><div class=dm-text></div></div><button class=dm-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});let e=require("solid-js/web"),t=require("solid-js"),n=require("effect"),r=require("@opencode/plugin/gui");var i=require("@opencode/plugin/rpc").Rpc.define({id:`delayed-message`,methods:{pending:{input:{type:`object`,properties:{sessionID:{type:`string`}},required:[`sessionID`],additionalProperties:!1},output:{type:`object`,properties:{jobs:{type:`array`,items:{type:`object`,properties:{id:{type:`string`},fireAt:{type:`number`},preview:{type:`string`}},required:[`id`,`fireAt`,`preview`],additionalProperties:!1}}},required:[`jobs`],additionalProperties:!1}},cancel:{input:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1},output:{type:`object`,properties:{ok:{type:`boolean`}},required:[`ok`],additionalProperties:!1}},deliver:{input:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1},output:{type:`object`,properties:{status:{type:`string`}},required:[`status`],additionalProperties:!1}},edit:{input:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1},output:{type:`object`,properties:{ok:{type:`boolean`},text:{type:`string`},fireAt:{type:`number`}},required:[`ok`],additionalProperties:!1}}},events:{scheduled:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`},fireAt:{type:`number`},preview:{type:`string`},directory:{type:`string`}},required:[`sessionID`,`id`,`fireAt`,`preview`,`directory`],additionalProperties:!1}},fired:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1}},cancelled:{schema:{type:`object`,properties:{sessionID:{type:`string`},id:{type:`string`}},required:[`sessionID`,`id`],additionalProperties:!1}}}}),a=(0,e.template)(`<div class=dm>`),o=(0,e.template)(`<div class=dm-row><div class=dm-body><div class=dm-when><span></span><span class=dm-left></span></div><div class=dm-text></div></div><button class=dm-action>✎</button><button class=dm-action>×`),s=2**31-1,c=e=>String(e).padStart(2,`0`),l=e=>{let t=new Date(e),n=`${t.getFullYear()}-${c(t.getMonth()+1)}-${c(t.getDate())}`,r=`${c(t.getHours())}:${c(t.getMinutes())}`;return t.getSeconds()===0?`${n}T${r}`:`${n}T${r}:${c(t.getSeconds())}`},u=`
|
|
2
2
|
.dm { display: flex; flex-direction: column; min-width: 280px; max-width: 440px; }
|
|
3
3
|
.dm-row { display: flex; align-items: center; gap: 8px; padding: 8px 2px; }
|
|
4
4
|
.dm-row + .dm-row { border-top: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
|
|
@@ -6,6 +6,5 @@ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{val
|
|
|
6
6
|
.dm-when { display: flex; align-items: baseline; gap: 8px; font-variant-numeric: tabular-nums; }
|
|
7
7
|
.dm-left { opacity: .55; font-size: 12px; white-space: nowrap; }
|
|
8
8
|
.dm-text { opacity: .72; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
9
|
-
.dm-
|
|
10
|
-
.dm-
|
|
11
|
-
`,l=r.Plugin.define({i18n:{en:{"pill.one":`Scheduled · {{time}}`,"pill.many":`Scheduled: {{count}}`,"menu.title":`Scheduled messages`,"row.cancel":`Cancel`,"left.now":`now`,"left.days":`in {{days}}d {{hours}}h`,"left.hours":`in {{hours}}h {{minutes}}m`,"left.minutes":`in {{minutes}}m`},ru:{"pill.one":`Отложенное · {{time}}`,"pill.many":`Отложенных: {{count}}`,"menu.title":`Отложенные сообщения`,"row.cancel":`Отменить`,"left.now":`сейчас`,"left.days":`через {{days}} д {{hours}} ч`,"left.hours":`через {{hours}} ч {{minutes}} мин`,"left.minutes":`через {{minutes}} мин`},de:{"pill.one":`Geplant · {{time}}`,"pill.many":`Geplant: {{count}}`,"menu.title":`Geplante Nachrichten`,"row.cancel":`Abbrechen`,"left.now":`jetzt`,"left.days":`in {{days}} T {{hours}} Std.`,"left.hours":`in {{hours}} Std. {{minutes}} Min.`,"left.minutes":`in {{minutes}} Min.`},fr:{"pill.one":`Programmé · {{time}}`,"pill.many":`Programmés : {{count}}`,"menu.title":`Messages programmés`,"row.cancel":`Annuler`,"left.now":`maintenant`,"left.days":`dans {{days}} j {{hours}} h`,"left.hours":`dans {{hours}} h {{minutes}} min`,"left.minutes":`dans {{minutes}} min`},zh:{"pill.one":`已安排 · {{time}}`,"pill.many":`已安排:{{count}}`,"menu.title":`已安排的消息`,"row.cancel":`取消`,"left.now":`现在`,"left.days":`还有 {{days}} 天 {{hours}} 小时`,"left.hours":`还有 {{hours}} 小时 {{minutes}} 分钟`,"left.minutes":`还有 {{minutes}} 分钟`}},setup(n){let[s,l]=(0,t.createSignal)({}),u=(e,t)=>l(n=>({...n,[e]:[...(n[e]??[]).filter(e=>e.id!==t.id),t].sort((e,t)=>e.fireAt-t.fireAt)})),d=(e,t)=>l(n=>({...n,[e]:(n[e]??[]).filter(e=>e.id!==t)})),f=e=>new Date(e).toLocaleTimeString(n.locale.locale(),{hour:`2-digit`,minute:`2-digit`}),p=e=>new Date(e).toLocaleDateString(n.locale.locale(),{day:`2-digit`,month:`2-digit`}),m=e=>{let t=new Date(e),n=new Date,r=f(e);return t.toDateString()===n.toDateString()?r:`${p(e)} ${r}`},h=e=>{let t=e-Date.now();if(t<=0)return n.t(`left.now`);let r=Math.ceil(t/6e4),i=Math.floor(r/60),a=Math.floor(i/24);return a>0?n.t(`left.days`,{days:a,hours:i%24}):i>0?n.t(`left.hours`,{hours:i,minutes:r%60}):n.t(`left.minutes`,{minutes:r})},g=n.client.rpc(i);g.events.on(`scheduled`,e=>{u(e.data.sessionID,{id:e.data.id,fireAt:e.data.fireAt,preview:e.data.preview})}),g.events.on(`fired`,e=>{d(e.data.sessionID,e.data.id)}),g.events.on(`cancelled`,e=>{d(e.data.sessionID,e.data.id)});let _=async(e,t)=>{try{let{ok:n}=await g.cancel({sessionID:e,id:t});n&&d(e,t)}catch{}};(0,r.createKeyed)(()=>n.sessions.current()?.id,e=>{g.pending({sessionID:e}).then(({jobs:t})=>l(n=>({...n,[e]:t}))).catch(()=>{})}),n.add(r.Style,c),n.add(r.ComposerPill,{id:`pending`,show(e){let t=s()[e.session.id]??[];if(t.length!==0)return{label:t.length===1?n.t(`pill.one`,{time:f(t[0].fireAt)}):n.t(`pill.many`,{count:t.length}),icon:`notifications`,title:n.t(`menu.title`)}},menu:r=>(()=>{var i=a();return(0,e.insert)(i,(0,e.createComponent)(t.For,{get each(){return s()[r.session.id]??[]},children:t=>(()=>{var i=o(),a=i.firstChild,s=a.firstChild,c=s.firstChild,l=c.nextSibling,u=s.nextSibling,d=a.nextSibling;return(0,e.insert)(c,()=>m(t.fireAt)),(0,e.insert)(l,()=>h(t.fireAt)),(0,e.insert)(u,()=>t.preview),d.$$click=()=>void _(r.session.id,t.id),(0,e.effect)(t=>{var r=n.t(`row.cancel`),i=n.t(`row.cancel`);return r!==t.e&&(0,e.setAttribute)(d,`title`,t.e=r),i!==t.t&&(0,e.setAttribute)(d,`aria-label`,t.t=i),t},{e:void 0,t:void 0}),i})()})),i})()})},main(e){let t=n.Schema.Struct({id:n.Schema.String,sessionID:n.Schema.String,directory:n.Schema.String,fireAt:n.Schema.Number}),r=e.storage.store(`pending`,{schema:n.Schema.Array(t),initial:[]}),a=new Map,o=new Map,c=e=>{let t=a.get(e);t&&clearTimeout(t),a.delete(e)},l=e=>{c(e),o.delete(e),r.value.some(t=>t.id===e)&&r.set(r.value.filter(t=>t.id!==e))},u=e=>{c(e.id);let t=e.fireAt-Date.now();t>s-1?a.set(e.id,setTimeout(()=>u(e),s-1)):a.set(e.id,setTimeout(()=>void d(e),Math.max(0,t)))},d=async t=>{if(c(t.id),Date.now()<t.fireAt)u(t);else try{let{status:n}=await e.client.rpc(i).deliver({sessionID:t.sessionID,id:t.id},{location:{directory:t.directory}});if(n===`delivered`||n===`missing`){o.delete(t.id),l(t.id);return}if(n===`failed`){let e=(o.get(t.id)??0)+1;if(e>=5){o.delete(t.id),l(t.id);return}o.set(t.id,e)}a.set(t.id,setTimeout(()=>void d(t),n===`busy`?2e3:3e4))}catch{a.set(t.id,setTimeout(()=>void d(t),15e3))}},f=e=>{r.set([...r.value.filter(t=>t.id!==e.id),e]),u(e)},p=e=>{if(e.type===`rpc.delayed-message.scheduled`){let t=e.data;if(!t?.id||!t.sessionID||!t.fireAt||!t.directory)return;f({id:t.id,sessionID:t.sessionID,directory:t.directory,fireAt:t.fireAt})}else if(e.type===`rpc.delayed-message.fired`||e.type===`rpc.delayed-message.cancelled`){let t=e.data;t?.id&&l(t.id)}};for(let e of r.value)u(e);(async()=>{for(;!e.signal.aborted;){try{for await(let t of e.client.event.subscribe({signal:e.signal}))p(t)}catch{}if(e.signal.aborted)break;await new Promise(e=>setTimeout(e,3e3))}})(),e.onCleanup(()=>{for(let e of a.values())clearTimeout(e);a.clear()})}});(0,e.delegateEvents)([`click`]),exports.default=l;
|
|
9
|
+
.dm-action { flex: none; width: 22px; height: 22px; border: 0; border-radius: 6px; background: transparent; color: inherit; opacity: .45; cursor: pointer; font: inherit; line-height: 1; }
|
|
10
|
+
.dm-action:hover { opacity: 1; background: color-mix(in srgb, currentColor 14%, transparent); }`,d=r.Plugin.define({i18n:{en:{"pill.one":`Scheduled · {{time}}`,"pill.many":`Scheduled: {{count}}`,"menu.title":`Scheduled messages`,"row.cancel":`Cancel`,"row.edit":`Edit`,"left.now":`now`,"left.days":`in {{days}}d {{hours}}h`,"left.hours":`in {{hours}}h {{minutes}}m`,"left.minutes":`in {{minutes}}m`},ru:{"pill.one":`Отложенное · {{time}}`,"pill.many":`Отложенных: {{count}}`,"menu.title":`Отложенные сообщения`,"row.cancel":`Отменить`,"row.edit":`Изменить`,"left.now":`сейчас`,"left.days":`через {{days}} д {{hours}} ч`,"left.hours":`через {{hours}} ч {{minutes}} мин`,"left.minutes":`через {{minutes}} мин`},de:{"pill.one":`Geplant · {{time}}`,"pill.many":`Geplant: {{count}}`,"menu.title":`Geplante Nachrichten`,"row.cancel":`Abbrechen`,"row.edit":`Bearbeiten`,"left.now":`jetzt`,"left.days":`in {{days}} T {{hours}} Std.`,"left.hours":`in {{hours}} Std. {{minutes}} Min.`,"left.minutes":`in {{minutes}} Min.`},fr:{"pill.one":`Programmé · {{time}}`,"pill.many":`Programmés : {{count}}`,"menu.title":`Messages programmés`,"row.cancel":`Annuler`,"row.edit":`Modifier`,"left.now":`maintenant`,"left.days":`dans {{days}} j {{hours}} h`,"left.hours":`dans {{hours}} h {{minutes}} min`,"left.minutes":`dans {{minutes}} min`},zh:{"pill.one":`已安排 · {{time}}`,"pill.many":`已安排:{{count}}`,"menu.title":`已安排的消息`,"row.cancel":`取消`,"row.edit":`编辑`,"left.now":`现在`,"left.days":`还有 {{days}} 天 {{hours}} 小时`,"left.hours":`还有 {{hours}} 小时 {{minutes}} 分钟`,"left.minutes":`还有 {{minutes}} 分钟`}},setup(n){let[s,c]=(0,t.createSignal)({}),d=(e,t)=>c(n=>({...n,[e]:[...(n[e]??[]).filter(e=>e.id!==t.id),t].sort((e,t)=>e.fireAt-t.fireAt)})),f=(e,t)=>c(n=>({...n,[e]:(n[e]??[]).filter(e=>e.id!==t)})),p=e=>new Date(e).toLocaleTimeString(n.locale.locale(),{hour:`2-digit`,minute:`2-digit`}),m=e=>new Date(e).toLocaleDateString(n.locale.locale(),{day:`2-digit`,month:`2-digit`}),h=e=>{let t=new Date(e),n=new Date,r=p(e);return t.toDateString()===n.toDateString()?r:`${m(e)} ${r}`},g=e=>{let t=e-Date.now();if(t<=0)return n.t(`left.now`);let r=Math.ceil(t/6e4),i=Math.floor(r/60),a=Math.floor(i/24);return a>0?n.t(`left.days`,{days:a,hours:i%24}):i>0?n.t(`left.hours`,{hours:i,minutes:r%60}):n.t(`left.minutes`,{minutes:r})},_=n.client.rpc(i);_.events.on(`scheduled`,e=>{d(e.data.sessionID,{id:e.data.id,fireAt:e.data.fireAt,preview:e.data.preview})}),_.events.on(`fired`,e=>{f(e.data.sessionID,e.data.id)}),_.events.on(`cancelled`,e=>{f(e.data.sessionID,e.data.id)});let v=async(e,t)=>{try{let{ok:n}=await _.cancel({sessionID:e,id:t});n&&f(e,t)}catch{}};(0,r.createKeyed)(()=>n.sessions.current()?.id,e=>{_.pending({sessionID:e}).then(({jobs:t})=>c(n=>({...n,[e]:t}))).catch(()=>{})}),n.add(r.Style,u),n.add(r.ComposerPill,{id:`pending`,show(e){let t=s()[e.session.id]??[];if(t.length!==0)return{label:t.length===1?n.t(`pill.one`,{time:p(t[0].fireAt)}):n.t(`pill.many`,{count:t.length}),icon:`notifications`,title:n.t(`menu.title`)}},menu:r=>(()=>{var i=a();return(0,e.insert)(i,(0,e.createComponent)(t.For,{get each(){return s()[r.session.id]??[]},children:t=>(()=>{var i=o(),a=i.firstChild,s=a.firstChild,c=s.firstChild,u=c.nextSibling,d=s.nextSibling,p=a.nextSibling,m=p.nextSibling;return(0,e.insert)(c,()=>h(t.fireAt)),(0,e.insert)(u,()=>g(t.fireAt)),(0,e.insert)(d,()=>t.preview),p.$$click=()=>{_.edit({sessionID:r.session.id,id:t.id}).then(({ok:e,text:n,fireAt:i})=>{e&&n!==void 0&&i!==void 0&&(f(r.session.id,t.id),r.screen.composer.setText(`/delayed-message ${l(i)} ${n}`),r.close(),r.screen.composer.focus())}).catch(()=>{})},m.$$click=()=>void v(r.session.id,t.id),(0,e.effect)(t=>{var r=n.t(`row.edit`),i=n.t(`row.edit`),a=n.t(`row.cancel`),o=n.t(`row.cancel`);return r!==t.e&&(0,e.setAttribute)(p,`title`,t.e=r),i!==t.t&&(0,e.setAttribute)(p,`aria-label`,t.t=i),a!==t.a&&(0,e.setAttribute)(m,`title`,t.a=a),o!==t.o&&(0,e.setAttribute)(m,`aria-label`,t.o=o),t},{e:void 0,t:void 0,a:void 0,o:void 0}),i})()})),i})()})},main(e){let t=n.Schema.Struct({id:n.Schema.String,sessionID:n.Schema.String,directory:n.Schema.String,fireAt:n.Schema.Number}),r=e.storage.store(`pending`,{schema:n.Schema.Array(t),initial:[]}),a=new Map,o=new Map,c=e=>{let t=a.get(e);t&&clearTimeout(t),a.delete(e)},l=e=>{c(e),o.delete(e),r.value.some(t=>t.id===e)&&r.set(r.value.filter(t=>t.id!==e))},u=e=>{c(e.id);let t=e.fireAt-Date.now();t>s-1?a.set(e.id,setTimeout(()=>u(e),s-1)):a.set(e.id,setTimeout(()=>void d(e),Math.max(0,t)))},d=async t=>{if(c(t.id),Date.now()<t.fireAt)u(t);else try{let{status:n}=await e.client.rpc(i).deliver({sessionID:t.sessionID,id:t.id},{location:{directory:t.directory}});if(n===`delivered`||n===`missing`){o.delete(t.id),l(t.id);return}if(n===`failed`){let e=(o.get(t.id)??0)+1;if(e>=5){o.delete(t.id),l(t.id);return}o.set(t.id,e)}a.set(t.id,setTimeout(()=>void d(t),n===`busy`?2e3:3e4))}catch{a.set(t.id,setTimeout(()=>void d(t),15e3))}},f=e=>{r.set([...r.value.filter(t=>t.id!==e.id),e]),u(e)},p=e=>{if(e.type===`rpc.delayed-message.scheduled`){let t=e.data;if(!t?.id||!t.sessionID||!t.fireAt||!t.directory)return;f({id:t.id,sessionID:t.sessionID,directory:t.directory,fireAt:t.fireAt})}else if(e.type===`rpc.delayed-message.fired`||e.type===`rpc.delayed-message.cancelled`){let t=e.data;t?.id&&l(t.id)}};for(let e of r.value)u(e);(async()=>{for(;!e.signal.aborted;){try{for await(let t of e.client.event.subscribe({signal:e.signal}))p(t)}catch{}if(e.signal.aborted)break;await new Promise(e=>setTimeout(e,3e3))}})(),e.onCleanup(()=>{for(let e of a.values())clearTimeout(e);a.clear()})}});(0,e.delegateEvents)([`click`]),exports.default=d;
|
package/index.ts
CHANGED
|
@@ -87,18 +87,13 @@ export default Plugin.define({
|
|
|
87
87
|
},
|
|
88
88
|
cancel: async (input) => {
|
|
89
89
|
const { sessionID, id } = input as { sessionID: string; id: string }
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
timers.delete(id)
|
|
98
|
-
}
|
|
99
|
-
await ctx.storage.remove(key(id))
|
|
100
|
-
await rpc.events.emit("cancelled", { sessionID, id }).catch(() => {})
|
|
101
|
-
return { ok: true }
|
|
90
|
+
return { ok: (await withdraw(sessionID, id)) !== undefined }
|
|
91
|
+
},
|
|
92
|
+
edit: async (input) => {
|
|
93
|
+
const { sessionID, id } = input as { sessionID: string; id: string }
|
|
94
|
+
const job = await withdraw(sessionID, id)
|
|
95
|
+
if (!job) return { ok: false }
|
|
96
|
+
return { ok: true, text: job.message, fireAt: job.fireAt }
|
|
102
97
|
},
|
|
103
98
|
deliver: async (input) => {
|
|
104
99
|
const { sessionID, id } = input as { sessionID: string; id: string }
|
|
@@ -110,6 +105,21 @@ export default Plugin.define({
|
|
|
110
105
|
},
|
|
111
106
|
})
|
|
112
107
|
|
|
108
|
+
const withdraw = async (sessionID: string, id: string): Promise<Job | undefined> => {
|
|
109
|
+
if (claimed.has(id) || delivering.has(id)) return undefined
|
|
110
|
+
const stored = (await ctx.storage.get(key(id))) as Job | undefined
|
|
111
|
+
if (!stored || stored.sessionID !== sessionID) return undefined
|
|
112
|
+
claimed.add(id)
|
|
113
|
+
const timer = timers.get(id)
|
|
114
|
+
if (timer) {
|
|
115
|
+
clearTimeout(timer)
|
|
116
|
+
timers.delete(id)
|
|
117
|
+
}
|
|
118
|
+
await ctx.storage.remove(key(id))
|
|
119
|
+
await rpc.events.emit("cancelled", { sessionID, id }).catch(() => {})
|
|
120
|
+
return stored
|
|
121
|
+
}
|
|
122
|
+
|
|
113
123
|
const deliver = async (job: Job): Promise<Delivery> => {
|
|
114
124
|
if (claimed.has(job.id)) return "delivered"
|
|
115
125
|
if (delivering.has(job.id)) return "busy"
|
package/package.json
CHANGED
package/rpc.ts
CHANGED
|
@@ -70,6 +70,29 @@ export const Delayed = Rpc.define({
|
|
|
70
70
|
additionalProperties: false,
|
|
71
71
|
},
|
|
72
72
|
},
|
|
73
|
+
// Cancels the job and hands its full text back so the pill can put the
|
|
74
|
+
// command into the composer for editing.
|
|
75
|
+
edit: {
|
|
76
|
+
input: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
sessionID: { type: "string" },
|
|
80
|
+
id: { type: "string" },
|
|
81
|
+
},
|
|
82
|
+
required: ["sessionID", "id"],
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
output: {
|
|
86
|
+
type: "object",
|
|
87
|
+
properties: {
|
|
88
|
+
ok: { type: "boolean" },
|
|
89
|
+
text: { type: "string" },
|
|
90
|
+
fireAt: { type: "number" },
|
|
91
|
+
},
|
|
92
|
+
required: ["ok"],
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
73
96
|
},
|
|
74
97
|
events: {
|
|
75
98
|
scheduled: {
|