diff --git a/daslib/curl.das b/daslib/curl.das deleted file mode 100644 index 0dd0fcbdd..000000000 --- a/daslib/curl.das +++ /dev/null @@ -1,208 +0,0 @@ -options no_unused_block_arguments = false -options no_unused_function_arguments = false -options indenting = 4 - -module curl shared private - -require strings -require fio - -let public HTTP_CURL_FAILED = -100500 - -let private LOG_REQUEST = false -let private LOG_RESPONSE = false -var private ALWAYS_USE_TEMP_FILE = false -var private ALWAYS_REMOVE_TEMP_FILE = true - -struct public DownloadResponse - status_code : int //! HTTP status code - error : string //! misc error message - bytes : array //! data - -def public set_use_temp_file ( use_temp_file:bool ) - let atf = ALWAYS_USE_TEMP_FILE - ALWAYS_USE_TEMP_FILE = use_temp_file - return atf - -def public DOWNLOAD ( uri:string; timeout:float = 0.0 ) - var cmd = build_string <| $ ( writer ) - writer |> write("curl -s -i {uri}") - if timeout != 0.0 - writer |> write(" --connect-timeout ") |> format("%.1f", timeout) |> write(" --max-time ") |> format("%.1f", timeout) - static_if LOG_REQUEST - print("request {cmd}\n") - var resp : DownloadResponse - let exit_code = unsafe(popen_binary(cmd)) <| $ ( f ) - if f!=null - var content_length = 0ul - while !feof(f) - let line = fgets(f) - static_if LOG_RESPONSE - print("{line}") - if line |> starts_with("HTTP/1.1 100 Continue") - fgets(f) - elif line |> starts_with("HTTP/2 100 Continue") - fgets(f) - elif line |> starts_with("HTTP/1.1 ") - resp.status_code = line |> slice(9) |> to_int - elif line |> starts_with("HTTP/2 ") - resp.status_code = line |> slice(7) |> to_int - elif line |> starts_with("Content-Length:") - content_length = uint64(line |> slice(16)) - elif length(line)==1 || length(line)==2 - break - if content_length>=uint64(INT_MAX) - resp.status_code = HTTP_CURL_FAILED - resp.error = "content length is too big" - return - if content_length != 0ul - resp.bytes |> reserve ( int(content_length) ) - var rbytes = 0 - var buf : uint8[1024] - while !feof(f) - let r = f |> fread(buf) - if r==0 - break - resp.bytes |> resize ( rbytes + r ) - unsafe - memcpy(addr(resp.bytes[rbytes]), addr(buf[0]), r) - rbytes += r - else - resp.status_code = HTTP_CURL_FAILED - resp.error = "curl failed to start" - static_if LOG_RESPONSE - print("exit code: {exit_code}\n") - if exit_code != 0 - resp.status_code = HTTP_CURL_FAILED - if exit_code==28 - resp.error = "curl timeout (max {timeout} seconds)" - elif exit_code==26 - resp.error = "file not found" - else - resp.error = "curl failed with exit code {exit_code}" - return <- resp - -struct public HttpResponse - //! HTTP request response - status_code : int //! HTTP status code - text : string //! HTTP response body - error : string //! misc error message - -def public HTTP_REQ ( method:string; uri:string; timeout:float; header:string; var data:string; blk:block<(resp:HttpResponse?):void> ) - //! HTTP request - let old_data = data - if ! data |> empty - data = data |> replace("\n", "") // todo: we need multi-replace? - data = data |> replace("\t", "") - data = data |> sanitize_command_line - data = data |> escape - var temp_file = "" - var cmd = build_string <| $ ( writer ) - writer |> write("curl -s -i -X {method} {uri}") - if timeout != 0.0 - writer |> write(" --connect-timeout ") |> format("%.1f", timeout) |> write(" --max-time ") |> format("%.1f", timeout) - if !header|> empty - writer |> write(" {header}") - if !data |> empty - if ALWAYS_USE_TEMP_FILE || length(data)>=1024 - let temp_dir = "{get_das_root()}/temp" - mkdir(temp_dir) - let ttemp_file = "{temp_dir}/{hash(data)}.curl" - var ok = false - fopen(ttemp_file, "w") <| $ ( f ) - if f!=null - f |> fprint(old_data) - f |> fprint("\n") - ok = true - if ok - temp_file = ttemp_file - writer |> write(" -d @{temp_file}") - else - writer |> write(" -d \"{data}\"") - else - writer |> write(" -d \"{data}\"") - static_if LOG_REQUEST - print("request {cmd}\n") - var resp : HttpResponse - var inscope lines : array - let exit_code = unsafe(popen(cmd)) <| $ ( f ) - if f!=null - while !f |> feof - lines |> push <| fgets(f) - else - resp.status_code = HTTP_CURL_FAILED - resp.error = "curl failed to start" - if ALWAYS_REMOVE_TEMP_FILE && !empty(temp_file) - if !remove(temp_file) - to_log(LOG_ERROR,"curl failed to remove {temp_file}\n") - static_if LOG_RESPONSE - for l in lines - print("{l}\n") - print("exit code: {exit_code}\n") - if exit_code != 0 - resp.status_code = HTTP_CURL_FAILED - if exit_code==28 - resp.error = "curl timeout (max {timeout} seconds)" - elif exit_code==26 - resp.error = "file not found" - else - resp.error = "curl failed with exit code {exit_code}" - if resp.status_code!=HTTP_CURL_FAILED - if !lines |> empty - var first_line = 0 - let linesl = lines |> length - for i in first_line..linesl-1 - if lines[first_line] |> starts_with("HTTP/1.1 100 Continue") - first_line += 2 - elif lines[first_line] |> starts_with("HTTP/2 100 Continue") - first_line += 2 - else - break - if lines[first_line] |> starts_with("HTTP/1.1 ") - resp.status_code = lines[first_line] |> slice(9) |> to_int - first_line ++ - elif lines[first_line] |> starts_with("HTTP/2 ") - resp.status_code = lines[first_line] |> slice(7) |> to_int - first_line ++ - for i in first_line..linesl - let llength = lines[i] |> length - if llength==1 || llength==2 - first_line = i + 1 - break - resp.text = build_string <| $ ( writer ) - for i in first_line..lines |> length - writer |> write(lines[i]) - blk |> invoke(unsafe(addr(resp))) - -def private header_str ( header:array> ) : string - return build_string <| $ ( writer ) - for kv in header - writer |> write("-H \"") |> write(kv._0) |> write(": ") |> write(kv._1) |> write("\" ") - -def private fragment_str ( fragment:array> ) : string - return build_string <| $ ( writer ) - for kv in fragment - writer |> write("-F ") |> write(kv._0) |> write("=") |> write(kv._1) |> write(" ") - -def public GET ( uri:string; timeout:float; blk:block<(resp:HttpResponse?):void> ) - //! HTTP GET request - HTTP_REQ("GET", uri, timeout, "", "", blk) - -def public GET ( uri:string; timeout:float; header:array>; blk:block<(resp:HttpResponse?):void> ) - //! HTTP GET request with header - HTTP_REQ("GET", uri, timeout, header_str(header), "", blk) - -def public POST ( uri:string; timeout:float; data:string; blk:block<(resp:HttpResponse?):void> ) - //! HTTP POST request - HTTP_REQ("POST", uri, timeout, "", data, blk) - -def public POST ( uri:string; timeout:float; data:string; header:array>; blk:block<(resp:HttpResponse?):void> ) - //! HTTP POST request with header - HTTP_REQ("POST", uri, timeout, header_str(header), data, blk) - -def public POST ( uri:string; timeout:float; data:string; header:array>; fragment:array>; blk:block<(resp:HttpResponse?):void> ) - //! HTTP POST request with header - HTTP_REQ("POST", uri, timeout, header_str(header)+fragment_str(fragment), data, blk) - - - diff --git a/modules/dasHV b/modules/dasHV index 9f54a4b5c..8982cd499 160000 --- a/modules/dasHV +++ b/modules/dasHV @@ -1 +1 @@ -Subproject commit 9f54a4b5ce6ff2efcabc1f05fefc941147904788 +Subproject commit 8982cd499398c3bc3e21d3a8c89889c79e267dae diff --git a/modules/dasTelegram/godfather/godfather.das b/modules/dasTelegram/godfather/godfather.das index e5f71e6cc..e03a87984 100644 --- a/modules/dasTelegram/godfather/godfather.das +++ b/modules/dasTelegram/godfather/godfather.das @@ -1,5 +1,3 @@ -require daslib/curl - require fio require daslib/strings_boost require math @@ -337,7 +335,6 @@ def main panic("bot config file name is not set. use -bot-config command line argument") if !openai_key_is_set() panic("openai key is not set") - set_use_temp_file(true) // sometimes curl removes \n from the command line input. don't know why. for now this is a workaround g_botConfig <- read_bot_config(configPath) g_adminId = int64(g_botConfig.admin) telegram_set_configuration(g_botConfig) diff --git a/modules/dasTelegram/telegram/tbot.das b/modules/dasTelegram/telegram/tbot.das index 2baf8ae56..5362d0a11 100644 --- a/modules/dasTelegram/telegram/tbot.das +++ b/modules/dasTelegram/telegram/tbot.das @@ -6,24 +6,18 @@ module tbot shared private require tbotapi public -require daslib/curl +require dashv/dashv_boost require daslib/json_boost require fio require strings struct public configuration token : string - api_timeout : float = 10.0 - send_timeout : float = 60.0 def public telegram_set_configuration ( conf:configuration ) BOT_TOKEN = conf.token - BOT_TIMEOUT = conf.api_timeout - BOT_SEND_TIMEOUT = conf.send_timeout var private BOT_TOKEN : string -var private BOT_TIMEOUT = 10.0 -var private BOT_SEND_TIMEOUT = 60.0 var private LAST_ERROR = "" var private LOG_API_CALLS = false @@ -48,23 +42,24 @@ def private telegram_call ( METHOD:string; updates:auto; var res:auto(RES_TYPE) LAST_ERROR = "" if LOG_API_CALLS to_log(LOG_INFO, "API CALL: {url}\n{headers}\n") - POST(url,BOT_TIMEOUT,headers,[{auto "Content-Type" => "application/json"}]) <| $ (resp) - if resp.status_code == 200 - var JV = read_json(resp.text,LAST_ERROR) - if JV != null - if (JV as _object)["ok"] as _bool - static_if typeinfo(can_copy type) - res = from_JV((JV as _object)["result"],type) + POST(url,headers,{{"Content-Type" => "application/json"}}) <| $ (resp) + if resp.status_code == http_status OK + peek(resp.body) <| $ ( text ) + var JV = read_json(text,LAST_ERROR) + if JV != null + if (JV as _object)["ok"] as _bool + static_if typeinfo(can_copy type) + res = from_JV((JV as _object)["result"],type) + else + res <- from_JV((JV as _object)["result"],type) else - res <- from_JV((JV as _object)["result"],type) + LAST_ERROR = "API returned not OK\n{resp.body}" else - LAST_ERROR = "API returned not OK\n{resp.text}" - else - LAST_ERROR = "FAILED\n{resp.status_code}\n{resp.error}\n{resp.text}\n" - unsafe - delete JV + LAST_ERROR = "FAILED\n{resp.status_code}\n{resp.body}\n" + unsafe + delete JV else - LAST_ERROR = "FAILED\n{resp.status_code}\n{resp.error}\n{resp.text}\n" + LAST_ERROR = "FAILED\n{resp.status_code}\n{resp.body}\n" def public telegram_getupdates ( updates:getupdates ) //! get updates from telegram @@ -107,14 +102,13 @@ def public telegram_download ( file:tbotapi::file ) //! download file from telegram LAST_ERROR = "" let url = "https://api.telegram.org/file/bot{BOT_TOKEN}/{file.file_path}" - var resp <- DOWNLOAD(url) - if resp.status_code == HTTP_CURL_FAILED - LAST_ERROR = resp.error - return <- resp.bytes - elif resp.status_code != 200 - LAST_ERROR = "DOWNLOAD failed with status {resp.status_code}" - return <- resp.bytes - return <- resp.bytes + var bytes : array + GET(url) <| $ ( resp ) + if resp.status_code != http_status OK + LAST_ERROR = "GET failed with status {resp.status_code}" + return + bytes <- get_body_bytes(resp) + return <- bytes def public telegram_getMe //! get information about bot @@ -133,20 +127,15 @@ struct public sendphoto caption : string def public telegram_sendPhoto ( photo : tbot::sendphoto ) - //! create a translation of an audio file to english - var settings <-[{auto "photo" => "@{photo.file}"; - "chat_id" => "\"{photo.chat_id}\""}] + //! send photo to telegram char or user + var settings <-{{"photo" => "@{photo.file}"; "chat_id" => "\"{photo.chat_id}\""}} if photo.reply_to_message_id != 0l - settings |> push([[auto "reply_to_message_id" => "\"{photo.reply_to_message_id}\""]]) + settings["reply_to_message_id"] ="\"{photo.reply_to_message_id}\"" if !empty(photo.caption) - settings |> push([[auto "caption" => "\"{photo.caption}\""]]) + settings["caption"] = "\"{photo.caption}\"" LAST_ERROR = "" - POST("https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto", BOT_SEND_TIMEOUT, "", - [{auto "Content-Type" => "multipart/form-data"}], settings - ) <| $ ( resp ) - if resp.status_code == HTTP_CURL_FAILED - LAST_ERROR = resp.error - elif resp.status_code != 200 + POST("https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto", "", {{"Content-Type" => "multipart/form-data"}}, settings ) <| $ ( resp ) + if resp.status_code != http_status OK LAST_ERROR = "HTTPS POST failed with status {resp.status_code}" def public telegram_setMyCommands ( commands : setmycommands )