-module(telega@file). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/telega/file.gleam"). -export([from_string/1, from_file/1, from_file_with_name/2, from_bytes/2, read_file/1, to_json_value/1, requires_multipart/1, get_attach_name/1, to_multipart_file/1, download_by_path/2, download_file/2, download_to_file/3, get_file_info/2]). -export_type([media_input/0, multipart_file/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " File handling utilities for Telegram Bot API\n" " Provides types and functions for working with files in media uploads\n" ). -type media_input() :: {url, binary()} | {file_id, binary()} | {local_file, binary(), binary()} | {bytes, bitstring(), binary(), binary()}. -type multipart_file() :: {multipart_file, binary(), binary(), bitstring(), gleam@option:option(binary())}. -file("src/telega/file.gleam", 33). ?DOC(" Creates a MediaInput from a string that could be URL or file ID\n"). -spec from_string(binary()) -> media_input(). from_string(Value) -> case {gleam_stdlib:string_starts_with(Value, <<"http://"/utf8>>), gleam_stdlib:string_starts_with(Value, <<"https://"/utf8>>)} of {true, _} -> {url, Value}; {_, true} -> {url, Value}; {_, _} -> {file_id, Value} end. -file("src/telega/file.gleam", 44). ?DOC(" Creates a MediaInput from a local file path\n"). -spec from_file(binary()) -> media_input(). from_file(Path) -> Attach_name = <<"file_"/utf8, (gleam@string:replace(Path, <<"/"/utf8>>, <<"_"/utf8>>))/binary>>, {local_file, Path, Attach_name}. -file("src/telega/file.gleam", 50). ?DOC(" Creates a MediaInput from a local file path with custom attach name\n"). -spec from_file_with_name(binary(), binary()) -> media_input(). from_file_with_name(Path, Attach_name) -> {local_file, Path, Attach_name}. -file("src/telega/file.gleam", 55). ?DOC(" Creates a MediaInput from bytes\n"). -spec from_bytes(bitstring(), binary()) -> media_input(). from_bytes(Data, Filename) -> Attach_name = <<"bytes_"/utf8, Filename/binary>>, {bytes, Data, Filename, Attach_name}. -file("src/telega/file.gleam", 61). ?DOC(" Reads a file and creates a MediaInput with its contents\n"). -spec read_file(binary()) -> {ok, media_input()} | {error, simplifile:file_error()}. read_file(Path) -> gleam@result:'try'( simplifile_erl:read_bits(Path), fun(Data) -> Filename = case gleam@string:split(Path, <<"/"/utf8>>) of [] -> <<"file"/utf8>>; Parts -> case gleam@list:last(Parts) of {ok, Name} -> Name; {error, _} -> <<"file"/utf8>> end end, {ok, from_bytes(Data, Filename)} end ). -file("src/telega/file.gleam", 77). ?DOC( " Gets the string representation for JSON encoding\n" " For local files and bytes, returns the attach:// reference\n" ). -spec to_json_value(media_input()) -> binary(). to_json_value(Input) -> case Input of {url, Url} -> Url; {file_id, Id} -> Id; {local_file, _, Attach_name} -> <<"attach://"/utf8, Attach_name/binary>>; {bytes, _, _, Attach_name@1} -> <<"attach://"/utf8, Attach_name@1/binary>> end. -file("src/telega/file.gleam", 87). ?DOC(" Checks if this MediaInput requires multipart upload\n"). -spec requires_multipart(media_input()) -> boolean(). requires_multipart(Input) -> case Input of {local_file, _, _} -> true; {bytes, _, _, _} -> true; {url, _} -> false; {file_id, _} -> false end. -file("src/telega/file.gleam", 95). ?DOC(" Gets the attach name if this is a local file or bytes\n"). -spec get_attach_name(media_input()) -> gleam@option:option(binary()). get_attach_name(Input) -> case Input of {local_file, _, Name} -> {some, Name}; {bytes, _, _, Name} -> {some, Name}; _ -> none end. -file("src/telega/file.gleam", 158). ?DOC(" Simple MIME type detection based on file extension\n"). -spec detect_mime_type(binary()) -> gleam@option:option(binary()). detect_mime_type(Filename) -> Extension = case gleam@string:split(Filename, <<"."/utf8>>) of [] -> <<""/utf8>>; Parts -> case gleam@list:last(Parts) of {ok, Ext} -> string:lowercase(Ext); {error, _} -> <<""/utf8>> end end, case Extension of <<"jpg"/utf8>> -> {some, <<"image/jpeg"/utf8>>}; <<"jpeg"/utf8>> -> {some, <<"image/jpeg"/utf8>>}; <<"png"/utf8>> -> {some, <<"image/png"/utf8>>}; <<"gif"/utf8>> -> {some, <<"image/gif"/utf8>>}; <<"webp"/utf8>> -> {some, <<"image/webp"/utf8>>}; <<"mp4"/utf8>> -> {some, <<"video/mp4"/utf8>>}; <<"avi"/utf8>> -> {some, <<"video/x-msvideo"/utf8>>}; <<"mkv"/utf8>> -> {some, <<"video/x-matroska"/utf8>>}; <<"webm"/utf8>> -> {some, <<"video/webm"/utf8>>}; <<"mp3"/utf8>> -> {some, <<"audio/mpeg"/utf8>>}; <<"ogg"/utf8>> -> {some, <<"audio/ogg"/utf8>>}; <<"wav"/utf8>> -> {some, <<"audio/wav"/utf8>>}; <<"pdf"/utf8>> -> {some, <<"application/pdf"/utf8>>}; <<"zip"/utf8>> -> {some, <<"application/zip"/utf8>>}; <<"json"/utf8>> -> {some, <<"application/json"/utf8>>}; <<"xml"/utf8>> -> {some, <<"application/xml"/utf8>>}; _ -> none end. -file("src/telega/file.gleam", 117). ?DOC(" Converts MediaInput to MultipartFile for upload\n"). -spec to_multipart_file(media_input()) -> {ok, gleam@option:option(multipart_file())} | {error, simplifile:file_error()}. to_multipart_file(Input) -> case Input of {url, _} -> {ok, none}; {file_id, _} -> {ok, none}; {local_file, Path, Attach_name} -> gleam@result:'try'( simplifile_erl:read_bits(Path), fun(Content) -> Filename = case gleam@string:split(Path, <<"/"/utf8>>) of [] -> <<"file"/utf8>>; Parts -> case gleam@list:last(Parts) of {ok, Name} -> Name; {error, _} -> <<"file"/utf8>> end end, {ok, {some, {multipart_file, Attach_name, Filename, Content, detect_mime_type(Filename)}}} end ); {bytes, Data, Filename@1, Attach_name@1} -> {ok, {some, {multipart_file, Attach_name@1, Filename@1, Data, detect_mime_type(Filename@1)}}} end. -file("src/telega/file.gleam", 270). ?DOC(" Builds the full URL for downloading a file from Telegram\n"). -spec build_file_url(telega@client:telegram_client(), binary()) -> binary(). build_file_url(Client, File_path) -> Api_url = telega@client:get_api_url(Client), Base_url = case Api_url of <<"https://api.telegram.org/bot"/utf8>> -> <<"https://api.telegram.org/file"/utf8>>; Custom -> case gleam_stdlib:string_ends_with(Custom, <<"/bot"/utf8>>) of true -> <<(gleam@string:drop_end(Custom, 4))/binary, "/file"/utf8>>; false -> <> end end, <<<<<<<>/binary, (telega@client:get_token(Client))/binary>>/binary, "/"/utf8>>/binary, File_path/binary>>. -file("src/telega/file.gleam", 213). ?DOC( " Downloads a file using its file_path from the File object.\n" "\n" " Requires a `FetchBitsClient` to be configured on the client\n" " (via `client.set_fetch_bits_client`). Adapter packages like\n" " `telega_httpc` configure this automatically.\n" ). -spec download_by_path(telega@client:telegram_client(), binary()) -> {ok, bitstring()} | {error, binary()}. download_by_path(Client, File_path) -> Url = build_file_url(Client, File_path), gleam@result:'try'(case telega@client:get_fetch_bits_client(Client) of {some, F} -> {ok, F}; none -> {error, <<"No FetchBitsClient configured. Use client.set_fetch_bits_client or an adapter like telega_httpc that provides one."/utf8>>} end, fun(Fetch_bits) -> gleam@result:'try'( begin _pipe = gleam@http@request:to(Url), gleam@result:map_error( _pipe, fun(_) -> <<"Failed to build request for: "/utf8, Url/binary>> end ) end, fun(Req) -> Bits_req = gleam@http@request:set_body(Req, <<>>), gleam@result:'try'( begin _pipe@1 = Fetch_bits(Bits_req), gleam@result:map_error( _pipe@1, fun(E) -> <<<<<<"Failed to download file from: "/utf8, Url/binary>>/binary, " — "/utf8>>/binary, (gleam@string:inspect(E))/binary>> end ) end, fun(Resp) -> case erlang:element(2, Resp) of 200 -> {ok, erlang:element(4, Resp)}; Status -> {error, <<"Download failed with status: "/utf8, (erlang:integer_to_binary(Status))/binary>>} end end ) end ) end). -file("src/telega/file.gleam", 191). ?DOC( " Downloads a file from Telegram servers\n" " First gets the file path using getFile API, then downloads the actual file\n" ). -spec download_file(telega@client:telegram_client(), binary()) -> {ok, bitstring()} | {error, binary()}. download_file(Client, File_id) -> gleam@result:'try'( begin _pipe = telega@api:get_file(Client, File_id), gleam@result:map_error( _pipe, fun(E) -> <<"Failed to get file info: "/utf8, (gleam@string:inspect(E))/binary>> end ) end, fun(File_info) -> case erlang:element(5, File_info) of none -> {error, <<"File path not available"/utf8>>}; {some, Path} -> download_by_path(Client, Path) end end ). -file("src/telega/file.gleam", 249). ?DOC(" Downloads a file and saves it to disk\n"). -spec download_to_file(telega@client:telegram_client(), binary(), binary()) -> {ok, nil} | {error, binary()}. download_to_file(Client, File_id, Save_path) -> gleam@result:'try'( download_file(Client, File_id), fun(Content) -> _pipe = simplifile_erl:write_bits(Save_path, Content), gleam@result:map_error( _pipe, fun(E) -> <<"Failed to save file: "/utf8, (gleam@string:inspect(E))/binary>> end ) end ). -file("src/telega/file.gleam", 261). ?DOC(" Gets file information without downloading\n"). -spec get_file_info(telega@client:telegram_client(), binary()) -> {ok, telega@model@types:file()} | {error, binary()}. get_file_info(Client, File_id) -> _pipe = telega@api:get_file(Client, File_id), gleam@result:map_error( _pipe, fun(E) -> <<"Failed to get file info: "/utf8, (gleam@string:inspect(E))/binary>> end ).