~fnux/telegram-tl-elixir

c93d70c1c3828af77cfbcc6060d287a2ebdd3ae6 — Timothée Floure 4 years ago 18f62f4
Allow to serialize with "flags"
3 files changed, 93 insertions(+), 5 deletions(-)

M lib/tl/binary.ex
M lib/tl/build.ex
M test/tl_test.exs
M lib/tl/binary.ex => lib/tl/binary.ex +17 -0
@@ 29,4 29,21 @@ defmodule TL.Binary do
    right = :binary.part binary, index, byte_size(binary) - index
    {left, right}
  end

  def build_integer_from_bits_list(list, value \\ 0)
  @doc false
  def build_integer_from_bits_list([], value), do: value
  @doc """
  Build an integer from a list of bit positions.

  Example:
  ```
  iex> build_integer_from_bits_list([0,1,3,10])
  1035
  ```
  """
  def build_integer_from_bits_list([bit_index|tail], value) do
    bit_value = :math.pow(2, bit_index) |> round()
    build_integer_from_bits_list(tail, value + bit_value)
  end
end

M lib/tl/build.ex => lib/tl/build.ex +38 -4
@@ 1,6 1,7 @@
defmodule TL.Build do
  alias TL.Schema
  require Bitwise
  import TL.Binary
  alias TL.Schema

  @moduledoc false



@@ 17,6 18,9 @@ defmodule TL.Build do
      }
    end

    # Handle flags
    map = process_flags(map)

    # Serialized values
    serialized_values = map |> Enum.map(fn {type, value} -> serialize(value, type) end)



@@ 30,8 34,40 @@ defmodule TL.Build do
    serialized_method <> :binary.list_to_bin serialized_values
  end

  # Handle flags
  def process_flags(input_params, flags \\ 0, processed_params \\ [])
  def process_flags([], _flags, processed_params), do: processed_params
  def process_flags([param|params_tail], flags, processed_params) do
    {type, value} = param
    cond do
      {type, value} == {:"#", nil} ->
        flags_value = 0
        process_flags(params_tail, flags_value, processed_params ++ [int: flags_value])
      type == :"#" && is_list(value) ->
        flags_value = TL.Binary.build_integer_from_bits_list(value)
        process_flags(params_tail, flags_value, processed_params ++ [int: flags_value])
      Regex.match?(~r/^flags.\d*\?.*$/ui , Atom.to_string(type)) ->
        [_, index, wrapped_type] = Regex.run(
          ~r/^flags.(\d*)\?(.*)$/ui, Atom.to_string(type)
        )

        index = String.to_integer(index)
        pow_index = :math.pow(2, index) |> round

        if (Bitwise.band(flags, pow_index) == pow_index) do
          returned_params = processed_params ++ [{String.to_atom(wrapped_type), value}]
          process_flags(params_tail, flags, returned_params)
        else
          process_flags(params_tail, flags, processed_params)
        end

      true -> process_flags(params_tail, flags, processed_params ++ [param])
    end
  end

  # Serialize a value given its type
  def serialize(data, type) do
    #IO.inspect {type, data}
    case type do
      :int -> <<data::signed-little-size(4)-unit(8)>>
      :int64 -> <<data::signed-big-size(8)-unit(8)>>


@@ 40,16 76,14 @@ defmodule TL.Build do
      :long -> <<data::signed-little-size(8)-unit(8)>>
      :double -> <<data::signed-little-size(2)-unit(32)>>
      :string -> serialize_string(data)
      :true -> <<>> # flags
      :bytes ->
        bin =
          if (is_binary data), do: data, else: encode_signed(data)
        serialize_string(bin)
      :"#" ->
        serialize(0,:int) # ¯\(ツ)/¯ (issue 3)
      _ ->
        cond do
          Atom.to_string(type) =~ ~r/^vector/ui -> box(:vector, data, type)
          Atom.to_string(type) =~ ~r/^flags.\d\?[a-zA-Z]*$/ui -> <<>> # ¯\(ツ)/¯ (issue 3)
          true -> data
        end
    end

M test/tl_test.exs => test/tl_test.exs +38 -1
@@ 102,9 102,46 @@ defmodule TLTest do
    expected = %{name: "contactStatus", status: %{name: "userStatusOffline",
was_online: 1489598653}, user_id: 13022687}

    {map, tail} = TL.parse(container, content)
    {map, _tail} = TL.parse(container, content)
    output = map |> Map.get(:value) |> List.first

    assert output == expected
  end

  test "Parse: auth.sentCode (basic flags)" do
    {container, content} = {-212046591,  <<0, 0, 0, 0, 47, 63, 136, 89, 2, 37,
    0, 94, 3, 0, 0, 0, 134, 89, 187, 61, 5, 0, 0, 0, 18, 51, 48, 99, 102, 101,
    56, 50, 53, 99, 98, 98, 102, 49, 99, 51, 101, 57, 50, 0, 140, 21, 163, 114>>}

    {map, tail} = TL.parse container, content
    {expected_map, expected_tail} = {%{name: "rpc_result", req_msg_id: 6451475937304248320,
      result: %{name: "auth.sentCode", next_type: %{name: "auth.codeTypeSms"},
        phone_code_hash: "30cfe825cbbf1c3e92", phone_registered: true,
        type: %{length: 5, name: "auth.sentCodeTypeApp"}}}, ""}

    assert {map, tail} == {expected_map, expected_tail}
  end

  test "Build: auth.sendCode (basic flags)" do
    # Flags OFF
    serialized = TL.build("auth.sendCode", %{phone_number: "0041767780936",
      sms_type: 0, api_id: 1234, api_hash: "hashashash", lang_code: "en"})

    expected = <<236, 240, 174, 134, 0, 0, 0, 0, 13, 48, 48, 52, 49, 55, 54,
    55, 55, 56, 48, 57, 51, 54, 0, 0, 210, 4, 0, 0, 10, 104, 97, 115, 104, 97,
    115, 104, 97, 115, 104, 0>>

    assert serialized == expected

    # Flags ON
    serialized =  TL.build("auth.sendCode", %{flags: [0],
      phone_number: "0041767780936", sms_type: 0, api_id: 1234,
      api_hash: "hashashash", lang_code: "en", current_number: "BoolFalse"})

    expected = <<236, 240, 174, 134, 1, 0, 0, 0, 13, 48, 48, 52, 49, 55, 54,
    55, 55, 56, 48, 57, 51, 54, 0, 0, 66, 111, 111, 108, 70, 97, 108, 115, 101,
    210, 4, 0, 0, 10, 104, 97, 115, 104, 97, 115, 104, 97, 115, 104, 0>>

    assert serialized == expected
  end
end