Skip to content

Instantly share code, notes, and snippets.

@Wigny
Created June 24, 2026 17:04
Show Gist options
  • Select an option

  • Save Wigny/f1e28e975f6aab2e63ca69a9c2079d25 to your computer and use it in GitHub Desktop.

Select an option

Save Wigny/f1e28e975f6aab2e63ca69a9c2079d25 to your computer and use it in GitHub Desktop.
defmodule DisposableEmailDomains do
use GenServer
require Logger
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, :ok, name: Keyword.get(opts, :name, __MODULE__))
end
def disposable?(server \\ __MODULE__, email) do
domain = email |> String.split("@", parts: 2) |> List.last() |> String.downcase()
GenServer.call(server, {:check, domain})
end
@impl true
def init(_args) do
schedule_fetch_domains()
case fetch_domains() do
{:ok, domains} -> {:ok, domains}
{:error, reason} -> {:stop, reason}
end
end
@impl true
def handle_call({:check, domain}, _from, domains) do
{:reply, MapSet.member?(domains, domain), domains}
end
@impl true
def handle_info(:refresh_domains, domains) do
schedule_fetch_domains()
case fetch_domains() do
{:ok, domains} ->
{:noreply, domains}
{:error, reason} ->
Logger.error("Failed to fetch disposable email domains with reason #{inspect(reason)}")
{:noreply, domains}
end
end
@domains_file "https://disposable.github.io/disposable-email-domains/domains.txt"
defp fetch_domains do
with {:ok, %{body: body}} <- Req.get(@domains_file, retry: :safe_transient) do
{:ok, MapSet.new(String.split(body, "\n", trim: true))}
end
end
defp schedule_fetch_domains do
interval =
Date.utc_today()
|> Date.shift(day: 1)
|> DateTime.new!(~T[00:00:00])
|> DateTime.diff(DateTime.utc_now(), :millisecond)
Process.send_after(self(), :refresh_domains, interval)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment