Skip to content

Commit

Permalink
geo: add geo-location utility
Browse files Browse the repository at this point in the history
  • Loading branch information
fhunleth committed Jun 14, 2024
1 parent ae01fd7 commit d27a08a
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/toolshed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Toolshed do
* `dmesg/0` - print kernel messages (Nerves-only)
* `exit/0` - exit out of an IEx session
* `fw_validate/0` - marks the current image as valid (check Nerves system if supported)
* `geo/0` - print out a rough physical location
* `geo/1` - print out a rough physical location
* `grep/2` - print out lines that match a regular expression
* `hex/1` - print a number as hex
* `history/0` - print out the IEx shell history
Expand Down
94 changes: 94 additions & 0 deletions lib_src/core/geo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
defmodule Toolshed.Core.Geo do
@whenwhere_url "http://whenwhere.nerves-project.org/"

@doc """
Geo-locate this Elixir instance
Options:
* `:ifname` - Network interface to use (e.g., `"eth0"`)
* `:whenwhere_url` - URL for the whenwhere server to query. Defaults to http://whenwhere.nerves-project.org
"""
@spec geo(keyword()) :: :"do not show this result in output"
def geo(options \\ []) do
check_app(:inets)
check_app(:ssl)

do_geo(options) |> IO.puts()
IEx.dont_display_result()
end

defp do_geo(options) do
url = Keyword.get(options, :whenwhere_url, @whenwhere_url)

request_headers = [
{~c"user-agent", ~c"toolshed"},
{~c"content-type", ~c"application/x-erlang-binary"}
]

case :httpc.request(
:get,
{url, request_headers},
[ssl: [verify: :verify_none]],
socket_opts: socket_opts(options)
) do
{:ok, {_status, _headers, body}} ->
body |> :erlang.list_to_binary() |> :erlang.binary_to_term() |> format_geo_output()

{:error, reason} ->
error_message(reason)
end
rescue
e in MatchError -> error_message(e)
catch
:exit, reason -> error_message(reason)
end

defp extract_ip(ip_port_string) do
case Regex.run(~r/^(.*):\d+$/, ip_port_string) do
[_, ip_address] -> ip_address
_ -> []
end
end

defp format_geo_output(result) do
now = NaiveDateTime.from_iso8601!(result["now"])

local_now =
with {:ok, time_zone} <- Map.fetch(result, "time_zone"),
{:ok, utc} <- DateTime.from_naive(now, "Etc/UTC"),
{:ok, dt} <- DateTime.shift_zone(utc, time_zone) do
dt
else
_ -> nil
end

where =
[result["city"], result["country_region"], result["country"]]
|> Enum.filter(&Function.identity/1)
|> Enum.intersperse(", ")

[
"UTC Time : ",
NaiveDateTime.to_string(now),
"\n",
if(local_now, do: ["Local time: ", DateTime.to_string(local_now), "\n"], else: []),
["Location : ", where, "\n"],
if(result["latitude"] && result["longitude"],
do: [
"Map : https://www.openstreetmap.org/?mlat=",
result["latitude"],
"&mlon=",
result["longitude"],
"&zoom=12#map=12/",
result["latitude"],
"/",
result["longitude"],
"\n"
],
else: []
),
if(result["address"], do: ["Public IP : ", extract_ip(result["address"])], else: [])
]
end
end
15 changes: 15 additions & 0 deletions test/toolshed/geo_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Toolshed.GeoTest do
use ExUnit.Case
import ExUnit.CaptureIO

test "geo/1 returns at least the time" do
# Everything else is optional unfortunately
assert capture_io(&Toolshed.geo/0) =~ "UTC Time : "
end

test "geo/1 supports overriding the server" do
assert capture_io(fn ->
Toolshed.geo(whenwhere_url: "http://not_a_server.nerves-project.org")
end) =~ "Something went wrong when making an HTTP request"
end
end
2 changes: 2 additions & 0 deletions test/toolshed_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule ToolshedTest do
cat: 1,
cmd: 1,
date: 0,
geo: 0,
geo: 1,
grep: 2,
hex: 1,
history: 0,
Expand Down

0 comments on commit d27a08a

Please sign in to comment.