A discord bot for teal.fm
discord tealfm music
at main 2.1 kB view raw
1import type { Context } from "hono"; 2 3import { Agent } from "@atproto/api"; 4import { isAtprotoDid, isAtprotoDidWeb } from "@atproto/did"; 5import { isValidHandle } from "@atproto/syntax"; 6import { env } from "@tealfmbot/common/constants"; 7import { logger } from "@tealfmbot/common/logger"; 8import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie"; 9 10import { client } from "./client.js"; 11 12export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0; 13 14export async function createSession(value: string) { 15 const cookie = await generateSignedCookie("__teal_fm_bot_session", value, env.COOKIE_SECRET, { 16 path: "/", 17 secure: process.env.NODE_ENV === "production", 18 httpOnly: true, 19 sameSite: "lax", 20 maxAge: 60 * 60 * 24 * 7, 21 }); 22 23 return cookie; 24} 25 26export async function getSessionAgent(c: Context) { 27 c.header("Vary", "Cookie"); 28 const session = await getSignedCookie(c, env.COOKIE_SECRET, "__teal_fm_bot_session"); 29 if (!session) return null; 30 c.header("Cache-Control", `max-age=${MAX_AGE}, private`); 31 32 try { 33 const oauthSession = await client.restore(session); 34 return oauthSession ? new Agent(oauthSession) : null; 35 } catch (error) { 36 logger.warn({ error }, "oauth restore failed"); 37 deleteCookie(c, "__teal_fm_bot_session"); 38 return null; 39 } 40} 41 42export async function getSession(c: Context, e: typeof env) { 43 const session = await getSignedCookie(c, e.COOKIE_SECRET, "__teal_fm_bot_session"); 44 return session; 45} 46 47export function isValidUrl(url: string) { 48 if (URL.canParse(url)) { 49 const pdsUrl = new URL(url); 50 return pdsUrl.protocol === "http:" || pdsUrl.protocol === "https:"; 51 } 52 return false; 53} 54 55// https://github.com/bluesky-social/social-app/blob/main/src/lib/strings/handles.ts#L51 56export function validateIdentifier(input: string) { 57 if (typeof input !== "string") return false; 58 const results = { 59 validHandle: isValidHandle(input), 60 nonEmptyIdentifier: !input.trim(), 61 validDid: isAtprotoDid(input), 62 validUrl: isValidUrl(input), 63 validDidWeb: isAtprotoDidWeb(input), 64 }; 65 66 return Object.values(results).includes(true); 67}