mailer/cli/send.ts

131 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Command, EnumType } from '@cliffy/command/mod.ts'
import { Input } from '@cliffy/prompt/mod.ts'
import { Contact } from '../src/contact.ts'
import { send } from '../src/send.ts'
import type { Mail, Template } from '../types.ts'
import type { JSX } from 'preact'
const templates: Map<
string,
Template<
(props: Record<string, unknown>) => JSX.Element,
Record<string, unknown>
>
> = new Map()
const templatesDir = import.meta.resolve('../templates').slice('file://'.length)
//Load templates dynamicaly
for await (
const template of Deno.readDir(templatesDir)
) {
if (template.isFile && template.name.endsWith('.tsx')) {
const modPath = new URL(
template.name,
`${import.meta.resolve('../templates')}/`,
)
const mod = await import(modPath.href)
templates.set(mod.default.name, mod.default)
}
}
const templateType = new EnumType([...templates.keys()])
export const cmd = new Command()
.name('send')
.description('Send a mail.')
.type('template', templateType)
.option(
'-f, --from <account:string>',
'From mail account or short name from config.',
{ default: '!me' },
)
.option('-r, --recipient <recipient:string>', 'Recipient (to) of the mail.', {
required: true,
collect: true,
})
.option('--cc <recipient:string>', 'Copy carbon.', { collect: true })
.option('--cci <recipient:string>', 'Copy carbon invisible.', {
collect: true,
})
.option('-a, --attachments <file:file>', 'Attachments.', { collect: true })
.option('-t, --template <name:template>', 'HTML template from config', {
default: 'message',
})
.arguments('<subject:string>')
.action(
async ({ from, recipient, cc, cci, attachments, template }, subject) => {
const fromContact: Contact = await (async () => {
if (from === '!me') {
const whoami = new Deno.Command('whoami', {
stderr: 'inherit',
})
const { stdout } = await whoami.output()
const rawName = new TextDecoder().decode(stdout).trim()
const name = encodeURIComponent(rawName)
return new Contact({
name: `${name} de Cohabit`,
address: `_${name}_@cohabit.fr`,
})
}
if (from.startsWith('!')) {
//@ts-ignore try expand
return expand(from.slice(1))
}
return Contact.fromString(from)
})()
const selectedTemplate = templates.get(template)!
type Props = typeof selectedTemplate
const props: Partial<Props> = {}
for (const prop of selectedTemplate.props) {
if (prop.multiline) {
console.log(
`%c?%c ${prop.description} %c[end input with "!EOL" on a new line] %c`,
'color: yellow;',
'font-weight: bold',
'color: white',
'color: blue',
)
const decoder = new TextDecoder()
for await (const chunk of Deno.stdin.readable) {
const text = decoder.decode(chunk)
if (text.startsWith('!EOF\n')) {
break
}
//@ts-ignore TODO fix type inference
props[prop.tag] += text
}
//@ts-ignore TODO fix type inference
if (props[prop.tag].startsWith('undefined')) {
//@ts-ignore TODO fix type inference
props[prop.tag] = props[prop.tag].slice('undefined'.length)
}
} else {
const value = await Input.prompt({
message: prop.description,
})
//@ts-ignore TODO fix type inference
props[prop.tag] = value
}
}
const mail: Mail = {
from: fromContact,
to: recipient.map((to) => Contact.fromString(to)),
subject,
body: selectedTemplate.builder(props)!,
options: {
cc: cc?.map(Contact.fromString) ?? [],
cci: cci?.map(Contact.fromString) ?? [],
attachments: attachments ?? [],
},
}
await send(mail)
},
)