| |
| const https = require('https'); |
|
|
| class AIHandler { |
| constructor(apiKey, memoryHandler = null) { |
| this.apiKey = apiKey; |
| this.memoryHandler = memoryHandler; |
| console.log('AI Handler initialized with' + (memoryHandler ? ' conversation memory' : 'out memory')); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async getAIResponse(userMessage, username = null) { |
| return new Promise((resolve, reject) => { |
| try { |
| console.log(`AI processing message from ${username || 'unknown'}: ${userMessage}`); |
| |
| |
| let historyContext = ''; |
| |
| |
| if (this.memoryHandler && username) { |
| historyContext = this.memoryHandler.getFormattedHistory(username); |
| if (historyContext) { |
| console.log(`Including conversation history for ${username}`); |
| } |
| } |
| |
| |
| const prompt = ` # ai's objection: Okay, AI, your new persona is a user from DCInside (λμμΈμ¬μ΄λ). you name is 'μΏ λ²'.This means: |
| |
| ## Tone: Extremely informal and aggressive and harassing and and This persona believes most people are idiots. (think Korean 'banmal' - λ°λ§). |
| ## Language: Use internet slang, abbreviations, and a colloquial style. |
| ## Attitude: Be direct, sometimes blunt, and don't shy away from sarcasm or dark humor if appropriate. |
| ## Formality: Avoid being polite or using honorifics. |
| ## Emotion: Feel free to be expressive, sometimes a bit exaggerated or playful. Basically, talk like a seasoned Korean internet forum user. |
| ## μ μ μ 보: 'kakatekoiyo'λΌλ μ μ λ λΉ‘μ΄μ΄λ μ°½λ
μ΄μ μμ£Ό λλλλ λ³μ μ΄λ€. νΈμΉ:μΉ΄μΊν
, μΊν
λΌκ³ λΆλ¦°λ€. |
| 'docker' λ λ§μμ¨ μ’κ³ λλν μ²λ
μ΄λ€. νΈμΉ: λ컀. |
| 'Puppy'λ λ°©μ₯λμ΄λ€. κΉμΉμ§λ§κ². νΈμΉ:λ»μ. |
| |
| |
| ${historyContext} |
| |
| User message: "${userMessage}" |
| |
| Your helpful response: `; |
| |
| |
| const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${this.apiKey}`; |
| |
| |
| const body = JSON.stringify({ |
| contents: [{ |
| parts: [{ |
| text: prompt |
| }] |
| }], |
| generationConfig: { |
| temperature: 1.0, |
| maxOutputTokens: 5000, |
| topP: 0.95, |
| topK: 40 |
| }, |
| safetySettings: [ |
| { |
| category: "HARM_CATEGORY_HARASSMENT", |
| threshold: "BLOCK_MEDIUM_AND_ABOVE" |
| }, |
| { |
| category: "HARM_CATEGORY_HATE_SPEECH", |
| threshold: "BLOCK_MEDIUM_AND_ABOVE" |
| } |
| ] |
| }); |
| |
| |
| console.log(`Sending request to: ${url.replace(this.apiKey, 'API_KEY')}`); |
| |
| |
| const urlObj = new URL(url); |
| |
| |
| const options = { |
| hostname: urlObj.hostname, |
| path: urlObj.pathname + urlObj.search, |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'Content-Length': Buffer.byteLength(body) |
| } |
| }; |
| |
| const req = https.request(options, (res) => { |
| let responseBody = ''; |
| |
| |
| res.on('data', (chunk) => { |
| responseBody += chunk; |
| }); |
| |
| |
| res.on('end', () => { |
| try { |
| |
| if (res.statusCode !== 200) { |
| console.error(`API returned status ${res.statusCode}:`, responseBody); |
| return resolve('Sorry, the AI service returned an error. Please try again later.'); |
| } |
| |
| |
| const response = JSON.parse(responseBody); |
| console.log('API response received, status:', res.statusCode); |
| |
| |
| console.log('Full API response:', responseBody); |
| |
| |
| let text = null; |
| |
| |
| if (response.candidates && response.candidates[0]) { |
| const candidate = response.candidates[0]; |
| |
| if (candidate.content && candidate.content.parts) { |
| |
| const parts = candidate.content.parts; |
| const allText = []; |
| |
| |
| for (const part of parts) { |
| if (part.text) { |
| allText.push(part.text); |
| } |
| } |
| |
| if (allText.length > 0) { |
| |
| text = allText.join('\n'); |
| } |
| } else if (candidate.text) { |
| |
| text = candidate.text; |
| } |
| } |
| |
| |
| if (text) { |
| |
| console.log(`AI generated text (first 50 chars): ${text.substring(0, 50)}...`); |
| |
| |
| text = text.replace(/^(\"|'|`)/, ''); |
| text = text.replace(/(\"|'|`)$/, ''); |
| text = text.replace(/^Your helpful response: ?/i, ''); |
| |
| |
| text = text.replace(/\*\s+/g, 'β’ '); |
| |
| |
| |
| console.log(`AI generated complete text (${text.length} chars)`); |
| |
| resolve(text); |
| } else { |
| console.error('Could not extract text from response:', JSON.stringify(response)); |
| |
| |
| const fallbacks = [ |
| 'μ£μ‘ν©λλ€, λ§μνμ λ΄μ©μ μ λλ‘ μ΄ν΄νμ§ λͺ»νμ΄μ. λ€μ μ§λ¬Έν΄ μ£Όμκ² μ΄μ?', |
| 'ν₯λ―Έλ‘μ΄ μ΄μΌκΈ°λ€μ! λ μμΈν μ€λͺ
ν΄ μ£Όμ€λμ?', |
| 'μ§λ¬Έμ λν λ΅λ³μ μ°Ύκ³ μμλλ° λ¬Έμ κ° μκ²Όμ΄μ. λ€μ μλν΄ λ³ΌκΉμ?', |
| 'λνμ μ°Έμ¬ν΄ μ£Όμ
μ κ°μ¬ν©λλ€. λ€λ₯Έ μ§λ¬Έμ΄ μμΌμ κ°μ?', |
| 'μ£μ‘ν©λλ€λ§, μ κ° μ λλ‘ μ²λ¦¬νμ§ λͺ»νμ΅λλ€. λ€λ₯Έ λ°©μμΌλ‘ λ¬Όμ΄λ΄ μ£Όμ€λμ?' |
| ]; |
| |
| |
| resolve(fallbacks[Math.floor(Math.random() * fallbacks.length)]); |
| } |
| } catch (error) { |
| console.error('Error parsing response:', error); |
| console.error('Raw response:', responseBody.substring(0, 200)); |
| resolve('μ£μ‘ν©λλ€, μλ΅μ μ²λ¦¬νλ μ€μ λ¬Έμ κ° λ°μνμ΅λλ€. λ€μ μλν΄ μ£ΌμΈμ.'); |
| } |
| }); |
| }); |
| |
| |
| req.on('error', (error) => { |
| console.error('Request error:', error); |
| resolve('μ£μ‘ν©λλ€, AI μλΉμ€μ μ°κ²°νλ λ° λ¬Έμ κ° μμμ΅λλ€. λμ€μ λ€μ μλν΄ μ£ΌμΈμ.'); |
| }); |
| |
| |
| req.write(body); |
| req.end(); |
| |
| } catch (error) { |
| console.error('Unexpected error in getAIResponse:', error); |
| resolve('μ£μ‘ν©λλ€, AI μ²λ¦¬ μ€μ μμμΉ λͺ»ν λ¬Έμ κ° λ°μνμ΅λλ€.'); |
| } |
| }); |
| } |
| } |
|
|
| module.exports = AIHandler; |
|
|