Aethel Bot OSS repository! aethel.xyz
bot fun ai discord discord-bot aethel
at main 6.1 kB view raw
1export type V2ComponentType = 3 | 4 | 10 | 12 | 18; 2 3export interface V2BaseComponent { 4 type: V2ComponentType; 5} 6 7export interface V2StringSelect extends V2BaseComponent { 8 type: 3; 9 custom_id: string; 10 placeholder?: string; 11 options: Array<{ 12 label: string; 13 value: string; 14 description?: string; 15 emoji?: { id?: string; name?: string; animated?: boolean }; 16 }>; 17 min_values?: number; 18 max_values?: number; 19} 20 21export interface V2TextInput extends V2BaseComponent { 22 type: 4; 23 custom_id: string; 24 style: 1 | 2; 25 label?: string; 26 placeholder?: string; 27 required?: boolean; 28 min_length?: number; 29 max_length?: number; 30 value?: string; 31} 32 33export interface V2LabeledComponent extends V2BaseComponent { 34 type: 18; 35 label?: string; 36 description?: string; 37 component: V2StringSelect | V2TextInput; 38} 39 40export type V2ModalRow = 41 | V2LabeledComponent 42 | { type: 1; components: V2TextInput[] } 43 | { type: 18; components: V2TextInput[] }; 44 45export interface V2SubmissionValueMap { 46 [customId: string]: string | string[]; 47} 48 49export interface V2ModalPayload { 50 custom_id: string; 51 title: string; 52 components: V2ModalRow[]; 53} 54 55interface V2Component { 56 type: number; 57 custom_id?: string; 58 customId?: string; 59 value?: string | string[]; 60 values?: string[]; 61 [key: string]: unknown; 62} 63 64interface V2Row { 65 component?: V2Component; 66 components?: V2Component[]; 67 type?: number; 68 [key: string]: unknown; 69} 70 71export function buildProviderModal(customId: string, title: string): V2ModalPayload { 72 return { 73 custom_id: customId, 74 title, 75 components: [ 76 { 77 type: 18, 78 label: 'AI Provider', 79 description: 'Select an authorized provider', 80 component: { 81 type: 3, 82 custom_id: 'provider', 83 placeholder: 'Choose provider', 84 options: [ 85 { label: 'OpenAI', value: 'openai', description: 'api.openai.com' }, 86 { label: 'Anthropic', value: 'anthropic', description: 'api.anthropic.com' }, 87 { label: 'OpenRouter', value: 'openrouter', description: 'openrouter.ai' }, 88 { 89 label: 'Google Gemini', 90 value: 'gemini', 91 description: 'generativelanguage.googleapis.com', 92 }, 93 { label: 'DeepSeek', value: 'deepseek', description: 'api.deepseek.com' }, 94 { label: 'Moonshot AI', value: 'moonshot', description: 'api.moonshot.ai' }, 95 { label: 'Perplexity AI', value: 'perplexity', description: 'api.perplexity.ai' }, 96 ], 97 }, 98 }, 99 { 100 type: 1 as const, 101 components: [ 102 { 103 type: 4, 104 custom_id: 'model', 105 label: 'Model', 106 style: 1, 107 required: true, 108 placeholder: 'openai/gpt-4o-mini', 109 min_length: 2, 110 max_length: 100, 111 }, 112 ], 113 }, 114 { 115 type: 1 as const, 116 components: [ 117 { 118 type: 4, 119 custom_id: 'apiKey', 120 label: 'API Key', 121 style: 1, 122 required: true, 123 placeholder: 'sk-... or other', 124 min_length: 10, 125 max_length: 500, 126 }, 127 ], 128 }, 129 ], 130 }; 131} 132 133interface RawModalSubmission { 134 fields?: { 135 getTextInputValue?: (id: string) => string; 136 [key: string]: unknown; 137 }; 138 data?: { 139 components?: Array<{ 140 component?: V2Component; 141 components?: V2Component[]; 142 [key: string]: unknown; 143 }>; 144 [key: string]: unknown; 145 }; 146 components?: Array<{ 147 component?: V2Component; 148 components?: V2Component[]; 149 [key: string]: unknown; 150 }>; 151 message?: { 152 components?: Array<{ 153 component?: V2Component; 154 components?: V2Component[]; 155 [key: string]: unknown; 156 }>; 157 [key: string]: unknown; 158 }; 159 [key: string]: unknown; 160} 161 162export function parseV2ModalSubmission(raw: RawModalSubmission): V2SubmissionValueMap { 163 const result: V2SubmissionValueMap = {}; 164 try { 165 const fields = raw?.fields as { getTextInputValue?: (id: string) => string } | undefined; 166 if (fields?.getTextInputValue) { 167 for (const id of ['model', 'apiKey']) { 168 try { 169 const v = fields.getTextInputValue(id); 170 if (v !== undefined && v !== '') result[id] = v; 171 } catch (error) { 172 console.error(`Error getting text input value for ${id}:`, error); 173 } 174 } 175 } 176 } catch (error) { 177 console.error('Error processing text input fields:', error); 178 } 179 180 try { 181 const mergedRows = ( 182 [ 183 ...(raw?.data?.components || []), 184 ...(raw?.components || []), 185 ...(raw?.message?.components || []), 186 ] as Array<V2Row | undefined> 187 ).filter(Boolean) as V2Row[]; 188 189 const flat = mergedRows.flatMap((r) => (r.component ? [r.component] : r.components || [])); 190 191 for (const c of flat) { 192 if (!c) continue; 193 194 if (c.customId === 'provider' || c.custom_id === 'provider') { 195 if (Array.isArray((c as { values?: string[] }).values)) { 196 result.provider = (c as { values: string[] }).values; 197 } else if ((c as { value?: string | string[] }).value) { 198 const value = (c as { value: string | string[] }).value; 199 result.provider = Array.isArray(value) ? value : [value]; 200 } 201 } 202 203 if (c.type === 4 && (c.custom_id || c.customId) && (c as { value?: unknown }).value) { 204 const value = (c as { value: unknown }).value; 205 if (typeof value === 'string') { 206 result[c.custom_id || c.customId!] = value; 207 } 208 } 209 } 210 } catch (error) { 211 console.error('Error processing component values:', error); 212 } 213 return result; 214} 215 216export const PROVIDER_TO_URL: Record<string, string> = { 217 openai: 'https://api.openai.com/v1', 218 anthropic: 'https://api.anthropic.com/v1', 219 openrouter: 'https://openrouter.ai/api/v1', 220 gemini: 'https://generativelanguage.googleapis.com', 221 deepseek: 'https://api.deepseek.com', 222 moonshot: 'https://api.moonshot.ai', 223 perplexity: 'https://api.perplexity.ai', 224};