posts "question of the day" to a discord webhook

Updated Google Sheets Apps Script Code.gs to fetch themes

+62 -84
+62 -2
Code.gs
··· 1 1 // Google Sheets Apps Script 2 - function doGet() { 2 + function doGet(e) { 3 + const endpoint = e.parameter.endpoint; 4 + 5 + switch (endpoint) { 6 + case "questions": 7 + return getQuestions(); 8 + case "themes": 9 + return getThemes(); 10 + default: 11 + return getQuestions(); 12 + } 13 + } 14 + 15 + function getThemes() { 3 16 const doc = SpreadsheetApp.getActiveSpreadsheet(); 4 - const sheetNames = ["Default", "Halloween"]; 17 + const themeSheet = doc.getSheetByName("Themes"); 18 + if (!themeSheet) { 19 + throw new Error( 20 + `Sheet "Themes" not found. Please ensure a sheet with this name exists.`, 21 + ); 22 + } 23 + 24 + const range = themeSheet.getDataRange(); 25 + const values = range.getValues(); 26 + 27 + if (values.length < 2 || values[0].length < 2) { 28 + throw new Error( 29 + "Sheet must have at least one header row and one data column (Key + Value).", 30 + ); 31 + } 32 + 33 + const fieldKeys = values[0].slice(1); 34 + const dataRows = values.slice(1); 35 + const result = {}; 36 + 37 + dataRows.forEach((row) => { 38 + const mainKey = String(row[0]).trim().toLowerCase(); 39 + const fieldValues = row.slice(1); 40 + const nestedObject = {}; 41 + const numFields = Math.min(fieldKeys.length, fieldValues.length); 42 + 43 + for (let i = 0; i < numFields; i++) { 44 + nestedObject[fieldKeys[i]] = fieldValues[i]; 45 + } 46 + 47 + result[mainKey] = nestedObject; 48 + }); 49 + 50 + return ContentService.createTextOutput(JSON.stringify(result)) 51 + .setMimeType(ContentService.MimeType.JSON); 52 + } 53 + 54 + function getQuestions() { 55 + const doc = SpreadsheetApp.getActiveSpreadsheet(); 56 + const themeSheet = doc.getSheetByName("Themes"); 57 + if (!themeSheet) { 58 + throw new Error( 59 + `Sheet "Themes" not found. Please ensure a sheet with this name exists.`, 60 + ); 61 + } 62 + const sheetNames = themeSheet.getRange(2, 1, themeSheet.getLastRow() - 1) 63 + .getDisplayValues() 64 + .flat(); 5 65 const result = {}; 6 66 7 67 sheetNames.forEach((sheetName) => {
-82
utils/themes.ts
··· 1 - export interface Theme { 2 - // Styling, how the embed looks 3 - color?: number; 4 - image?: { url: string }; 5 - thumbnail?: { url: string }; 6 - title?: string; 7 - author?: { name: string; url: string }; 8 - footer?: { text: string }; 9 - // Date range for seasonal themes 10 - start?: Temporal.PlainMonthDay; 11 - end?: Temporal.PlainMonthDay; 12 - // Deno KV storage key for the theme 13 - kvKey: string[]; 14 - } 15 - 16 - export const themes: Record<string, Theme> = { 17 - default: { 18 - color: 0xFEF250, // lemon yellow 19 - image: { 20 - url: "https://i.pinimg.com/736x/86/43/cf/8643cf6d02b03cf426bf5c0020cd215b.jpg", 21 - }, 22 - thumbnail: { 23 - url: "https://media.discordapp.net/stickers/1399014806014398616.webp", 24 - }, 25 - title: `· · ─ · 𝒒𝒖𝒆𝒔𝒕𝒊𝒐𝒏 𝒐𝒇 𝒕𝒉𝒆 𝒅𝒂𝒚 · ─ · ·`, 26 - author: { 27 - name: ".•° ✿ °•. punchy .•° ✿ °•.", 28 - url: "https://tangled.sh/@timtinkers.online/discord-qotd-webhook", 29 - }, 30 - footer: { text: `𐔌՞. .՞𐦯` }, 31 - kvKey: ["defaultIndex"], 32 - }, 33 - 34 - halloween: { 35 - color: 0xF25C05, // pumpkin orange 36 - image: { 37 - url: "https://media.discordapp.net/attachments/1279476809653686324/1420214040667361321/tenor.gif?ex=68d494e5&is=68d34365&hm=c2cea6d29d7a58afbe732b7a11775b18f156940bc127aff817bbe005c6bbde38&=", 38 - }, 39 - thumbnail: { 40 - url: "https://cdn.discordapp.com/attachments/1279476809653686324/1420213543961366618/jack_o_lantern__png_by_doloresminette_d5g6dbe-375w-2x.png?ex=68d4946f&is=68d342ef&hm=9d88e5dda9a0ae2ad5f4837ab3a519ad6904c1612f623e327dc4aaf1cc392317", 41 - }, 42 - title: `₊˚🕯️♱‧₊˚. 𝖖𝖚𝖊𝖘𝖙𝖎𝖔𝖓 𝖔𝖋 𝖙𝖍𝖊 𝖉𝖆𝖞 .˚₊‧♱🕯️˚₊`, 43 - author: { 44 - name: ".˚⊹. ࣪𓉸 ࣪⊹˚. 𝔭𝔲𝔫𝔠𝔥𝔶 .˚⊹. ࣪𓉸 ࣪⊹˚.", 45 - url: "https://tangled.sh/@timtinkers.online/discord-qotd-webhook", 46 - }, 47 - footer: { text: `⛧°。 ⋆༺♱༻⋆。 °⛧` }, 48 - start: Temporal.PlainMonthDay.from({ month: 10, day: 1 }), 49 - end: Temporal.PlainMonthDay.from({ month: 10, day: 31 }), 50 - kvKey: ["halloweenIndex"], 51 - }, 52 - // Expand as needed 53 - }; 54 - 55 - export function getTheme( 56 - date = Temporal.Now.plainDateISO(), 57 - ): { themeName: string; theme: Theme } { 58 - const currentYear = date.year; 59 - 60 - // Check all seasonal themes 61 - for (const [themeName, theme] of Object.entries(themes)) { 62 - if (themeName === "default" || !theme.start || !theme.end) continue; 63 - 64 - // Temporal voodoo to check if the given date is within the interval 65 - const startDate = theme.start.toPlainDate({ year: currentYear }); 66 - const endDate = theme.end.toPlainDate({ year: currentYear }); 67 - 68 - const inRange = Temporal.PlainDate.compare(startDate, endDate) > 0 69 - ? Temporal.PlainDate.compare(date, startDate) >= 0 || 70 - Temporal.PlainDate.compare( 71 - date, 72 - theme.end.toPlainDate({ year: currentYear + 1 }), 73 - ) <= 0 74 - : Temporal.PlainDate.compare(date, startDate) >= 0 && 75 - Temporal.PlainDate.compare(date, endDate) <= 0; 76 - 77 - if (inRange) return { themeName, theme }; 78 - } 79 - 80 - // Default fallback 81 - return { themeName: "default", theme: themes.default }; 82 - }