├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── deno.json ├── dev.ts ├── fresh.gen.ts ├── import_map.json ├── main.ts ├── routes └── index.tsx ├── static ├── favicon.ico ├── logo.svg ├── queue-reminder-slack.png └── queue-reminder-webui.png └── twind.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno", 4 | "sastan.twind-intellisense" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.unstable": true, 5 | "editor.defaultFormatter": "denoland.vscode-deno" 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2023 the Deno authors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple Slack reminder app using [Deno KV](https://deno.com/kv) queues and 2 | [Fresh](https://fresh.deno.dev/) 3 | 4 | ## Install 5 | 6 | Fork the repo. Then be sure to add the environmental variable 7 | [`SLACK_BOT_TOKEN`](https://api.slack.com/authentication/token-types). 8 | 9 | ## Usage 10 | 11 | Run the app with: 12 | 13 | ``` 14 | deno task start 15 | ``` 16 | 17 | If you go to localhost:8000 on your browser, you'll see a simple interface. Fill 18 | in your message, 19 | [Channel ID](https://www.wikihow.com/Find-a-Channel-ID-on-Slack-on-PC-or-Mac), 20 | and delay in seconds: 21 | 22 | ![](https://github.com/igorzi/queue-reminder/blob/main/static/queue-reminder-webui.png?raw=true) 23 | 24 | Hit remind. When the time comes, you'll see your message in Slack: 25 | 26 | ![](https://github.com/igorzi/queue-reminder/blob/main/static/queue-reminder-slack.png?raw=true) 27 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "lock": false, 3 | "tasks": { 4 | "start": "deno run -A --unstable --watch=static/,routes/ dev.ts" 5 | }, 6 | "importMap": "./import_map.json", 7 | "compilerOptions": { 8 | "jsx": "react-jsx", 9 | "jsxImportSource": "preact" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dev.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S deno run -A --watch=static/,routes/ 2 | 3 | import dev from "$fresh/dev.ts"; 4 | 5 | await dev(import.meta.url, "./main.ts"); 6 | -------------------------------------------------------------------------------- /fresh.gen.ts: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is generated by fresh. 2 | // This file SHOULD be checked into source version control. 3 | // This file is automatically updated during development when running `dev.ts`. 4 | 5 | import config from "./deno.json" assert { type: "json" }; 6 | import * as $0 from "./routes/index.tsx"; 7 | 8 | const manifest = { 9 | routes: { 10 | "./routes/index.tsx": $0, 11 | }, 12 | islands: {}, 13 | baseUrl: import.meta.url, 14 | config, 15 | }; 16 | 17 | export default manifest; 18 | -------------------------------------------------------------------------------- /import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "$fresh/": "https://deno.land/x/fresh@1.1.6/", 4 | "preact": "https://esm.sh/preact@10.13.1", 5 | "preact/": "https://esm.sh/preact@10.13.1/", 6 | "preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.6", 7 | "@preact/signals": "https://esm.sh/*@preact/signals@1.1.3", 8 | "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3", 9 | "twind": "https://esm.sh/twind@0.16.19", 10 | "twind/": "https://esm.sh/twind@0.16.19/", 11 | "$std/": "https://deno.land/std@0.187.0/" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | 7 | import "$std/dotenv/load.ts"; 8 | 9 | import { start } from "$fresh/server.ts"; 10 | import manifest from "./fresh.gen.ts"; 11 | 12 | import twindPlugin from "$fresh/plugins/twind.ts"; 13 | import twindConfig from "./twind.config.ts"; 14 | 15 | await start(manifest, { plugins: [twindPlugin(twindConfig)] }); 16 | -------------------------------------------------------------------------------- /routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { Handlers, PageProps } from "$fresh/server.ts"; 2 | import { SlackAPI } from "https://deno.land/x/deno_slack_api@2.1.1/mod.ts"; 3 | 4 | const kv = await Deno.openKv(); 5 | 6 | const token = Deno.env.get("SLACK_BOT_TOKEN") as string; 7 | const slackAPI = SlackAPI(token); 8 | 9 | kv.listenQueue(async (msg) => { 10 | const channel = msg.channel; 11 | const text = msg.text; 12 | const res = await postToChannel(channel, text); 13 | }); 14 | 15 | async function postToChannel(channel: string, text: string): Promise { 16 | const res = await slackAPI.chat.postMessage({ 17 | channel, 18 | text, 19 | blocks: [ 20 | { 21 | type: "section", 22 | text: { 23 | type: "mrkdwn", 24 | text, 25 | }, 26 | }, 27 | ], 28 | }); 29 | return res; 30 | } 31 | 32 | export const handler: Handlers = { 33 | async GET(req, ctx) { 34 | const url = new URL(req.url); 35 | const text = url.searchParams.get("text"); 36 | const channel = url.searchParams.get("channel"); 37 | const delay = Number(url.searchParams.get("delay")) * 1000; 38 | await kv.enqueue({ text, channel }, { delay }); 39 | return ctx.render({}); 40 | }, 41 | }; 42 | 43 | export default function Page({ data }: PageProps) { 44 | return ( 45 |
46 |
57 |

58 | Set a Reminder 59 |

60 |
61 | 71 | 83 |
84 |
85 | 95 | 107 |
108 |
109 | 119 | 132 |
133 | 148 |
149 |
150 | ); 151 | } 152 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorzi/queue-reminder/effbbdce8af1f79f75245841820d46f3121a81d4/static/favicon.ico -------------------------------------------------------------------------------- /static/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /static/queue-reminder-slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorzi/queue-reminder/effbbdce8af1f79f75245841820d46f3121a81d4/static/queue-reminder-slack.png -------------------------------------------------------------------------------- /static/queue-reminder-webui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/igorzi/queue-reminder/effbbdce8af1f79f75245841820d46f3121a81d4/static/queue-reminder-webui.png -------------------------------------------------------------------------------- /twind.config.ts: -------------------------------------------------------------------------------- 1 | import { Options } from "$fresh/plugins/twind.ts"; 2 | 3 | export default { 4 | selfURL: import.meta.url, 5 | } as Options; 6 | --------------------------------------------------------------------------------