diff --git a/og-image/api/_lib/challenge-template.ts b/og-image/api/_lib/challenge-template.ts new file mode 100644 index 00000000..b09d9ca2 --- /dev/null +++ b/og-image/api/_lib/challenge-template.ts @@ -0,0 +1,185 @@ +import { sanitizeHtml } from './sanitizer' +import { ParsedRequest } from './types' + +function getCss(theme: string, fontSize: string) { + let background = 'white' + let foreground = 'black' + let radial = 'lightgray' + + if (theme === 'dark') { + background = 'black' + foreground = 'white' + radial = 'dimgray' + } + // To use Readex Pro: `font-family: 'Readex Pro', sans-serif;` + return ` + @import url('https://fonts.googleapis.com/css2?family=Major+Mono+Display&family=Readex+Pro:wght@400;700&display=swap'); + + body { + background: ${background}; + background-image: radial-gradient(circle at 25px 25px, ${radial} 2%, transparent 0%), radial-gradient(circle at 75px 75px, ${radial} 2%, transparent 0%); + background-size: 100px 100px; + height: 100vh; + font-family: "Readex Pro", sans-serif; + } + + code { + color: #D400FF; + font-family: 'Vera'; + white-space: pre-wrap; + letter-spacing: -5px; + } + + code:before, code:after { + content: '\`'; + } + + .logo-wrapper { + display: flex; + align-items: center; + align-content: center; + justify-content: center; + justify-items: center; + } + + .logo { + margin: 0 75px; + } + + .plus { + color: #BBB; + font-family: Times New Roman, Verdana; + font-size: 100px; + } + + .spacer { + margin: 150px; + } + + .emoji { + height: 1em; + width: 1em; + margin: 0 .05em 0 .1em; + vertical-align: -0.1em; + } + + .heading { + font-family: 'Major Mono Display', monospace; + font-size: ${sanitizeHtml(fontSize)}; + font-style: normal; + color: ${foreground}; + line-height: 1.8; + } + + .font-major-mono { + font-family: "Major Mono Display", monospace; + } + + .text-primary { + color: #11b981; + } + ` +} + +export function getChallengeHtml(parsedReq: ParsedRequest) { + const { + theme, + fontSize, + question, + creatorName, + // creatorUsername, + creatorAvatarUrl, + challengerAmount, + challengerOutcome, + creatorAmount, + creatorOutcome, + } = parsedReq + const MAX_QUESTION_CHARS = 85 + const truncatedQuestion = + question.length > MAX_QUESTION_CHARS + ? question.slice(0, MAX_QUESTION_CHARS) + '...' + : question + const hideAvatar = creatorAvatarUrl ? '' : 'hidden' + return ` + + + + Generated Image + + + + + +
+ + +
+
+ ${truncatedQuestion} +
+
+
+
+ +
+

${creatorName}

+
+
+
${'M$' + creatorAmount}
+
${'on'}
+
${creatorOutcome}
+
+
+ ⚔️ +
+
+
+ +
+

You

+ +
+
+
${'M$' + challengerAmount}
+
${'on'}
+
${challengerOutcome}
+
+
+ +
+
+ +
+ + + +
+ + + +` +} diff --git a/og-image/api/_lib/parser.ts b/og-image/api/_lib/parser.ts index a335fe48..bcb38ffc 100644 --- a/og-image/api/_lib/parser.ts +++ b/og-image/api/_lib/parser.ts @@ -22,8 +22,10 @@ export function parseRequest(req: IncomingMessage) { creatorAvatarUrl, // Challenge attributes: - challengeAmount, - challengeOutcome, + challengerAmount, + challengerOutcome, + creatorAmount, + creatorOutcome, } = query || {} if (Array.isArray(fontSize)) { @@ -71,8 +73,10 @@ export function parseRequest(req: IncomingMessage) { creatorName: getString(creatorName) || 'Manifold Markets', creatorUsername: getString(creatorUsername) || 'ManifoldMarkets', creatorAvatarUrl: getString(creatorAvatarUrl) || '', - challengeAmount: getString(challengeAmount) || '', - challengeOutcome: getString(challengeOutcome) || '', + challengerAmount: getString(challengerAmount) || '', + challengerOutcome: getString(challengerOutcome) || '', + creatorAmount: getString(creatorAmount) || '', + creatorOutcome: getString(creatorOutcome) || '', } parsedRequest.images = getDefaultImages(parsedRequest.images) return parsedRequest diff --git a/og-image/api/_lib/template.ts b/og-image/api/_lib/template.ts index 70f692d8..1fe54554 100644 --- a/og-image/api/_lib/template.ts +++ b/og-image/api/_lib/template.ts @@ -91,16 +91,13 @@ export function getHtml(parsedReq: ParsedRequest) { creatorName, creatorUsername, creatorAvatarUrl, - challengeAmount, - challengeOutcome, } = parsedReq - const MAX_QUESTION_CHARS = challengeAmount ? 85 : 100 + const MAX_QUESTION_CHARS = 100 const truncatedQuestion = question.length > MAX_QUESTION_CHARS ? question.slice(0, MAX_QUESTION_CHARS) + '...' : question const hideAvatar = creatorAvatarUrl ? '' : 'hidden' - const hideNonChallengeElements = challengeAmount !== '' ? 'hidden' : '' return ` @@ -147,29 +144,17 @@ export function getHtml(parsedReq: ParsedRequest) {
-
- ${challengeAmount ? '⚔️ Bet against me ⚔️' : ''} -
- ${truncatedQuestion} -
+
+ ${truncatedQuestion}
-
${ - challengeAmount ? 'M$' + challengeAmount : probability - }
- -
-
${challengeOutcome ? 'on' : ''}
-
${challengeOutcome ?? ''}
-
-
${ - probability !== '' ? 'chance' : '' - }
+
${probability}
+
${probability !== '' ? 'chance' : ''}
-
+
${metadata}
diff --git a/og-image/api/_lib/types.ts b/og-image/api/_lib/types.ts index eb8b412d..565db1b2 100644 --- a/og-image/api/_lib/types.ts +++ b/og-image/api/_lib/types.ts @@ -18,6 +18,9 @@ export interface ParsedRequest { creatorName: string creatorUsername: string creatorAvatarUrl: string - challengeAmount: string - challengeOutcome: string + // Challenge attributes: + challengerAmount: string + challengerOutcome: string + creatorAmount: string + creatorOutcome: string } diff --git a/og-image/api/index.ts b/og-image/api/index.ts index 467afcc9..1f1a837c 100644 --- a/og-image/api/index.ts +++ b/og-image/api/index.ts @@ -1,36 +1,38 @@ -import { IncomingMessage, ServerResponse } from "http"; -import { parseRequest } from "./_lib/parser"; -import { getScreenshot } from "./_lib/chromium"; -import { getHtml } from "./_lib/template"; +import { IncomingMessage, ServerResponse } from 'http' +import { parseRequest } from './_lib/parser' +import { getScreenshot } from './_lib/chromium' +import { getHtml } from './_lib/template' +import { getChallengeHtml } from './_lib/challenge-template' -const isDev = !process.env.AWS_REGION; -const isHtmlDebug = process.env.OG_HTML_DEBUG === "1"; +const isDev = !process.env.AWS_REGION +const isHtmlDebug = process.env.OG_HTML_DEBUG === '1' export default async function handler( req: IncomingMessage, res: ServerResponse ) { try { - const parsedReq = parseRequest(req); - const html = getHtml(parsedReq); + const parsedReq = parseRequest(req) + let html = getHtml(parsedReq) + if (parsedReq.challengerOutcome) html = getChallengeHtml(parsedReq) if (isHtmlDebug) { - res.setHeader("Content-Type", "text/html"); - res.end(html); - return; + res.setHeader('Content-Type', 'text/html') + res.end(html) + return } - const { fileType } = parsedReq; - const file = await getScreenshot(html, fileType, isDev); - res.statusCode = 200; - res.setHeader("Content-Type", `image/${fileType}`); + const { fileType } = parsedReq + const file = await getScreenshot(html, fileType, isDev) + res.statusCode = 200 + res.setHeader('Content-Type', `image/${fileType}`) res.setHeader( - "Cache-Control", + 'Cache-Control', `public, immutable, no-transform, s-maxage=31536000, max-age=31536000` - ); - res.end(file); + ) + res.end(file) } catch (e) { - res.statusCode = 500; - res.setHeader("Content-Type", "text/html"); - res.end("

Internal Error

Sorry, there was a problem

"); - console.error(e); + res.statusCode = 500 + res.setHeader('Content-Type', 'text/html') + res.end('

Internal Error

Sorry, there was a problem

') + console.error(e) } }