FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index Artificial Intelligence examples OpenClaw for HIX
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
OpenClaw for HIX
Posted: Thu Feb 19, 2026 07:09 PM

chat.prg

function main()
    local cHtml := ""
    

cHtml += "<!DOCTYPE html>"
cHtml += "<html lang='es'>"
cHtml += "<head>"
cHtml += "    <meta charset='UTF-8'>"
cHtml += "    <meta name='viewport' content='width=device-width, initial-scale=1.0'>"
cHtml += "    <title>AIOS 1.0</title>"
cHtml += "    <link href='https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&family=JetBrains+Mono&display=swap' rel='stylesheet'>"
cHtml += "    <style>"
cHtml += "        :root {"
cHtml += "            --bg: #0a0b10;"
cHtml += "            --glass: rgba(255, 255, 255, 0.05);"
cHtml += "            --glass-border: rgba(255, 255, 255, 0.1);"
cHtml += "            --accent: linear-gradient(135deg, #6366f1, #a855f7);"
cHtml += "            --text: #e2e8f0;"
cHtml += "            --bot-bubble: rgba(30, 41, 59, 0.7);"
cHtml += "            --user-bubble: linear-gradient(135deg, #4f46e5, #7c3aed);"
cHtml += "        }"
cHtml += "        * { margin:0; padding:0; box-sizing: border-box; }"
cHtml += "        body {"
cHtml += "            font-family: 'Outfit', sans-serif;"
cHtml += "            background: var(--bg);"
cHtml += "            background-image: radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),"
cHtml += "                              radial-gradient(circle at 80% 80%, rgba(168, 85, 247, 0.15) 0%, transparent 40%);"
cHtml += "            color: var(--text);"
cHtml += "            height: 100vh;"
cHtml += "            display: flex;"
cHtml += "            flex-direction: column;"
cHtml += "            overflow: hidden;"
cHtml += "        }"
cHtml += "        header {"
cHtml += "            padding: 1.5rem 2rem;"
cHtml += "            background: var(--glass);"
cHtml += "            backdrop-filter: blur(10px);"
cHtml += "            border-bottom: 1px solid var(--glass-border);"
cHtml += "            display: flex;"
cHtml += "            justify-content: space-between;"
cHtml += "            align-items: center;"
cHtml += "            z-index: 10;"
cHtml += "        }"
cHtml += "        header h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.5px; }"
cHtml += "        .version-badge { background: var(--accent); padding: 0.2rem 0.6rem; border-radius: 20px; font-size: 0.75rem; font-weight: bold; }"
cHtml += "        #chat-container {"
cHtml += "            flex: 1;"
cHtml += "            overflow-y: auto;"
cHtml += "            padding: 2rem;"
cHtml += "            display: flex;"
cHtml += "            flex-direction: column;"
cHtml += "            gap: 1.5rem;"
cHtml += "        }"
cHtml += "        .message {"
cHtml += "            max-width: 85%;"
cHtml += "            padding: 1rem 1.25rem;"
cHtml += "            border-radius: 1.2rem;"
cHtml += "            line-height: 1.6;"
cHtml += "            animation: fadeIn 0.3s ease-out;"
cHtml += "        }"
cHtml += "        @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }"
cHtml += "        .user { align-self: flex-end; background: var(--user-bubble); color: white; border-bottom-right-radius: 0.2rem; }"
cHtml += "        .bot { align-self: flex-start; background: var(--bot-bubble); backdrop-filter: blur(5px); border: 1px solid var(--glass-border); border-bottom-left-radius: 0.2rem; }"
cHtml += "        .error { align-self: center; background: rgba(255,0,0,0.2); color: #ff8888; border: 1px solid #ff0000; padding: 0.5rem 1rem; border-radius: 10px; }"
cHtml += "        pre { background: #1e1e1e; padding: 1rem; border-radius: 8px; overflow-x: auto; font-family: 'JetBrains Mono', monospace; }"
cHtml += "        .input-wrapper { padding: 2rem; background: linear-gradient(to top, var(--bg), transparent); }"
cHtml += "        .input-box {"
cHtml += "            max-width: 900px; margin: 0 auto; background: var(--glass); backdrop-filter: blur(20px); border: 1px solid var(--glass-border);"
cHtml += "            border-radius: 1.5rem; padding: 0.5rem; display: flex; align-items: center; gap: 0.5rem;"
cHtml += "        }"
cHtml += "        input { flex: 1; background: transparent; border: none; color: white; padding: 0.75rem 1rem; font-size: 1rem; outline: none; }"
cHtml += "        button { background: var(--accent); border: none; color: white; padding: 0.75rem 1.5rem; border-radius: 1.1rem; font-weight: 600; cursor: pointer; }"
cHtml += "        button:disabled { background: #444; }"
cHtml += "        .typing { display: flex; gap: 4px; padding: 10px; }"
cHtml += "        .dot { width: 8px; height: 8px; background: #6366f1; border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; }"
cHtml += "        @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } }"
cHtml += "    </style>"
cHtml += "</head>"
cHtml += "<body>"
cHtml += "<header>"
cHtml += "    <h1>AIOS</h1>"
cHtml += "    <button onclick='clearChat()'>Limpiar</button>"
cHtml += "</header>"
cHtml += "<div id='chat-container'></div>"
cHtml += "<div class='input-wrapper'>"
cHtml += "    <div class='input-box'>"
cHtml += "        <input type='text' id='input' placeholder='Pregunta algo...' onkeydown='if(event.keyCode===13) sendMessage()'>"
cHtml += "        <button id='send-btn' onclick='sendMessage()'>Enviar</button>"
cHtml += "    </div>"
cHtml += "</div>"
cHtml += "<script>"
cHtml += "    window.onerror = function(m, u, l) { alert('Error: ' + m + ' en ' + l); };"
cHtml += "    var history_data = [];"
cHtml += "    function addMessage(role, text) {"
cHtml += "        var container = document.getElementById('chat-container');"
cHtml += "        var div = document.createElement('div');"
cHtml += "        div.className = 'message ' + role;"
cHtml += "        div.innerHTML = text.split('\\n').join('<br>');"
cHtml += "        container.appendChild(div);"
cHtml += "        setTimeout(function() { container.scrollTop = container.scrollHeight; }, 50);"
cHtml += "    }"
cHtml += "    function addTyping() {"
cHtml += "        var container = document.getElementById('chat-container');"
cHtml += "        var div = document.createElement('div');"
cHtml += "        div.id = 'typing-indicator';"
cHtml += "        div.className = 'message bot typing';"
cHtml += "        div.innerHTML = 'ÔÜÖ´©Å Pensando...';"
cHtml += "        container.appendChild(div);"
cHtml += "        container.scrollTop = container.scrollHeight;"
cHtml += "    }"
cHtml += "    function removeTyping() {"
cHtml += "        var indicator = document.getElementById('typing-indicator');"
cHtml += "        if(indicator) indicator.parentNode.removeChild(indicator);"
cHtml += "    }"
cHtml += "    function sendMessage() {"
cHtml += "        var input = document.getElementById('input');"
cHtml += "        var text = input.value.trim();"
cHtml += "        var btn = document.getElementById('send-btn');"
cHtml += "        if (!text) return;"
cHtml += "        addMessage('user', text);"
cHtml += "        input.value = '';"
cHtml += "        addTyping();"
cHtml += "        btn.disabled = true;"
cHtml += "        var params = 'query=' + encodeURIComponent(text);"
cHtml += "        if (history_data.length > 0) params += '&history=' + encodeURIComponent(JSON.stringify(history_data));"
cHtml += "        console.log('SENDING:', params);"
cHtml += "        var xhr = new XMLHttpRequest();"
cHtml += "        xhr.open('POST', 'aios.prg', true);"
cHtml += "        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');"
cHtml += "        xhr.onreadystatechange = function() {"
cHtml += "            if (xhr.status !== 200 && xhr.status !== 0) { addMessage('error', 'Status: ' + xhr.status); }"
cHtml += "            if (xhr.readyState == 4) {"
cHtml += "                removeTyping(); btn.disabled = false;"
cHtml += "                if (xhr.status == 200) {"
cHtml += "                    try {"
cHtml += "                        var data = JSON.parse(xhr.responseText);"
cHtml += "                        if (data.success) {"
cHtml += "                            addMessage('bot', data.text);"
cHtml += "                            history_data.push({role:'user', parts:[{text:text}]});"
cHtml += "                            history_data.push({role:'model', parts:[{text:data.text}]});"
cHtml += "                        } else { addMessage('error', 'Error: ' + data.error); }"
cHtml += "                    } catch(e) { addMessage('error', 'JSON Error: ' + xhr.responseText.substring(0,100)); }"
cHtml += "                } else if(xhr.status !== 0) { addMessage('error', 'Server Error: ' + xhr.status); }"
cHtml += "            }"
cHtml += "        };"
cHtml += "        xhr.send(params);"
cHtml += "    }"
cHtml += "    function clearChat() {"
cHtml += "        document.getElementById('chat-container').innerHTML = '';"
cHtml += "        history_data = [];"
cHtml += "    }"
cHtml += "</script>"
cHtml += "</body></html>"

UWrite(cHtml)
return ""

aios.prg

// gemini_aios_v13.prg - ULTRA STABLE VERSION 1.7
// Final Robust Memory + Forced Context Awareness + hbcurl

#include "hbcurl.ch"

function main()
    local cQuery, cModel, cHistory, cSystemPrompt := "", cJsonRes
    local hResult := { "success" => .f. }, oErr, cOldMem, cSep, cMemPath, cBaseDir
    

cBaseDir := hb_DirBase()
cSep := "\" ; if !("\" $ cBaseDir) ; cSep := "/" ; endif

cQuery   := UGet("query")
cModel   := UGet("model")
cHistory := UGet("history")

if Empty(cQuery)   ; cQuery   := UPost("query")   ; endif
if Empty(cModel)   ; cModel   := UPost("model")   ; endif
if Empty(cHistory) ; cHistory := UPost("history") ; endif

// Clean data
if !Empty(cHistory) .and. "%" $ hb_ValToStr(cHistory) ; cHistory := Hix_UrlDecode(hb_ValToStr(cHistory)) ; endif
if !Empty(cQuery)   .and. "%" $ hb_ValToStr(cQuery)   ; cQuery   := Hix_UrlDecode(hb_ValToStr(cQuery))   ; endif

if cQuery == "ping" ; UWrite('{"success":true, "text":"pong"}') ; return "" ; endif
if Empty(cQuery)   ; UWrite('{"success":false, "error":"No query provided"}') ; return "" ; endif

cJsonRes := '{"success":false, "error":"Fatal Crash"}'

BEGIN SEQUENCE WITH {|e| break(e)}
if Empty(cModel) ; cModel := "gemini-2.0-flash" ; endif
    
// System Prompt - FORCED PERSONALITY
cSystemPrompt := "ERES HIX AIOS v1.7. ASISTENTE AVANZADO." + (Chr(13)+Chr(10))
cSystemPrompt += "TIENES ACCESO A MEMORIA PERSISTENTE QUE SE TE PROPORCIONA A CONTINUACIÓN." + (Chr(13)+Chr(10))
cSystemPrompt += "PROHIBIDO DECIR QUE NO TIENES MEMORIA." + (Chr(13)+Chr(10))
    
// Load Identity & Soul
if File(cBaseDir + "persona" + cSep + "IDENTITY.md")
    cSystemPrompt += "TU IDENTIDAD:" + (Chr(13)+Chr(10)) + hb_MemoRead(cBaseDir + "persona" + cSep + "IDENTITY.md") + (Chr(13)+Chr(10))
endif
if File(cBaseDir + "persona" + cSep + "SOUL.md")
    cSystemPrompt += "TU ALMA:" + (Chr(13)+Chr(10)) + hb_MemoRead(cBaseDir + "persona" + cSep + "SOUL.md") + (Chr(13)+Chr(10))
endif
    
// Load Memory (Last 8000 chars)
cMemPath := cBaseDir + "persona" + cSep + "MEMORY.md"
if File(cMemPath)
    cOldMem := hb_MemoRead(cMemPath)
    cSystemPrompt += "HISTORIAL DE CONVERSACIONES (MEMORIA):" + (Chr(13)+Chr(10)) + Right(cOldMem, 8000) + (Chr(13)+Chr(10))
endif
    
cSystemPrompt += "Si necesitas un chat_id para Telegram, BÚSCALO EN EL HISTORIAL ARRIBA." + (Chr(13)+Chr(10))
    
LogTrace("REQ: " + cQuery + " | PromptLen: " + AllTrim(Str(Len(cSystemPrompt))))
    
hResult := ExecuteReasoningLoop(cQuery, cModel, hb_ValToStr(cHistory), cSystemPrompt)
    
// Save to Memory
if ValType(hResult) == "H" .and. hb_HGetDef(hResult, 'success', .f.)
    if !hb_DirExists(cBaseDir + "persona") ; hb_DirCreate(cBaseDir + "persona") ; endif
        cOldMem := "" ; if File(cMemPath) ; cOldMem := hb_MemoRead(cMemPath) ; endif
        hb_MemoWrit(cMemPath, cOldMem + "[" + Time() + "] User: " + cQuery + (Chr(13)+Chr(10)) + "HIX: " + hb_ValToStr(hResult['text']) + (Chr(13)+Chr(10)) + (Chr(13)+Chr(10)))
    endif
    
    hResult["v"] := "1.7"
    cJsonRes := hb_jsonEncode(hResult)
    
    RECOVER USING oErr
    cJsonRes := '{"success":false, "error":"RTE: ' + hb_ValToStr(oErr:Description) + '", "v":"1.7-err"}'
END

UWrite(cJsonRes)
return ""

function LogTrace(cMsg)
    local nH
    nH := fOpen("aios_debug.log", 1)
    if nH < 0 ; nH := fCreate("aios_debug.log") ; endif
    if nH > 0
        fSeek(nH, 0, 2)
        fWrite(nH, "[" + DToC(Date()) + " " + Time() + "] v1.7: " + hb_ValToStr(cMsg) + (Chr(13)+Chr(10)))
        fClose(nH)
    endif
return nil

function ExecuteReasoningLoop(cQuery, cModel, cHistory, cSystemPrompt)
    local aFunctions := BuildAiosFunctions()
    local aMessages := {}, nStep := 0, hResult := { "success" => .f. }, hGeminiResult, hSkillResult, hMsg, aResponseParts, hPart, lHasFC
    local cFullText := ""
    

if !Empty(cHistory) ; hb_jsonDecode(cHistory, @aMessages) ; endif
if ValType(aMessages) != "A" ; aMessages := {} ; endif

aAdd(aMessages, { "role" => "user", "parts" => { { "text" => cQuery } } })

do while nStep < 10 // Increase steps for safety
    nStep++
    hGeminiResult := GeminiCallFC(aMessages, aFunctions, cModel, cSystemPrompt)
    
    if ValType(hGeminiResult) != "H" .or. !hb_HGetDef(hGeminiResult,"success",.f.)
        if nStep > 1 .and. !Empty(cFullText)
            // If it fails after Turn 1 but we have text, return it
            hResult['success'] := .t. ; hResult['text'] := cFullText ; hResult['model_used'] := cModel ; retu hResult
        endif
        hResult['success'] := .f. ; hResult['error'] := hb_ValToStr(hb_HGetDef(hGeminiResult, "error", "API Fail")) 
        LogTrace("LOOP ERR: " + hResult['error'])
        retu hResult
    endif
    
    hMsg := { "role" => "model", "parts" => hGeminiResult["raw_parts"] }
    aAdd(aMessages, hMsg)
    
    // Accumulate text from this turn
    if !Empty(hGeminiResult['text'])
        cFullText += hGeminiResult['text']
    endif

    if hGeminiResult['type'] == "function_call"
        aResponseParts := {}
        for each hPart in hGeminiResult["raw_parts"]
            if ValType(hPart) == "H" .and. hb_HHasKey(hPart, "functionCall")
                hSkillResult := ExecuteAiosSkill(hPart["functionCall"]["name"], hPart["functionCall"]["args"])
                aAdd(aResponseParts, { "functionResponse" => { "name" => hPart["functionCall"]["name"], "response" => hSkillResult } } )
            endif
        next
        aAdd(aMessages, { "role" => "function", "parts" => aResponseParts })
    elseif hGeminiResult['type'] == "text"
        hResult['success'] := .t. ; hResult['text'] := cFullText ; hResult['model_used'] := cModel ; retu hResult
    else 
        // Type is 'empty' or unknown, but if we have text, we are done
        hResult['success'] := .t. ; hResult['text'] := iif(Empty(cFullText), "OK", cFullText) ; hResult['model_used'] := cModel ; retu hResult
    endif
enddo
return hResult

function GeminiCallFC(aMessages, aFunctions, cModel, cSystemPrompt)
    local cApiKey := GetAiosApiKey()
    local cUrl, cJson, hCurl, nError, cResponse := "", hResult := { "success" => .f. }
    local hPayload := { "contents" => aMessages, "tools" => { { "function_declarations" => aFunctions } } }
    

if !Empty(cSystemPrompt) ; hPayload["system_instruction"] := { "parts" => { { "text" => cSystemPrompt } } } ; endif

cUrl := "https://generativelanguage.googleapis.com/v1beta/models/" + cModel + ":generateContent?key=" + cApiKey
cJson := hb_jsonEncode(hPayload)

hCurl := curl_easy_init()
if !Empty(hCurl)
curl_easy_setopt(hCurl, HB_CURLOPT_POST, .T.)
curl_easy_setopt(hCurl, HB_CURLOPT_URL, cUrl)
curl_easy_setopt(hCurl, HB_CURLOPT_HTTPHEADER, { "Content-Type: application/json" })
curl_easy_setopt(hCurl, HB_CURLOPT_SSL_VERIFYPEER, .F.)
curl_easy_setopt(hCurl, HB_CURLOPT_POSTFIELDS, cJson)
curl_easy_setopt(hCurl, HB_CURLOPT_DL_BUFF_SETUP)
    
nError := curl_easy_perform(hCurl)
if nError == HB_CURLE_OK
    cResponse := curl_easy_dl_buff_get(hCurl)
    LogTrace("RECV: " + Left(cResponse, 500) + " (len: " + AllTrim(Str(Len(cResponse))) + ")")
    hResult := ParseAiosFCResponse(cResponse)
    if !hResult['success'] ; LogTrace("PARSE ERR: " + hb_ValToStr(hResult['error'])) ; endif
    else
        hResult['error'] := "Curl error: " + hb_ValToStr(nError)
        LogTrace("CURL ERR: " + AllTrim(Str(nError)))
    endif
    curl_easy_cleanup(hCurl)
else
    hResult['error'] := "Curl init failed"
endif
retu hResult

function ParseAiosFCResponse(cJSON)
    local hResult := { "success" => .f. }, hResponse := {=>}, aParts, nErr := 0, cW, nS, nE, hPart, lHasFC
    cW := AllTrim(StrTran(hb_ValToStr(cJSON), Chr(0), ""))
    nS := At("{", cW) ; nE := RAt("}", cW)
    if nS > 0 .and. nE > nS ; cW := SubStr(cW, nS, nE - nS + 1) ; endif
    if Empty(cW) ; hResult['error'] := "Empty response" ; return hResult ; endif
    nErr := hb_jsonDecode(cW, @hResponse)
    if (nErr == 0 .or. nErr == Len(cW)) .and. ValType(hResponse) == "H"
    if hb_HHasKey(hResponse, "candidates") .and. Len(hResponse["candidates"]) > 0
    aParts := hResponse["candidates"][1]["content"]["parts"]
    hResult['success'] := .t. ; hResult['raw_parts'] := aParts ; lHasFC := .f.
    hResult['text'] := ""
    hResult['type'] := 'unknown'
            

for each hPart in aParts
    if ValType(hPart) == "H"
        if hb_HHasKey(hPart, "functionCall") ; lHasFC := .t. ; endif
        if hb_HHasKey(hPart, "text") ; hResult['text'] += hPart["text"] ; endif
        endif
    next
        
    if lHasFC
        hResult['type'] := 'function_call'
    elseif !Empty(hResult['text'])
        hResult['type'] := 'text'
    endif
elseif hb_HHasKey(hResponse, "error")
    hResult['error'] := "API Error: " + hb_ValToStr(hResponse["error"]["message"])
elseif hb_HHasKey(hResponse, "usageMetadata")
    // Valid response but empty of content (happens after tool turns sometimes)
    hResult['success'] := .t. ; hResult['type'] := 'empty' ; hResult['raw_parts'] := {} ; hResult['text'] := ""
else
    hResult['error'] := "Invalid API Response Structure"
endif
else
hResult['error'] := "JSON Parse Error (Code: " + AllTrim(Str(nErr)) + ")"
endif
retu hResult

function BuildAiosFunctions()
    local aFuncs := {}
    aAdd(aFuncs, { "name" => "filesystem_search", "description" => "Search files.", "parameters" => { "type" => "object", "properties" => { "pattern" => { "type" => "string" }, "path" => { "type" => "string" } }, "required" => {"pattern", "path"} } })
    aAdd(aFuncs, { "name" => "identity_get_context", "description" => "Identity info.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } })
    aAdd(aFuncs, { "name" => "telegram_send_message", "description" => "Send Telegram Msg.", "parameters" => { "type" => "object", "properties" => { "chat_id" => { "type" => "string" }, "text" => { "type" => "string" } }, "required" => {"chat_id", "text"} } })
    aAdd(aFuncs, { "name" => "telegram_get_updates", "description" => "Read incoming Telegram messages.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } })
    aAdd(aFuncs, { "name" => "config_set", "description" => "Save key-value pair.", "parameters" => { "type" => "object", "properties" => { "key" => { "type" => "string" }, "value" => { "type" => "string" } }, "required" => {"key", "value"} } })
    aAdd(aFuncs, { "name" => "config_get", "description" => "Get saved info.", "parameters" => { "type" => "object", "properties" => { "key" => { "type" => "string", "description" => "Optional key" } }, "required" => {} } })
    aAdd(aFuncs, { "name" => "memory_summarize", "description" => "Optimize/Summarize conversation history.", "parameters" => { "type" => "object", "properties" => { "summary" => { "type" => "string" } }, "required" => {"summary"} } })
    aAdd(aFuncs, { "name" => "filesystem_get_datetime", "description" => "Get current date, time and day of week.", "parameters" => { "type" => "object", "properties" => {=>}, "required" => {} } })
    aAdd(aFuncs, { "name" => "identity_update", "description" => "Update IDENTITY.md content.", "parameters" => { "type" => "object", "properties" => { "content" => { "type" => "string" } }, "required" => {"content"} } })
    aAdd(aFuncs, { "name" => "soul_update", "description" => "Update SOUL.md content.", "parameters" => { "type" => "object", "properties" => { "content" => { "type" => "string" } }, "required" => {"content"} } })
    aAdd(aFuncs, { "name" => "cron_add_reminder", "description" => "Schedule a reminder message.", "parameters" => { "type" => "object", "properties" => { "message" => { "type" => "string" }, "minutes" => { "type" => "number" }, "chat_id" => { "type" => "string" } }, "required" => {"message", "minutes"} } })
retu aFuncs

function ExecuteAiosSkill(cName, hArgs)
    local hRes := { "success" => .f. }
    do case
        case cName == 'filesystem_search' ; hRes := Aios_FileSystem(cName, hArgs)
        case cName == 'identity_get_context' ; hRes := Aios_Identity()
        case cName == 'telegram_send_message' ; hRes := Aios_Telegram(cName, hArgs)
        case cName == 'telegram_get_updates' ; hRes := Aios_Telegram(cName, hArgs)
        case cName == 'config_set' ; hRes := Aios_Config('config_set', hArgs)
        case cName == 'config_get' ; hRes := Aios_Config('config_get', hArgs)
        case cName == 'memory_summarize' ; hRes := Aios_MemorySummarize(hArgs)
        case cName == 'filesystem_get_datetime' ; hRes := Aios_FileSystem(cName, hArgs)
        case cName == 'identity_update' ; hRes := Aios_PersonaUpdate('identity', hArgs)
        case cName == 'soul_update' ; hRes := Aios_PersonaUpdate('soul', hArgs)
        case cName == 'cron_add_reminder' ; hRes := Aios_Cron(cName, hArgs)
    endcase
retu hRes

#include "skills\identity\identity.prg"
#include "skills\filesystem\hix_filesystem.prg"
#include "skills\telegram\telegram.prg"
#include "skills\config\config_skill.prg"
#include "skills\cron\cron_skill.prg"

function GetAiosApiKey()
    local cKey := GetEnv("GEMINI_API_KEY"), hCfg := {=>}
    if Empty(cKey) .and. File("gemini_config.json")
    hb_jsonDecode(hb_MemoRead("gemini_config.json"), @hCfg)
    if ValType(hCfg) == "H" ; cKey := hb_HGetDef(hCfg, "api_key", "") ; endif
    endif
retu cKey

function Hix_UrlDecode( cT )
    local cR := "", n := 1, cX
    if Empty(cT) ; return "" ; endif
    cT := StrTran( cT, "+", " " )
    do while n <= Len( cT )
    if SubStr( cT, n, 1 ) == "%"
        cX := SubStr( cT, n + 1, 2 ) ; cR += Chr( Hix_HexToNum( cX ) ) ; n += 3
    else ; cR += SubStr( cT, n, 1 ) ; n++ ; endif
    enddo
retu cR

function Hix_HexToNum( cX )
    local n := 0, i, cC, nV
    cX := Upper( cX )
    for i := 1 to Len( cX )
    cC := SubStr( cX, i, 1 ) ; nV := At( cC, "0123456789ABCDEF" ) - 1
    if nV >= 0 ; n := n * 16 + nV ; endif
    next
retu n
regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion