diff --git a/lib/couch.ex b/lib/couch.ex
index 71f86a3..9cafd39 100644
--- a/lib/couch.ex
+++ b/lib/couch.ex
@@ -1,96 +1,96 @@
 defmodule Couch do
   @moduledoc """
   Simple, no-frills, CouchDB client
   """
 
   @type base_error :: {:error, :bad_request} | {:error, :unauthorized} |
                       {:error, :server_error} | {:error, :service_unavailable} |
                       {:error, :bad_response} | {:error, HTTPoison.Error.t()}
 
   def new(db, params \\ []) do
     {url, headers, options} = prepare_request([db], [], params)
     case HTTPoison.put(url, headers, options) do
       {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
         :ok
       {:ok, %HTTPoison.Response{status_code: 412}} ->
         {:error, :exists}
       error ->
         handle_generic_response(error)
     end
   end
 
   @doc """
   Retrieve a document `doc` from `db`.
 
   `params` are [documented here](https://docs.couchdb.org/en/3.2.2-docs/api/document/common.html)
   """
   @spec get(String.t(), String.t() | :all_docs, Keyword.t()) :: {:ok, Map.t()} | {:error, :not_found} | {:error, any()}
   def get(db, doc, params \\ [])
   def get(db, :all_docs, params), do: get(db, "_all_docs", params)
   def get(db, doc, params) do
     {url, headers, options} = prepare_request([db, doc], [], params)
     case HTTPoison.get(url, headers, options) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         {:ok, Poison.decode!(body)}
       error -> handle_generic_response(error)
     end
   end
 
   @spec post(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :exists} | {:error, any()}
   def post(db, data, params \\ []) do
     {url, headers, options} = prepare_request([db], [{"content-type", "application/json"}], params)
     with \
       {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] <- HTTPoison.post(url, Poison.encode!(data), headers, options),
       {:json, {:ok, %{"ok" => true, "id" => id, "rev" => rev}}} <- {:json, Poison.decode(body)} do
         {:ok, id, rev}
     else
       {:ok, %HTTPoison.Response{status_code: 409}} -> {:error, :exists}
       {:json, {:ok, body}} ->
         Logger.error("couch: operation failed: #{inspect body}")
         {:error, :operation_failed}
        error -> handle_generic_response(error)
     end
   end
 
   @spec put(String.t(), Map.t(), Keyword.t()) :: {:error, :operation_failed} | {:error, :not_found} | {:error, :conflict} | {:error, any()}
   def put(db, doc = %{"_id" => id, "_rev" => _}, params \\ []) do
     {url, headers, options} = prepare_request([db, id], [{"content-type", "application/json"}], params)
     case HTTPoison.put(url, Poison.encode!(doc), headers, options) do
       {:ok, %HTTPoison.Response{status_code: ok, body: body}} when ok in [201, 202] ->
         body = Poison.decode!(body)
         if Map.get(body, "ok") do
           {:ok, Map.get(body, "id"), Map.get(body, "rev")}
         else
           {:error, :operation_failed}
         end
       {:ok, %HTTPoison.Response{status_code: 209}} ->
         {:error, :conflict}
       error -> handle_generic_response(error)
     end
   end
 
   defp prepare_request(path, headers \\ [], params \\ [], options \\ []) do
-    config = Application.get_env(:lsg, :couch)
+    config = Application.get_env(:nola, :couch)
 
     base_url = Keyword.get(config, :url, "http://localhost:5984")
     url = URI.merge(base_url, Path.join(path)) |> to_string()
 
     headers = headers ++ [{"accept", "application/json"}, {"user-agent", "beautte"}]
 
     params = Enum.map(params, fn({k, v}) -> {to_string(k), v} end)
     client_options = Keyword.get(config, :client_options, [])
     options = [params: params] ++ options ++ client_options
 
     user = Keyword.get(config, :user)
     pass = Keyword.get(config, :pass)
     hackney_options = Keyword.get(options, :hackney, [])
     hackney_options = if user, do: [{:basic_auth, {user, pass}} | hackney_options], else: []
     options = [{:hackney, [:insecure | hackney_options]} | options]
 
     {url, headers, options}
   end
 
   defp handle_generic_response(%HTTPoison.Response{status_code: code}), do: {:error, Plug.Conn.Status.reason_atom(code)}
   defp handle_generic_response(%HTTPoison.Error{reason: reason}), do: {:error, reason}
 
 end
diff --git a/lib/lsg/application.ex b/lib/lsg/application.ex
index 1782053..4f3d1da 100644
--- a/lib/lsg/application.ex
+++ b/lib/lsg/application.ex
@@ -1,56 +1,56 @@
 defmodule Nola.Application do
   use Application
 
   # See https://hexdocs.pm/elixir/Application.html
   # for more information on OTP Applications
   def start(_type, _args) do
     import Supervisor.Spec
 
     Logger.add_backend(Sentry.LoggerBackend)
     :ok = Nola.Matrix.setup()
     :ok = Nola.TelegramRoom.setup()
 
     # Define workers and child supervisors to be supervised
     children = [
       # Start the endpoint when the application starts
       supervisor(NolaWeb.Endpoint, []),
       # Start your own worker by calling: Nola.Worker.start_link(arg1, arg2, arg3)
       # worker(Nola.Worker, [arg1, arg2, arg3]),
       worker(Registry, [[keys: :duplicate, name: Nola.BroadcastRegistry]], id: :registry_broadcast),
       worker(Nola.IcecastAgent, []),
       worker(Nola.Token, []),
       worker(Nola.AuthToken, []),
       Nola.Subnet,
       {GenMagic.Pool, [name: Nola.GenMagic, pool_size: 2]},
       #worker(Nola.Icecast, []),
     ] ++ Nola.IRC.application_childs
       ++ Nola.Matrix.application_childs
 
     # See https://hexdocs.pm/elixir/Supervisor.html
     # for other strategies and supported options
     opts = [strategy: :one_for_one, name: Nola.Supervisor]
     sup = Supervisor.start_link(children, opts)
     start_telegram()
     spawn_link(fn() -> Nola.IRC.after_start() end)
     spawn_link(fn() -> Nola.Matrix.after_start() end)
     spawn_link(fn() -> Nola.TelegramRoom.after_start() end)
     sup
   end
 
   # Tell Phoenix to update the endpoint configuration
   # whenever the application is updated.
   def config_change(changed, _new, removed) do
     NolaWeb.Endpoint.config_change(changed, removed)
     :ok
   end
 
   defp start_telegram() do
-    token = Keyword.get(Application.get_env(:lsg, :telegram, []), :key)
+    token = Keyword.get(Application.get_env(:nola, :telegram, []), :key)
     options = [
-      username: Keyword.get(Application.get_env(:lsg, :telegram, []), :nick, "beauttebot"),
+      username: Keyword.get(Application.get_env(:nola, :telegram, []), :nick, "beauttebot"),
       purge: false
     ]
     telegram = Telegram.Bot.ChatBot.Supervisor.start_link({Nola.Telegram, token, options})
   end
 
 end
diff --git a/lib/lsg/icecast.ex b/lib/lsg/icecast.ex
index 60fb45a..5a53192 100644
--- a/lib/lsg/icecast.ex
+++ b/lib/lsg/icecast.ex
@@ -1,117 +1,117 @@
 defmodule Nola.Icecast do
   use GenServer
   require Logger
   @hackney_pool :default
   @httpoison_opts [hackney: [pool: @hackney_pool]]
   @fuse __MODULE__
 
   def start_link, do: GenServer.start_link(__MODULE__, [], [])
 
   def init(_) do
     GenServer.cast(self(), :poll)
     {:ok, nil}
   end
 
   def handle_cast(:poll, state) do
     state = poll(state)
     {:noreply, state}
   end
 
   def handle_info(:poll, state) do
     state = poll(state)
     {:noreply, state}
   end
 
   defp poll(state) do
     state = case request(base_url(), :get) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         #update_json_stats(Jason.decode(body))
         stats = update_stats(body)
         if state != stats do
           Logger.info "Icecast Update: " <> inspect(stats)
           Nola.IcecastAgent.update(stats)
           Registry.dispatch(Nola.BroadcastRegistry, "icecast", fn ws ->
             for {pid, _} <- ws, do: send(pid, {:icecast, stats})
           end)
           stats
         else
           state
         end
       error ->
         Logger.error "Icecast HTTP Error: #{inspect error}"
         state
     end
-    interval = Application.get_env(:lsg, :icecast_poll_interval, 60_000)
+    interval = Application.get_env(:nola, :icecast_poll_interval, 60_000)
     :timer.send_after(interval, :poll)
     state
   end
 
   defp update_stats(html) do
     raw = Floki.find(html, "div.roundbox")
     |> Enum.map(fn(html) ->
       html = Floki.raw_html(html)
       [{"h3", _, ["Mount Point /"<>mount]}] = Floki.find(html, "h3.mount")
       stats = Floki.find(html, "tr")
       |> Enum.map(fn({"tr", _, tds}) ->
         [{"td", _, keys}, {"td", _, values}] = tds
         key = List.first(keys)
         value = List.first(values)
         {key, value}
       end)
       |> Enum.into(Map.new)
       {mount, stats}
     end)
     |> Enum.into(Map.new)
 
     live? = if Map.get(raw["live"], "Content Type:", false), do: true, else: false
     np = if live? do
       raw["live"]["Currently playing:"]
     else
       raw["autodj"]["Currently playing:"]
     end
 
     genre = raw["live"]["Genre:"] || nil
     %{np: np || "", live: live? || false, genre: genre}
   end
 
   defp update_json_stats({:ok, body}) do
     Logger.debug "JSON STATS: #{inspect body}"
   end
 
   defp update_json_stats(error) do
     Logger.error "Failed to decode JSON Stats: #{inspect error}"
   end
 
   defp request(uri, method, body \\ [], headers \\ []) do
     headers = [{"user-agent", "Nola-API[115ans.net, sys.115ans.net] href@random.sh"}] ++ headers
     options = @httpoison_opts
     case :ok do #:fuse.ask(@fuse, :sync) do
       :ok -> run_request(method, uri, body, headers, options)
       :blown -> :blown
     end
   end
 
   # This is to work around hackney's behaviour of returning `{:error, :closed}` when a pool connection has been closed
   # (keep-alive expired). We just retry the request immediatly up to five times.
   defp run_request(method, uri, body, headers, options), do: run_request(method, uri, body, headers, options, 0)
   defp run_request(method, uri, body, headers, options, retries) when retries < 4 do
     case HTTPoison.request(method, uri, body, headers, options) do
       {:error, :closed} -> run_request(method, uri, body, headers, options, retries + 1)
       other -> other
     end
   end
   defp run_request(method, uri, body, headers, options, _exceeded_retries), do: {:error, :unavailable}
 
   #
   # -- URIs
   #
 
   defp stats_json_url do
     base_url() <> "/status-json.xsl"
   end
 
   defp base_url do
     "http://91.121.59.45:8089"
   end
 
 end
diff --git a/lib/lsg/lsg.ex b/lib/lsg/lsg.ex
index 11d0e24..0acb76e 100644
--- a/lib/lsg/lsg.ex
+++ b/lib/lsg/lsg.ex
@@ -1,30 +1,30 @@
 defmodule Nola do
 
   @default_brand [
     name: "Nola,
     source_url: "https://phab.random.sh/source/Bot/",
     owner: "Ashamed owner",
     owner_email: "contact@my.nola.bot"
   ]
 
-  def env(), do: Application.get_env(:lsg)
-  def env(key, default \\ nil), do: Application.get_env(:lsg, key, default)
+  def env(), do: Application.get_env(:nola)
+  def env(key, default \\ nil), do: Application.get_env(:nola, key, default)
 
   def brand(), do: env(:brand, @default_brand)
   def brand(key), do: Keyword.get(brand(), key)
   def name(), do: brand(:name)
   def source_url(), do: brand(:source_url)
 
   def data_path(suffix) do
     Path.join(data_path(), suffix)
   end
 
   def data_path do
-    Application.get_env(:lsg, :data_path)
+    Application.get_env(:nola, :data_path)
   end
 
   def version do
-    Application.spec(:lsg)[:vsn]
+    Application.spec(:nola)[:vsn]
   end
 
 end
diff --git a/lib/lsg_irc/base_plugin.ex b/lib/lsg_irc/base_plugin.ex
index ee0352e..a2b9ffb 100644
--- a/lib/lsg_irc/base_plugin.ex
+++ b/lib/lsg_irc/base_plugin.ex
@@ -1,131 +1,131 @@
 defmodule Nola.IRC.BasePlugin do
 
   def irc_doc, do: nil
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def init([]) do
     regopts = [plugin: __MODULE__]
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:version", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:help", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:liquidrender", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:plugin", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:plugins", regopts)
     {:ok, nil}
   end
 
   def handle_info({:irc, :trigger, "plugins", msg = %{trigger: %{type: :bang, args: []}}}, _) do
     enabled_string = IRC.Plugin.enabled()
     |> Enum.map(fn(mod) ->
       mod
       |> Macro.underscore()
       |> String.split("/", parts: :infinity)
       |> List.last()
       |> String.replace("_plugin", "")
       |> Enum.sort()
     end)
     |> Enum.join(", ")
     msg.replyfun.("Enabled plugins: #{enabled_string}")
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :query, args: [plugin]}} = m}, _) do
     module = Module.concat([Nola.IRC, Macro.camelize(plugin<>"_plugin")])
     with true <- Code.ensure_loaded?(module),
          pid when is_pid(pid) <- GenServer.whereis(module)
     do
       m.replyfun.("loaded, active: #{inspect(pid)}")
     else
       false -> m.replyfun.("not loaded")
       nil ->
         msg = case IRC.Plugin.get(module) do
           :disabled -> "disabled"
           {_, false, _} -> "disabled"
           _ -> "not active"
         end
         m.replyfun.(msg)
     end
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :plus, args: [plugin]}} = m}, _) do
     module = Module.concat([Nola.IRC, Macro.camelize(plugin<>"_plugin")])
     with true <- Code.ensure_loaded?(module),
          IRC.Plugin.switch(module, true),
          {:ok, pid} <- IRC.Plugin.start(module)
     do
       m.replyfun.("started: #{inspect(pid)}")
     else
       false -> m.replyfun.("not loaded")
       :ignore -> m.replyfun.("disabled or throttled")
       {:error, _} -> m.replyfun.("start error")
     end
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :tilde, args: [plugin]}} = m}, _) do
     module = Module.concat([Nola.IRC, Macro.camelize(plugin<>"_plugin")])
     with true <- Code.ensure_loaded?(module),
          pid when is_pid(pid) <- GenServer.whereis(module),
          :ok <- GenServer.stop(pid),
          {:ok, pid} <- IRC.Plugin.start(module)
     do
       m.replyfun.("restarted: #{inspect(pid)}")
     else
       false -> m.replyfun.("not loaded")
       nil -> m.replyfun.("not active")
     end
     {:noreply, nil}
   end
 
 
   def handle_info({:irc, :trigger, "plugin", %{trigger: %{type: :minus, args: [plugin]}} = m}, _) do
     module = Module.concat([Nola.IRC, Macro.camelize(plugin<>"_plugin")])
     with true <- Code.ensure_loaded?(module),
          pid when is_pid(pid) <- GenServer.whereis(module),
          :ok <- GenServer.stop(pid)
     do
       IRC.Plugin.switch(module, false)
       m.replyfun.("stopped: #{inspect(pid)}")
     else
       false -> m.replyfun.("not loaded")
       nil -> m.replyfun.("not active")
     end
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "liquidrender", m = %{trigger: %{args: args}}}, _) do
     template = Enum.join(args, " ")
     m.replyfun.(Tmpl.render(template, m))
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "help", m = %{trigger: %{type: :bang}}}, _) do
     url = NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :index, m.network, NolaWeb.format_chan(m.channel))
     m.replyfun.("-> #{url}")
     {:noreply, nil}
   end
 
   def handle_info({:irc, :trigger, "version", message = %{trigger: %{type: :bang}}}, _) do
-    {:ok, vsn} = :application.get_key(:lsg, :vsn)
+    {:ok, vsn} = :application.get_key(:nola, :vsn)
     ver = List.to_string(vsn)
     url = NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :index)
     elixir_ver = Application.started_applications() |> List.keyfind(:elixir, 0) |> elem(2) |> to_string()
     otp_ver = :erlang.system_info(:system_version) |> to_string() |> String.trim()
     system = :erlang.system_info(:system_architecture) |> to_string()
     message.replyfun.([
        <<"🤖 I am a robot running", 2, "beautte, version #{ver}", 2, " — source: #{Nola.source_url()}">>,
       "🦾 Elixir #{elixir_ver} #{otp_ver} on #{system}",
       "👷‍♀️ Owner: href <href@random.sh>",
       "🌍 Web interface: #{url}"
     ])
     {:noreply, nil}
   end
 
   def handle_info(msg, _) do
     {:noreply, nil}
   end
 
 end
diff --git a/lib/lsg_irc/finance_plugin.ex b/lib/lsg_irc/finance_plugin.ex
index c1a1771..16d06ee 100644
--- a/lib/lsg_irc/finance_plugin.ex
+++ b/lib/lsg_irc/finance_plugin.ex
@@ -1,190 +1,190 @@
 defmodule Nola.IRC.FinancePlugin do
   require Logger
 
   @moduledoc """
   # finance
 
   Données de [alphavantage.co](https://alphavantage.co).
 
   ## forex / monnaies / crypto-monnaies
 
   * **`!forex <MONNAIE1> [MONNAIE2]`**: taux de change entre deux monnaies.
   * **`!forex <MONTANT> <MONNAIE1> <MONNAIE2>`**: converti `montant` entre deux monnaies
   * **`?currency <recherche>`**: recherche une monnaie
 
   Utiliser le symbole des monnaies (EUR, USD, ...).
 
   ## bourses
 
   * **`!stocks <SYMBOLE>`**
   * **`?stocks <recherche>`** cherche un symbole
 
   Pour les symboles non-US, ajouter le suffixe (RNO Paris: RNO.PAR).
 
   """
 
   @currency_list "http://www.alphavantage.co/physical_currency_list/"
   @crypto_list "http://www.alphavantage.co/digital_currency_list/"
 
   HTTPoison.start()
   load_currency = fn(url) ->
     resp = HTTPoison.get!(url)
     resp.body
     |> String.strip()
     |> String.split("\n")
     |> Enum.drop(1)
     |> Enum.map(fn(line) ->
       [symbol, name] = line
       |> String.strip()
       |> String.split(",", parts: 2)
       {symbol, name}
     end)
     |> Enum.into(Map.new)
   end
   fiat = load_currency.(@currency_list)
   crypto = load_currency.(@crypto_list)
   @currencies Map.merge(fiat, crypto)
 
   def irc_doc, do: @moduledoc
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def init([]) do
     regopts = [plugin: __MODULE__]
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:forex", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:currency", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:stocks", regopts)
     {:ok, nil}
   end
 
 
   def handle_info({:irc, :trigger, "stocks", message = %{trigger: %{type: :query, args: args = search}}}, state) do
     search = Enum.join(search, "%20")
     url = "https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=#{search}&apikey=#{api_key()}"
     case HTTPoison.get(url) do
       {:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
         data = Poison.decode!(data)
         if error = Map.get(data, "Error Message") do
           Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
           message.replyfun.("stocks: requĂŞte invalide")
         else
           items = for item <- Map.get(data, "bestMatches") do
             symbol = Map.get(item, "1. symbol")
             name = Map.get(item, "2. name")
             type = Map.get(item, "3. type")
             region = Map.get(item, "4. region")
             currency = Map.get(item, "8. currency")
             "#{symbol}: #{name} (#{region}; #{currency}; #{type})"
           end
           |> Enum.join(", ")
           items = if items == "" do
             "no results!"
           else
             items
           end
           message.replyfun.(items)
         end
       {:ok, resp = %HTTPoison.Response{status_code: code}} ->
         Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
         message.replyfun.("forex: erreur (api #{code})")
       {:error, %HTTPoison.Error{reason: error}} ->
         Logger.error "AlphaVantage HTTP error: #{inspect error}"
         message.replyfun.("forex: erreur (http #{inspect error})")
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "stocks", message = %{trigger: %{type: :bang, args: args = [symbol]}}}, state) do
     url = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=#{symbol}&apikey=#{api_key()}"
     case HTTPoison.get(url) do
       {:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
         data = Poison.decode!(data)
         if error = Map.get(data, "Error Message") do
           Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
           message.replyfun.("stocks: requĂŞte invalide")
         else
           data = Map.get(data, "Global Quote")
           open = Map.get(data, "02. open")
           high = Map.get(data, "03. high")
           low = Map.get(data, "04. low")
           price = Map.get(data, "05. price")
           volume = Map.get(data, "06. volume")
           prev_close = Map.get(data, "08. previous close")
           change = Map.get(data, "09. change")
           change_pct = Map.get(data, "10. change percent")
 
           msg = "#{symbol}: #{price} #{change} [#{change_pct}] (high: #{high}, low: #{low}, open: #{open}, prev close: #{prev_close}) (volume: #{volume})"
           message.replyfun.(msg)
         end
       {:ok, resp = %HTTPoison.Response{status_code: code}} ->
         Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
         message.replyfun.("stocks: erreur (api #{code})")
       {:error, %HTTPoison.Error{reason: error}} ->
         Logger.error "AlphaVantage HTTP error: #{inspect error}"
         message.replyfun.("stocks: erreur (http #{inspect error})")
     end
     {:noreply, state}
   end
 
 
   def handle_info({:irc, :trigger, "forex", message = %{trigger: %{type: :bang, args: args = [_ | _]}}}, state) do
     {amount, from, to} = case args do
       [amount, from, to] ->
         {amount, _} = Float.parse(amount)
         {amount, from, to}
       [from, to] ->
         {1, from, to}
       [from] ->
         {1, from, "EUR"}
     end
     url = "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=#{from}&to_currency=#{to}&apikey=#{api_key()}"
     case HTTPoison.get(url) do
       {:ok, %HTTPoison.Response{status_code: 200, body: data}} ->
         data = Poison.decode!(data)
         if error = Map.get(data, "Error Message") do
           Logger.error("AlphaVantage API invalid request #{url} - #{inspect error}")
           message.replyfun.("forex: requĂŞte invalide")
         else
           data = Map.get(data, "Realtime Currency Exchange Rate")
           from_name = Map.get(data, "2. From_Currency Name")
           to_name = Map.get(data, "4. To_Currency Name")
           rate = Map.get(data, "5. Exchange Rate")
           {rate, _} = Float.parse(rate)
           value = amount*rate
           message.replyfun.("#{amount} #{from} (#{from_name}) -> #{value} #{to} (#{to_name}) (#{rate})")
         end
       {:ok, resp = %HTTPoison.Response{status_code: code}} ->
         Logger.error "AlphaVantage API error: #{code} #{url} - #{inspect resp}"
         message.replyfun.("forex: erreur (api #{code})")
       {:error, %HTTPoison.Error{reason: error}} ->
         Logger.error "AlphaVantage HTTP error: #{inspect error}"
         message.replyfun.("forex: erreur (http #{inspect error})")
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "currency", message = %{trigger: %{type: :query, args: args = search}}}, state) do
     search = Enum.join(search, " ")
     results = Enum.filter(@currencies, fn({symbol, name}) ->
       String.contains?(String.downcase(name), String.downcase(search)) || String.contains?(String.downcase(symbol), String.downcase(search))
     end)
     |> Enum.map(fn({symbol, name}) ->
       "#{symbol}: #{name}"
     end)
     |> Enum.join(", ")
 
     if results == "" do
       message.replyfun.("no results!")
     else
       message.replyfun.(results)
     end
     {:noreply, state}
   end
 
   defp api_key() do
-    Application.get_env(:lsg, :alphavantage, [])
+    Application.get_env(:nola, :alphavantage, [])
     |> Keyword.get(:api_key, "demo")
   end
 
 end
diff --git a/lib/lsg_irc/last_fm_plugin.ex b/lib/lsg_irc/last_fm_plugin.ex
index f29982c..03df675 100644
--- a/lib/lsg_irc/last_fm_plugin.ex
+++ b/lib/lsg_irc/last_fm_plugin.ex
@@ -1,187 +1,187 @@
 defmodule Nola.IRC.LastFmPlugin do
   require Logger
 
   @moduledoc """
   # last.fm
 
   * **!lastfm|np `[nick|username]`**
   * **.lastfm|np**
   * **+lastfm, -lastfm `<username last.fm>; ?lastfm`** Configurer un nom d'utilisateur last.fm
   """
 
   @single_trigger ~w(lastfm np)
   @pubsub_topics ~w(trigger:lastfm trigger:np)
 
   defstruct dets: nil
 
   def irc_doc, do: @moduledoc
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def init([]) do
     regopts = [type: __MODULE__]
     for t <- @pubsub_topics, do: {:ok, _} = Registry.register(IRC.PubSub, t, type: __MODULE__)
     dets_filename = (Nola.data_path() <> "/" <> "lastfm.dets") |> String.to_charlist
     {:ok, dets} = :dets.open_file(dets_filename, [])
     {:ok, %__MODULE__{dets: dets}}
   end
 
   def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :plus, args: [username]}}}, state) do
     username = String.strip(username)
     :ok = :dets.insert(state.dets, {message.account.id, username})
     message.replyfun.("#{message.sender.nick}: nom d'utilisateur last.fm configuré: \"#{username}\".")
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :minus, args: []}}}, state) do
     text = case :dets.lookup(state.dets, message.account.id) do
              [{_nick, _username}] ->
                :dets.delete(state.dets, message.account.id)
                message.replyfun.("#{message.sender.nick}: nom d'utilisateur last.fm enlevé.")
              _ -> nil
            end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "lastfm", message = %{trigger: %{type: :query, args: []}}}, state) do
     text = case :dets.lookup(state.dets, message.account.id) do
       [{_nick, username}] ->
         message.replyfun.("#{message.sender.nick}: #{username}.")
       _ -> nil
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :bang, args: []}}}, state) do
     irc_now_playing(message.account.id, message, state)
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :bang, args: [nick_or_user]}}}, state) do
     irc_now_playing(nick_or_user, message, state)
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, _, message = %{trigger: %{type: :dot}}}, state) do
     members = IRC.Membership.members(message.network, message.channel)
     foldfun = fn({nick, user}, acc) -> [{nick,user}|acc] end
     usernames = :dets.foldl(foldfun, [], state.dets)
                 |> Enum.uniq()
                 |> Enum.filter(fn({acct,_}) -> Enum.member?(members, acct) end)
                 |> Enum.map(fn({_, u}) -> u end)
     for u <- usernames, do: irc_now_playing(u, message, state)
     {:noreply, state}
   end
 
   def handle_info(info, state) do
     {:noreply, state}
   end
 
   def terminate(_reason, state) do
     if state.dets do
       :dets.sync(state.dets)
       :dets.close(state.dets)
     end
     :ok
   end
 
   defp irc_now_playing(nick_or_user, message, state) do
     nick_or_user = String.strip(nick_or_user)
 
     id_or_user = if account = IRC.Account.get(nick_or_user) || IRC.Account.find_always_by_nick(message.network, message.channel, nick_or_user) do
       account.id
     else
       nick_or_user
     end
 
     username = case :dets.lookup(state.dets, id_or_user) do
       [{_, username}] -> username
       _ -> id_or_user
     end
 
     case now_playing(username) do
       {:error, text} when is_binary(text) ->
         message.replyfun.(text)
       {:ok, map} when is_map(map) ->
         track = fetch_track(username, map)
         text = format_now_playing(map, track)
         user = if account = IRC.Account.get(id_or_user) do
           user = IRC.UserTrack.find_by_account(message.network, account)
           if(user, do: user.nick, else: account.name)
         else
           username
         end
         if user && text do
           message.replyfun.("#{user} #{text}")
         else
           message.replyfun.("#{username}: pas de résultat")
         end
       other ->
         message.replyfun.("erreur :(")
     end
   end
 
   defp now_playing(user) do
-    api = Application.get_env(:lsg, :lastfm)[:api_key]
+    api = Application.get_env(:nola, :lastfm)[:api_key]
     url = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&format=json&limit=1&extended=1" <> "&api_key=" <> api <> "&user="<> user
     case HTTPoison.get(url) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> Jason.decode(body)
       {:ok, %HTTPoison.Response{status_code: 404}} -> {:error, "last.fm: utilisateur \"#{user}\" inexistant"}
       {:ok, %HTTPoison.Response{status_code: code}} -> {:error, "last.fm: erreur #{to_string(code)}"}
       error ->
         Logger.error "Lastfm http error: #{inspect error}"
         :error
     end
   end
   defp fetch_track(user, %{"recenttracks" => %{"track" => [ t = %{"name" => name, "artist" => %{"name" => artist}} | _]}}) do
-    api = Application.get_env(:lsg, :lastfm)[:api_key]
+    api = Application.get_env(:nola, :lastfm)[:api_key]
     url = "http://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json" <> "&api_key=" <> api <> "&username="<> user <> "&artist="<>URI.encode(artist)<>"&track="<>URI.encode(name)
     case HTTPoison.get(url) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         case Jason.decode(body) do
           {:ok, body} -> body["track"] || %{}
           _ -> %{}
         end
       error ->
         Logger.error "Lastfm http error: #{inspect error}"
         :error
     end
   end
 
   defp format_now_playing(%{"recenttracks" => %{"track" => [track = %{"@attr" => %{"nowplaying" => "true"}} | _]}}, et) do
     format_track(true, track, et)
   end
 
   defp format_now_playing(%{"recenttracks" => %{"track" => [track | _]}}, et) do
     format_track(false, track, et)
   end
 
   defp format_now_playing(%{"error" => err, "message" => message}, _) do
     "last.fm error #{err}: #{message}"
   end
 
   defp format_now_playing(miss) do
     nil
   end
 
   defp format_track(np, track, extended) do
     artist = track["artist"]["name"]
     album = if track["album"]["#text"], do: " (" <> track["album"]["#text"] <> ")", else: ""
     name = track["name"] <> album
     action = if np, do: "écoute ", else: "a écouté"
     love = if track["loved"] != "0", do: "❤️"
     count = if x = extended["userplaycount"], do: "x#{x} #{love}"
     tags = (get_in(extended, ["toptags", "tag"]) || [])
     |> Enum.map(fn(tag) -> tag["name"] end)
     |> Enum.filter(& &1)
     |> Enum.join(", ")
 
     [action, artist, name, count, tags, track["url"]]
     |> Enum.filter(& &1)
     |> Enum.map(&String.trim(&1))
     |> Enum.join(" - ")
   end
 
 end
diff --git a/lib/lsg_irc/link_plugin.ex b/lib/lsg_irc/link_plugin.ex
index 28e537a..dee78e8 100644
--- a/lib/lsg_irc/link_plugin.ex
+++ b/lib/lsg_irc/link_plugin.ex
@@ -1,271 +1,271 @@
 defmodule Nola.IRC.LinkPlugin do
   @moduledoc """
   # Link Previewer
 
   An extensible link previewer for IRC.
 
   To extend the supported sites, create a new handler implementing the callbacks.
 
   See `link_plugin/` directory for examples. The first in list handler that returns true to the `match/2` callback will be used,
   and if the handler returns `:error` or crashes, will fallback to the default preview.
 
   Unsupported websites will use the default link preview method, which is for html document the title, otherwise it'll use
   the mimetype and size.
 
   ## Configuration:
 
   ```
-  config :lsg, Nola.IRC.LinkPlugin,
+  config :nola, Nola.IRC.LinkPlugin,
     handlers: [
       Nola.IRC.LinkPlugin.Youtube: [
         invidious: true
       ],
       Nola.IRC.LinkPlugin.Twitter: [],
       Nola.IRC.LinkPlugin.Imgur: [],
     ]
   ```
 
   """
 
   @ircdoc """
   # Link preview
 
   Previews links (just post a link!).
 
   Announces real URL after redirections and provides extended support for YouTube, Twitter and Imgur.
   """
   def short_irc_doc, do: false
   def irc_doc, do: @ircdoc
   require Logger
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   @callback match(uri :: URI.t, options :: Keyword.t) :: {true, params :: Map.t} | false
   @callback expand(uri :: URI.t, params :: Map.t, options :: Keyword.t) :: {:ok, lines :: [] | String.t} | :error
   @callback post_match(uri :: URI.t, content_type :: binary, headers :: [], opts :: Keyword.t) :: {:body | :file, params :: Map.t} | false
   @callback post_expand(uri :: URI.t, body :: binary() | Path.t, params :: Map.t, options :: Keyword.t) :: {:ok, lines :: [] | String.t} | :error
 
   @optional_callbacks [expand: 3, post_expand: 4]
 
   defstruct [:client]
 
   def init([]) do
     {:ok, _} = Registry.register(IRC.PubSub, "messages", [plugin: __MODULE__])
     #{:ok, _} = Registry.register(IRC.PubSub, "messages:telegram", [plugin: __MODULE__])
     Logger.info("Link handler started")
     {:ok, %__MODULE__{}}
   end
 
   def handle_info({:irc, :text, message = %{text: text}}, state) do
     String.split(text)
     |> Enum.map(fn(word) ->
       if String.starts_with?(word, "http://") || String.starts_with?(word, "https://") do
         uri = URI.parse(word)
         if uri.scheme && uri.host do
           spawn(fn() ->
             :timer.kill_after(:timer.seconds(30))
             case expand_link([uri]) do
               {:ok, uris, text} ->
                 text = case uris do
                   [uri] -> text
                   [luri | _] ->
                     if luri.host == uri.host && luri.path == luri.path do
                       text
                     else
                       ["-> #{URI.to_string(luri)}", text]
                     end
                 end
                 if is_list(text) do
                   for line <- text, do: message.replyfun.(line)
                 else
                   message.replyfun.(text)
                 end
               _ -> nil
             end
           end)
         end
       end
     end)
     {:noreply, state}
   end
 
   def handle_info(msg, state) do
     {:noreply, state}
   end
 
   def terminate(_reason, state) do
     :ok
   end
 
   # 1. Match the first valid handler
   # 2. Try to run the handler
   # 3. If :error or crash, default link.
   #    If :skip, nothing
   # 4. ?
 
   # Over five redirections: cancel.
   def expand_link(acc = [_, _, _, _, _ | _]) do
     {:ok, acc, "link redirects more than five times"}
   end
 
   def expand_link(acc=[uri | _]) do
     Logger.debug("link: expanding: #{inspect uri}")
-    handlers = Keyword.get(Application.get_env(:lsg, __MODULE__, [handlers: []]), :handlers)
+    handlers = Keyword.get(Application.get_env(:nola, __MODULE__, [handlers: []]), :handlers)
     handler = Enum.reduce_while(handlers, nil, fn({module, opts}, acc) ->
       Logger.debug("link: attempt expanding: #{inspect module} for #{inspect uri}")
       module = Module.concat([module])
       case module.match(uri, opts) do
         {true, params} -> {:halt, {module, params, opts}}
         false -> {:cont, acc}
       end
     end)
     run_expand(acc, handler)
   end
 
   def run_expand(acc, nil) do
     expand_default(acc)
   end
 
   def run_expand(acc=[uri|_], {module, params, opts}) do
     Logger.debug("link: expanding #{inspect uri} with #{inspect module}")
     case module.expand(uri, params, opts) do
       {:ok, data} -> {:ok, acc, data}
       :error -> expand_default(acc)
       :skip -> nil
     end
   rescue
     e ->
       Logger.error("link: rescued #{inspect uri} with #{inspect module}: #{inspect e}")
       Logger.error(Exception.format(:error, e, __STACKTRACE__))
       expand_default(acc)
   catch
     e, b ->
       Logger.error("link: catched #{inspect uri} with #{inspect module}: #{inspect {e, b}}")
       expand_default(acc)
   end
 
   defp get(url, headers \\ [], options \\ []) do
     get_req(url, :hackney.get(url, headers, <<>>, options))
   end
 
   defp get_req(_, {:error, reason}) do
     {:error, reason}
   end
 
   defp get_req(url, {:ok, 200, headers, client}) do
     headers = Enum.reduce(headers, %{}, fn({key, value}, acc) ->
       Map.put(acc, String.downcase(key), value)
     end)
     content_type = Map.get(headers, "content-type", "application/octect-stream")
     length = Map.get(headers, "content-length", "0")
     {length, _} = Integer.parse(length)
 
-    handlers = Keyword.get(Application.get_env(:lsg, __MODULE__, [handlers: []]), :handlers)
+    handlers = Keyword.get(Application.get_env(:nola, __MODULE__, [handlers: []]), :handlers)
     handler = Enum.reduce_while(handlers, false, fn({module, opts}, acc) ->
       module = Module.concat([module])
       try do
         case module.post_match(url, content_type, headers, opts) do
           {mode, params} when mode in [:body, :file] -> {:halt, {module, params, opts, mode}}
           false -> {:cont, acc}
         end
       rescue
         e ->
           Logger.error(inspect(e))
           {:cont, false}
       catch
         e, b ->
           Logger.error(inspect({b}))
           {:cont, false}
       end
     end)
 
     cond do
       handler != false and length <= 30_000_000 ->
         case get_body(url, 30_000_000, client, handler, <<>>) do
           {:ok, _} = ok -> ok
           :error ->
             {:ok, "file: #{content_type}, size: #{human_size(length)}"}
         end
       #String.starts_with?(content_type, "text/html") && length <= 30_000_000 ->
       #  get_body(url, 30_000_000, client, <<>>)
       true ->
         :hackney.close(client)
         {:ok, "file: #{content_type}, size: #{human_size(length)}"}
     end
   end
 
   defp get_req(_, {:ok, redirect, headers, client}) when redirect in 300..399 do
     headers = Enum.reduce(headers, %{}, fn({key, value}, acc) ->
       Map.put(acc, String.downcase(key), value)
     end)
     location = Map.get(headers, "location")
 
     :hackney.close(client)
     {:redirect, location}
   end
 
   defp get_req(_, {:ok, status, headers, client}) do
     :hackney.close(client)
     {:error, status, headers}
   end
 
   defp get_body(url, len, client, {handler, params, opts, mode} = h, acc) when len >= byte_size(acc) do
     case :hackney.stream_body(client) do
       {:ok, data} ->
         get_body(url, len, client, h, << acc::binary, data::binary >>)
       :done ->
         body = case mode do
           :body -> acc
           :file ->
             {:ok, tmpfile} = Plug.Upload.random_file("linkplugin")
             File.write!(tmpfile, acc)
             tmpfile
         end
         handler.post_expand(url, body, params, opts)
       {:error, reason} ->
         {:ok, "failed to fetch body: #{inspect reason}"}
     end
   end
 
   defp get_body(_, len, client, h, _acc) do
     :hackney.close(client)
     IO.inspect(h)
     {:ok, "Error: file over 30"}
   end
 
   def expand_default(acc = [uri = %URI{scheme: scheme} | _]) when scheme in ["http", "https"] do
     Logger.debug("link: expanding #{uri} with default")
     headers = [{"user-agent", "DmzBot (like TwitterBot)"}]
     options = [follow_redirect: false, max_body_length: 30_000_000]
     case get(URI.to_string(uri), headers, options) do
       {:ok, text} ->
         {:ok, acc, text}
       {:redirect, link} ->
         new_uri = URI.parse(link)
         #new_uri = %URI{new_uri | scheme: scheme, authority: uri.authority, host: uri.host, port: uri.port}
         expand_link([new_uri | acc])
       {:error, status, _headers} ->
         text = Plug.Conn.Status.reason_phrase(status)
         {:ok, acc, "Error: HTTP #{text} (#{status})"}
       {:error, {:tls_alert, {:handshake_failure, err}}} ->
         {:ok, acc, "TLS Error: #{to_string(err)}"}
       {:error, reason} ->
         {:ok, acc, "Error: #{to_string(reason)}"}
     end
   end
 
   # Unsupported scheme, came from a redirect.
   def expand_default(acc = [uri | _]) do
     {:ok, [uri], "-> #{URI.to_string(uri)}"}
   end
 
 
   defp human_size(bytes) do
     bytes
     |> FileSize.new(:b)
     |> FileSize.scale()
     |> FileSize.format()
   end
 end
diff --git a/lib/lsg_irc/link_plugin/imgur.ex b/lib/lsg_irc/link_plugin/imgur.ex
index af0b36a..5d74956 100644
--- a/lib/lsg_irc/link_plugin/imgur.ex
+++ b/lib/lsg_irc/link_plugin/imgur.ex
@@ -1,96 +1,96 @@
 defmodule Nola.IRC.LinkPlugin.Imgur do
   @behaviour Nola.IRC.LinkPlugin
 
   @moduledoc """
   # Imgur link preview
 
   No options.
 
   Needs to have a Imgur API key configured:
 
   ```
-  config :lsg, :imgur,
+  config :nola, :imgur,
     client_id: "xxxxxxxx",
     client_secret: "xxxxxxxxxxxxxxxxxxxx"
   ```
   """
 
   @impl true
   def match(uri = %URI{host: "imgur.io"}, arg) do
     match(%URI{uri | host: "imgur.com"}, arg)
   end
   def match(uri = %URI{host: "i.imgur.io"}, arg) do
     match(%URI{uri | host: "i.imgur.com"}, arg)
   end
   def match(uri = %URI{host: "imgur.com", path: "/a/"<>album_id}, _) do
     {true, %{album_id: album_id}}
   end
   def match(uri = %URI{host: "imgur.com", path: "/gallery/"<>album_id}, _) do
     {true, %{album_id: album_id}}
   end
   def match(uri = %URI{host: "i.imgur.com", path: "/"<>image}, _) do
     [hash, _] = String.split(image, ".", parts: 2)
     {true, %{image_id: hash}}
   end
   def match(_, _), do: false
 
   @impl true
   def post_match(_, _, _, _), do: false
 
   def expand(_uri, %{album_id: album_id}, opts) do
     expand_imgur_album(album_id, opts)
   end
 
   def expand(_uri, %{image_id: image_id}, opts) do
     expand_imgur_image(image_id, opts)
   end
 
   def expand_imgur_image(image_id, opts) do
-    client_id = Keyword.get(Application.get_env(:lsg, :imgur, []), :client_id, "42")
+    client_id = Keyword.get(Application.get_env(:nola, :imgur, []), :client_id, "42")
     headers = [{"Authorization", "Client-ID #{client_id}"}]
     options = []
     case HTTPoison.get("https://api.imgur.com/3/image/#{image_id}", headers, options) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         {:ok, json} = Jason.decode(body)
         data = json["data"]
         title = String.slice(data["title"] || data["description"], 0, 180)
         nsfw = if data["nsfw"], do: "(NSFW) - ", else: " "
         height = Map.get(data, "height")
         width = Map.get(data, "width")
         size = Map.get(data, "size")
         {:ok, "image, #{width}x#{height}, #{size} bytes #{nsfw}#{title}"}
       other ->
         :error
     end
   end
 
   def expand_imgur_album(album_id, opts) do
-    client_id = Keyword.get(Application.get_env(:lsg, :imgur, []), :client_id, "42")
+    client_id = Keyword.get(Application.get_env(:nola, :imgur, []), :client_id, "42")
     headers = [{"Authorization", "Client-ID #{client_id}"}]
     options = []
     case HTTPoison.get("https://api.imgur.com/3/album/#{album_id}", headers, options) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         {:ok, json} = Jason.decode(body)
         data = json["data"]
         title = data["title"]
         nsfw = data["nsfw"]
         nsfw = if nsfw, do: "(NSFW) - ", else: ""
         if data["images_count"] == 1 do
           [image] = data["images"]
           title = if title || data["title"] do
             title = [title, data["title"]] |> Enum.filter(fn(x) -> x end) |> Enum.uniq() |> Enum.join(" — ")
             "#{title} — "
           else
             ""
           end
           {:ok, "#{nsfw}#{title}#{image["link"]}"}
         else
           title = if title, do: title, else: "Untitled album"
           {:ok, "#{nsfw}#{title} - #{data["images_count"]} images"}
         end
       other ->
         :error
     end
   end
 
 end
diff --git a/lib/lsg_irc/link_plugin/youtube.ex b/lib/lsg_irc/link_plugin/youtube.ex
index 1e3a0de..f7c7541 100644
--- a/lib/lsg_irc/link_plugin/youtube.ex
+++ b/lib/lsg_irc/link_plugin/youtube.ex
@@ -1,72 +1,72 @@
 defmodule Nola.IRC.LinkPlugin.YouTube do
   @behaviour Nola.IRC.LinkPlugin
 
   @moduledoc """
   # YouTube link preview
 
   needs an API key:
 
   ```
-  config :lsg, :youtube,
+  config :nola, :youtube,
     api_key: "xxxxxxxxxxxxx"
   ```
 
   options:
 
   * `invidious`: Add a link to invidious.
   """
 
   @impl true
   def match(uri = %URI{host: yt, path: "/watch", query: "v="<>video_id}, _opts) when yt in ["youtube.com", "www.youtube.com"] do
     {true, %{video_id: video_id}}
   end
 
   def match(%URI{host: "youtu.be", path: "/"<>video_id}, _opts) do
     {true, %{video_id: video_id}}
   end
 
   def match(_, _), do: false
 
   @impl true
   def post_match(_, _, _, _), do: false
 
   @impl true
   def expand(uri, %{video_id: video_id}, opts) do
-    key = Application.get_env(:lsg, :youtube)[:api_key]
+    key = Application.get_env(:nola, :youtube)[:api_key]
     params = %{
       "part" => "snippet,contentDetails,statistics",
       "id" => video_id,
       "key" => key
     }
     headers = []
     options = [params: params]
     case HTTPoison.get("https://www.googleapis.com/youtube/v3/videos", [], options) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         case Jason.decode(body) do
           {:ok, json} ->
             item = List.first(json["items"])
             if item do
               snippet = item["snippet"]
               duration = item["contentDetails"]["duration"] |> String.replace("PT", "") |> String.downcase
               date = snippet["publishedAt"]
                      |> DateTime.from_iso8601()
                      |> elem(1)
                      |> Timex.format("{relative}", :relative)
                      |> elem(1)
 
               line = if host = Keyword.get(opts, :invidious) do
                 ["-> https://#{host}/watch?v=#{video_id}"]
               else
                   []
               end
               {:ok, line ++ ["#{snippet["title"]}", "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}"
                      <> " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes"]}
             else
               :error
             end
           _ -> :error
         end
     end
   end
 
 end
diff --git a/lib/lsg_irc/preums_plugin.ex b/lib/lsg_irc/preums_plugin.ex
index 9344ce9..f250e85 100644
--- a/lib/lsg_irc/preums_plugin.ex
+++ b/lib/lsg_irc/preums_plugin.ex
@@ -1,276 +1,276 @@
 defmodule Nola.IRC.PreumsPlugin do
   @moduledoc """
   # preums !!!
 
   * `!preums`: affiche le preums du jour
   * `.preums`: stats des preums
   """
 
   # WIP Scores
   # L'idée c'est de donner un score pour mettre un peu de challenge en pénalisant les preums faciles.
   #
   # Un preums ne vaut pas 1 point, mais plutĂ´t 0.10 ou 0.05, et on arrondi au plus proche. C'est un jeu sur le long
   # terme. Un gros bonus pourrait apporter beaucoup de points.
   #
   # Il faudrait ces données:
   #   - moyenne des preums
   #   - activité récente du channel et par nb actifs d'utilisateurs
   #      (aggréger memberships+usertrack last_active ?)
   #      (faire des stats d'activité habituelle (un peu a la pisg) ?)
   #   - preums consécutifs
   #
   # Malus:
   #   - est proche de la moyenne en faible activité
   #   - trop consécutif de l'utilisateur sauf si activité
   #
   # Bonus:
   #   - plus le preums est éloigné de la moyenne
   #   - après 18h double
   #   - plus l'activité est élévée, exponentiel selon la moyenne
   #   - derns entre 4 et 6 (pourrait être adapté selon les stats d'activité)
   #
   # WIP Badges:
   #   - derns
   #   - streaks
   #   - faciles
   #   - ?
 
   require Logger
 
   @perfects [~r/preum(s|)/i]
 
   # dets {{chan, day = {yyyy, mm, dd}}, nick, now, perfect?, text}
 
   def all(dets) do
     :dets.foldl(fn(i, acc) -> [i|acc] end, [], dets)
   end
 
   def all(dets, channel) do
     fun = fn({{chan, date}, account_id, time, perfect, text}, acc) ->
       if channel == chan do
         [%{date: date, account_id: account_id, time: time, perfect: perfect, text: text} | acc]
       else
         acc
       end
     end
     :dets.foldl(fun, [], dets)
   end
 
   def topnicks(dets, channel, options \\ []) do
     sort_elem = case Keyword.get(options, :sort_by, :score) do
       :score -> 1
       :count -> 0
     end
 
     fun = fn(x = {{chan, date}, account_id, time, perfect, text}, acc) ->
       if (channel == nil and chan) or (channel == chan) do
         {count, points} = Map.get(acc, account_id, {0, 0})
         score = score(chan, account_id, time, perfect, text)
         Map.put(acc, account_id, {count + 1, points + score})
       else
         acc
       end
     end
     :dets.foldl(fun, %{}, dets)
     |> Enum.sort_by(fn({_account_id, value}) -> elem(value, sort_elem) end, &>=/2)
   end
 
   def irc_doc, do: @moduledoc
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def dets do
     (Nola.data_path() <> "/preums.dets") |> String.to_charlist()
   end
 
   def init([]) do
     regopts = [plugin: __MODULE__]
     {:ok, _} = Registry.register(IRC.PubSub, "account", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "messages", regopts)
     {:ok, _} = Registry.register(IRC.PubSub, "triggers", regopts)
     {:ok, dets} = :dets.open_file(dets(), [{:repair, :force}])
     Util.ets_mutate_select_each(:dets, dets, [{:"$1", [], [:"$1"]}], fn(table, obj) ->
       {key, nick, now, perfect, text} = obj
       case key do
         {{net, {bork,chan}}, date} ->
           :dets.delete(table, key)
           nick = if IRC.Account.get(nick) do
             nick
           else
             if acct = IRC.Account.find_always_by_nick(net, nil, nick) do
               acct.id
             else
               nick
             end
           end
           :dets.insert(table, { { {net,chan}, date }, nick, now, perfect, text})
         {{_net, nil}, _} ->
           :dets.delete(table, key)
         {{net, chan}, date} ->
           if !IRC.Account.get(nick) do
             if acct = IRC.Account.find_always_by_nick(net, chan, nick) do
               :dets.delete(table, key)
               :dets.insert(table, { { {net,chan}, date }, acct.id, now, perfect, text})
             end
           end
         _ ->
           Logger.debug("DID NOT FIX: #{inspect key}")
       end
     end)
     {:ok, %{dets: dets}}
   end
 
   # Latest
   def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang}}}, state) do
     channelkey = {m.network, m.channel}
     state = handle_preums(m, state)
     tz = timezone(channelkey)
     {:ok, now} = DateTime.now(tz, Tzdata.TimeZoneDatabase)
     date = {now.year, now.month, now.day}
     key = {channelkey, date}
     chan_cache = Map.get(state, channelkey, %{})
     item = if i = Map.get(chan_cache, date) do
       i
     else
       case :dets.lookup(state.dets, key) do
         [item = {^key, _account_id, _now, _perfect, _text}] -> item
         _ -> nil
       end
     end
 
     if item do
       {_, account_id, date, _perfect, text} = item
       h = "#{date.hour}:#{date.minute}:#{date.second}"
       account = IRC.Account.get(account_id)
       user = IRC.UserTrack.find_by_account(m.network, account)
       nick = if(user, do: user.nick, else: account.name)
       m.replyfun.("preums: #{nick} à #{h}: “#{text}”")
     end
     {:noreply, state}
   end
 
   # Stats
   def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :dot}}}, state) do
     channel = {m.network, m.channel}
     state = handle_preums(m, state)
     top = topnicks(state.dets, channel, sort_by: :score)
           |> Enum.map(fn({account_id, {count, score}}) ->
             account = IRC.Account.get(account_id)
             user = IRC.UserTrack.find_by_account(m.network, account)
             nick = if(user, do: user.nick, else: account.name)
             "#{nick}: #{score} (#{count})"
           end)
           |> Enum.intersperse(", ")
           |> Enum.join("")
     msg = unless top == "" do
       "top preums: #{top}"
     else
       "vous ĂŞtes tous nuls"
     end
     m.replyfun.(msg)
     {:noreply, state}
   end
 
   # Help
   def handle_info({:irc, :trigger, "preums", m = %IRC.Message{trigger: %IRC.Trigger{type: :query}}}, state) do
     state = handle_preums(m, state)
     msg = "!preums - preums du jour, .preums top preumseurs"
     m.replymsg.(msg)
     {:noreply, state}
   end
 
   # Trigger fallback
   def handle_info({:irc, :trigger, _, m = %IRC.Message{}}, state) do
     state = handle_preums(m, state)
     {:noreply, state}
   end
 
   # Message fallback
   def handle_info({:irc, :text, m = %IRC.Message{}}, state) do
     {:noreply, handle_preums(m, state)}
   end
 
   # Account
   def handle_info({:account_change, old_id, new_id}, state) do
     spec = [{{:_, :"$1", :_, :_, :_}, [{:==, :"$1", {:const, old_id}}], [:"$_"]}]
     Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
       rename_object_owner(table, obj, new_id)
     end)
     {:noreply, state}
   end
 
   # Account: move from nick to account id
   # FIXME: Doesn't seem to work.
   def handle_info({:accounts, accounts}, state) do
     for x={:account, _net, _chan, _nick, _account_id} <- accounts do
       handle_info(x, state)
     end
     {:noreply, state}
   end
   def handle_info({:account, _net, _chan, nick, account_id}, state) do
     nick = String.downcase(nick)
     spec = [{{:_, :"$1", :_, :_, :_}, [{:==, :"$1", {:const, nick}}], [:"$_"]}]
     Util.ets_mutate_select_each(:dets, state.dets, spec, fn(table, obj) ->
       Logger.debug("account:: merging #{nick} -> #{account_id}")
       rename_object_owner(table, obj, account_id)
     end)
     {:noreply, state}
   end
 
   def handle_info(_, dets) do
     {:noreply, dets}
   end
 
 
   defp rename_object_owner(table, object = {key, _, now, perfect, time}, new_id) do
     :dets.delete_object(table, key)
     :dets.insert(table, {key, new_id, now, perfect, time})
   end
 
   defp timezone(channel) do
-    env = Application.get_env(:lsg, Nola.IRC.PreumsPlugin, [])
+    env = Application.get_env(:nola, Nola.IRC.PreumsPlugin, [])
     channels = Keyword.get(env, :channels, %{})
     channel_settings = Map.get(channels, channel, [])
     default = Keyword.get(env, :default_tz, "Europe/Paris")
     Keyword.get(channel_settings, :tz, default) || default
   end
 
   defp handle_preums(%IRC.Message{channel: nil}, state) do
     state
   end
 
   defp handle_preums(m = %IRC.Message{text: text, sender: sender}, state) do
     channel = {m.network, m.channel}
     tz = timezone(channel)
     {:ok, now} = DateTime.now(tz, Tzdata.TimeZoneDatabase)
     date = {now.year, now.month, now.day}
     key = {channel, date}
     chan_cache = Map.get(state, channel, %{})
     unless i = Map.get(chan_cache, date) do
       case :dets.lookup(state.dets, key) do
         [item = {^key, _nick, _now, _perfect, _text}] ->
           # Preums lost, but wasn't cached
           Map.put(state, channel, %{date => item})
         [] ->
           # Preums won!
           perfect? = Enum.any?(@perfects, fn(perfect) -> Regex.match?(perfect, text) end)
           item = {key, m.account.id, now, perfect?, text}
           :dets.insert(state.dets, item)
           :dets.sync(state.dets)
           Map.put(state, channel, %{date => item})
         {:error, _} = error ->
           Logger.error("#{__MODULE__} dets lookup failed: #{inspect error}")
           state
       end
     else
       state
     end
   end
 
   def score(_chan, _account, _time, _perfect, _text) do
     1
   end
 
 
 end
diff --git a/lib/lsg_irc/sms_plugin.ex b/lib/lsg_irc/sms_plugin.ex
index 2bbf13e..d8f7387 100644
--- a/lib/lsg_irc/sms_plugin.ex
+++ b/lib/lsg_irc/sms_plugin.ex
@@ -1,165 +1,165 @@
 defmodule Nola.IRC.SmsPlugin do
   @moduledoc """
   ## sms
 
   * **!sms `<nick>` `<message>`** envoie un SMS.
   """
   def short_irc_doc, do: false
   def irc_doc, do: @moduledoc
   require Logger
 
   def incoming(from, "enable "<>key) do
     key = String.trim(key)
     account = IRC.Account.find_meta_account("sms-validation-code", String.downcase(key))
     if account do
       net = IRC.Account.get_meta(account, "sms-validation-target")
       IRC.Account.put_meta(account, "sms-number", from)
       IRC.Account.delete_meta(account, "sms-validation-code")
       IRC.Account.delete_meta(account, "sms-validation-number")
       IRC.Account.delete_meta(account, "sms-validation-target")
       IRC.Connection.broadcast_message(net, account, "SMS Number #{from} added!")
       send_sms(from, "Yay! Number linked to account #{account.name}")
     end
   end
 
   def incoming(from, message) do
     account = IRC.Account.find_meta_account("sms-number", from)
     if account do
       reply_fun = fn(text) ->
         send_sms(from, text)
       end
       trigger_text = if Enum.any?(IRC.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(message, trigger) end) do
         message
       else
         "!"<>message
       end
       message = %IRC.Message{
         id: FlakeId.get(),
         transport: :sms,
         network: "sms",
         channel: nil,
         text: message,
         account: account,
         sender: %ExIRC.SenderInfo{nick: account.name},
         replyfun: reply_fun,
         trigger: IRC.Connection.extract_trigger(trigger_text)
       }
       Logger.debug("converted sms to message: #{inspect message}")
       IRC.Connection.publish(message, ["messages:sms"])
       message
     end
   end
 
   def my_number() do
-    Keyword.get(Application.get_env(:lsg, :sms, []), :number, "+33000000000")
+    Keyword.get(Application.get_env(:nola, :sms, []), :number, "+33000000000")
   end
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def path() do
-    account = Keyword.get(Application.get_env(:lsg, :sms), :account)
+    account = Keyword.get(Application.get_env(:nola, :sms), :account)
     "https://eu.api.ovh.com/1.0/sms/#{account}"
   end
 
   def path(rest) do
     Path.join(path(), rest)
   end
 
   def send_sms(number, text) do
     url = path("/virtualNumbers/#{my_number()}/jobs")
     body = %{
       "message" => text,
       "receivers" => [number],
       #"senderForResponse" => true,
       #"noStopClause" => true,
       "charset" => "UTF-8",
       "coding" => "8bit"
     } |> Poison.encode!()
     headers = [{"content-type", "application/json"}] ++ sign("POST", url, body)
     options = []
     case HTTPoison.post(url, body, headers, options) do
       {:ok, %HTTPoison.Response{status_code: 200}} -> :ok
       {:ok, %HTTPoison.Response{status_code: code} = resp} ->
         Logger.error("SMS Error: #{inspect resp}")
         {:error, code}
       {:error, error} -> {:error, error}
     end
   end
 
   def init([]) do
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:sms", [plugin: __MODULE__])
     :ok = register_ovh_callback()
     {:ok, %{}}
     :ignore
   end
 
   def handle_info({:irc, :trigger, "sms", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: [nick | text]}}}, state) do
     with \
         {:tree, false} <- {:tree, m.sender.nick == "Tree"},
         {_, %IRC.Account{} = account} <- {:account, IRC.Account.find_always_by_nick(m.network, m.channel, nick)},
         {_, number} when not is_nil(number) <- {:number, IRC.Account.get_meta(account, "sms-number")}
     do
       text = Enum.join(text, " ")
       sender = if m.channel do
         "#{m.channel} <#{m.sender.nick}> "
       else
         "<#{m.sender.nick}> "
       end
       case send_sms(number, sender<>text) do
         :ok -> m.replyfun.("sent!")
         {:error, error} -> m.replyfun.("not sent, error: #{inspect error}")
       end
     else
       {:tree, _} -> m.replyfun.("Tree: va en enfer")
       {:account, _} -> m.replyfun.("#{nick} not known")
       {:number, _} -> m.replyfun.("#{nick} have not enabled sms")
     end
     {:noreply, state}
   end
 
   def handle_info(msg, state) do
     {:noreply, state}
   end
 
   defp register_ovh_callback() do
     url = path()
     body = %{
       "callBack" =>NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
       "smsResponse" => %{
         "cgiUrl" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback),
         "responseType" => "cgi"
       }
     } |> Poison.encode!()
     headers = [{"content-type", "application/json"}] ++ sign("PUT", url, body)
     options = []
     case HTTPoison.put(url, body, headers, options) do
       {:ok, %HTTPoison.Response{status_code: 200}} ->
         :ok
       error -> error
     end
   end
 
   defp sign(method, url, body) do
     ts = DateTime.utc_now() |> DateTime.to_unix()
     as = env(:app_secret)
     ck = env(:consumer_key)
     sign = Enum.join([as, ck, String.upcase(method), url, body, ts], "+")
     sign_hex = :crypto.hash(:sha, sign) |> Base.encode16(case: :lower)
     headers = [{"X-OVH-Application", env(:app_key)}, {"X-OVH-Timestamp", ts},
       {"X-OVH-Signature", "$1$"<>sign_hex}, {"X-Ovh-Consumer", ck}]
   end
 
   def parse_number(num) do
     {:error, :todo}
   end
 
   defp env() do
-    Application.get_env(:lsg, :sms)
+    Application.get_env(:nola, :sms)
   end
 
   defp env(key) do
     Keyword.get(env(), key)
   end
 end
diff --git a/lib/lsg_irc/txt_plugin.ex b/lib/lsg_irc/txt_plugin.ex
index d2bb627..cab912a 100644
--- a/lib/lsg_irc/txt_plugin.ex
+++ b/lib/lsg_irc/txt_plugin.ex
@@ -1,556 +1,556 @@
 defmodule Nola.IRC.TxtPlugin do
   alias IRC.UserTrack
   require Logger
 
   @moduledoc """
   # [txt]({{context_path}}/txt)
 
   * **.txt**: liste des fichiers et statistiques.
   Les fichiers avec une `*` sont vérrouillés.
   [Voir sur le web]({{context_path}}/txt).
 
   * **!txt**: lis aléatoirement une ligne dans tous les fichiers.
   * **!txt `<recherche>`**: recherche une ligne dans tous les fichiers.
 
   * **~txt**: essaie de générer une phrase (markov).
   * **~txt `<début>`**: essaie de générer une phrase commencant par `<debut>`.
 
   * **!`FICHIER`**: lis aléatoirement une ligne du fichier `FICHIER`.
   * **!`FICHIER` `<chiffre>`**: lis la ligne `<chiffre>` du fichier `FICHIER`.
   * **!`FICHIER` `<recherche>`**: recherche une ligne contenant `<recherche>` dans `FICHIER`.
 
   * **+txt `<file`>**: crée le fichier `<file>`.
   * **+`FICHIER` `<texte>`**: ajoute une ligne `<texte>` dans le fichier `FICHIER`.
   * **-`FICHIER` `<chiffre>`**: supprime la ligne `<chiffre>` du fichier `FICHIER`.
 
   * **-txtrw, +txtrw**. op seulement. active/désactive le mode lecture seule.
   * **+txtlock `<fichier>`, -txtlock `<fichier>`**. op seulement. active/désactive le verrouillage d'un fichier.
 
   Insérez `\\\\` pour faire un saut de ligne.
   """
 
   def short_irc_doc, do: "!txt https://sys.115ans.net/irc/txt "
   def irc_doc, do: @moduledoc
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   defstruct triggers: %{}, rw: true, locks: nil, markov_handler: nil, markov: nil
 
   def random(file) do
     GenServer.call(__MODULE__, {:random, file})
   end
 
   def reply_random(message, file) do
     if line = random(file) do
       line
       |> format_line(nil, message)
       |> message.replyfun.()
 
       line
     end
   end
 
   def init([]) do
     dets_locks_filename = (Nola.data_path() <> "/" <> "txtlocks.dets") |> String.to_charlist
     {:ok, locks} = :dets.open_file(dets_locks_filename, [])
-    markov_handler = Keyword.get(Application.get_env(:lsg, __MODULE__, []), :markov_handler, Nola.IRC.TxtPlugin.Markov.Native)
+    markov_handler = Keyword.get(Application.get_env(:nola, __MODULE__, []), :markov_handler, Nola.IRC.TxtPlugin.Markov.Native)
     {:ok, markov} = markov_handler.start_link()
     {:ok, _} = Registry.register(IRC.PubSub, "triggers", [plugin: __MODULE__])
     {:ok, %__MODULE__{locks: locks, markov_handler: markov_handler, markov: markov, triggers: load()}}
   end
 
   def handle_info({:received, "!reload", _, chan}, state) do
     {:noreply, %__MODULE__{state | triggers: load()}}
   end
 
   #
   # ADMIN: RW/RO
   #
 
   def handle_info({:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :plus}}}, state = %{rw: false}) do
     if channel && UserTrack.operator?(msg.network, channel, msg.sender.nick) do
       msg.replyfun.("txt: écriture réactivée")
       {:noreply, %__MODULE__{state | rw: true}}
     else
       {:noreply, state}
     end
   end
 
   def handle_info({:irc, :trigger, "txtrw", msg = %{channel: channel, trigger: %{type: :minus}}}, state = %{rw: true}) do
     if channel && UserTrack.operator?(msg.network, channel, msg.sender.nick) do
       msg.replyfun.("txt: écriture désactivée")
       {:noreply, %__MODULE__{state | rw: false}}
     else
       {:noreply, state}
     end
   end
 
   #
   # ADMIN: LOCKS
   #
 
   def handle_info({:irc, :trigger, "txtlock", msg = %{trigger: %{type: :plus, args: [trigger]}}}, state) do
     with \
       {trigger, _} <- clean_trigger(trigger),
       true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick)
     do
       :dets.insert(state.locks, {trigger})
       msg.replyfun.("txt: #{trigger} verrouillé")
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "txtlock", msg = %{trigger: %{type: :minus, args: [trigger]}}}, state) do
     with \
       {trigger, _} <- clean_trigger(trigger),
       true <- UserTrack.operator?(msg.network, msg.channel, msg.sender.nick),
       true <- :dets.member(state.locks, trigger)
     do
       :dets.delete(state.locks, trigger)
       msg.replyfun.("txt: #{trigger} déverrouillé")
     end
     {:noreply, state}
   end
 
   #
   # FILE LIST
   #
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :dot}}}, state) do
     map = Enum.map(state.triggers, fn({key, data}) ->
       ignore? = String.contains?(key, ".")
       locked? = case :dets.lookup(state.locks, key) do
         [{trigger}] -> "*"
         _ -> ""
       end
 
       unless ignore?, do: "#{key}: #{to_string(Enum.count(data))}#{locked?}"
     end)
     |> Enum.filter(& &1)
     total = Enum.reduce(state.triggers, 0, fn({_, data}, acc) ->
       acc + Enum.count(data)
     end)
     detail = Enum.join(map, ", ")
     total = ". total: #{Enum.count(state.triggers)} fichiers, #{to_string(total)} lignes. Détail: https://sys.115ans.net/irc/txt"
 
     ro = if !state.rw, do: " (lecture seule activée)", else: ""
 
     (detail<>total<>ro)
     |> msg.replyfun.()
     {:noreply, state}
   end
 
   #
   # GLOBAL: RANDOM
   #
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :bang, args: []}}}, state) do
     result = Enum.reduce(state.triggers, [], fn({trigger, data}, acc) ->
       Enum.reduce(data, acc, fn({l, _}, acc) ->
         [{trigger, l} | acc]
       end)
     end)
     |> Enum.shuffle()
 
     if !Enum.empty?(result) do
       {source, line} = Enum.random(result)
       msg.replyfun.(format_line(line, "#{source}: ", msg))
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :bang, args: args}}}, state) do
     grep = Enum.join(args, " ")
     |> String.downcase
     |> :unicode.characters_to_nfd_binary()
 
     result = with_stateful_results(msg, {:bang,"txt",msg.network,msg.channel,grep}, fn() ->
       Enum.reduce(state.triggers, [], fn({trigger, data}, acc) ->
         if !String.contains?(trigger, ".") do
           Enum.reduce(data, acc, fn({l, _}, acc) ->
             [{trigger, l} | acc]
           end)
         else
           acc
         end
       end)
       |> Enum.filter(fn({_, line}) ->
         line
         |> String.downcase()
         |> :unicode.characters_to_nfd_binary()
         |> String.contains?(grep)
       end)
       |> Enum.shuffle()
     end)
 
     if result do
       {source, line} = result
       msg.replyfun.(["#{source}: " | line])
     end
     {:noreply, state}
   end
 
   def with_stateful_results(msg, key, initfun) do
     me = self()
     scope = {msg.network, msg.channel || msg.sender.nick}
     key = {__MODULE__, me, scope, key}
     with_stateful_results(key, initfun)
   end
 
   def with_stateful_results(key, initfun) do
     pid = case :global.whereis_name(key) do
       :undefined ->
         start_stateful_results(key, initfun.())
       pid -> pid
     end
     if pid, do: wait_stateful_results(key, initfun, pid)
   end
 
   def start_stateful_results(key, []) do
     nil
   end
 
   def start_stateful_results(key, list) do
     me = self()
     {pid, _} = spawn_monitor(fn() ->
       Process.monitor(me)
       stateful_results(me, list)
     end)
     :yes = :global.register_name(key, pid)
     pid
   end
 
   def wait_stateful_results(key, initfun, pid) do
     send(pid, :get)
     receive do
       {:stateful_results, line} ->
         line
       {:DOWN, _ref, :process, ^pid, reason} ->
         with_stateful_results(key, initfun)
     after
       5000 ->
         nil
     end
   end
 
   defp stateful_results(owner, []) do
     send(owner, :empty)
     :ok
   end
 
   @stateful_results_expire :timer.minutes(30)
   defp stateful_results(owner, [line | rest] = acc) do
     receive do
       :get ->
         send(owner, {:stateful_results, line})
         stateful_results(owner, rest)
       {:DOWN, _ref, :process, ^owner, _} ->
         :ok
       after
         @stateful_results_expire -> :ok
     end
   end
 
   #
   # GLOBAL: MARKOV
   #
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :tilde, args: []}}}, state) do
     case state.markov_handler.sentence(state.markov) do
       {:ok, line} ->
         msg.replyfun.(line)
       error ->
         Logger.error "Txt Markov error: "<>inspect error
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :tilde, args: complete}}}, state) do
     complete = Enum.join(complete, " ")
     case state.markov_handler.complete_sentence(complete, state.markov) do
       {:ok, line} ->
         msg.replyfun.(line)
       error ->
         Logger.error "Txt Markov error: "<>inspect error
     end
     {:noreply, state}
   end
 
   #
   # TXT CREATE
   #
 
   def handle_info({:irc, :trigger, "txt", msg = %{trigger: %{type: :plus, args: [trigger]}}}, state) do
     with \
       {trigger, _} <- clean_trigger(trigger),
       true <- can_write?(state, msg, trigger),
       :ok <- create_file(trigger)
     do
       msg.replyfun.("#{trigger}.txt créé. Ajouter: `+#{trigger} …` ; Lire: `!#{trigger}`")
       {:noreply, %__MODULE__{state | triggers: load()}}
     else
       _ -> {:noreply, state}
     end
   end
 
   #
   # TXT: RANDOM
   #
 
   def handle_info({:irc, :trigger, trigger, m = %{trigger: %{type: :query, args: opts}}}, state) do
     {trigger, _} = clean_trigger(trigger)
     if Map.get(state.triggers, trigger) do
       url = if m.channel do
         NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :txt, m.network, NolaWeb.format_chan(m.channel), trigger)
       else
         NolaWeb.Router.Helpers.irc_url(NolaWeb.Endpoint, :txt, trigger)
       end
       m.replyfun.("-> #{url}")
     end
     {:noreply, state}
   end
 
   def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :bang, args: opts}}}, state) do
     {trigger, _} = clean_trigger(trigger)
     line = get_random(msg, state.triggers, trigger, String.trim(Enum.join(opts, " ")))
     if line do
       msg.replyfun.(format_line(line, nil, msg))
     end
     {:noreply, state}
   end
 
   #
   # TXT: ADD
   #
 
   def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :plus, args: content}}}, state) do
     with \
       true <- can_write?(state, msg, trigger),
       {:ok, idx} <- add(state.triggers, msg.text)
     do
       msg.replyfun.("#{msg.sender.nick}: ajouté à #{trigger}. (#{idx})")
       {:noreply, %__MODULE__{state | triggers: load()}}
     else
       {:error, {:jaro, string, idx}} ->
         msg.replyfun.("#{msg.sender.nick}: doublon #{trigger}##{idx}: #{string}")
       error ->
         Logger.debug("txt add failed: #{inspect error}")
         {:noreply, state}
     end
   end
 
   #
   # TXT: DELETE
   #
 
   def handle_info({:irc, :trigger, trigger, msg = %{trigger: %{type: :minus, args: [id]}}}, state) do
     with \
       true <- can_write?(state, msg, trigger),
       data <- Map.get(state.triggers, trigger),
       {id, ""} <- Integer.parse(id),
       {text, _id} <- Enum.find(data, fn({_, idx}) -> id-1 == idx end)
     do
       data = data |> Enum.into(Map.new)
       data = Map.delete(data, text)
       msg.replyfun.("#{msg.sender.nick}: #{trigger}.txt##{id} supprimée: #{text}")
       dump(trigger, data)
       {:noreply, %__MODULE__{state | triggers: load()}}
     else
       _ ->
         {:noreply, state}
     end
   end
 
   def handle_info(:reload_markov, state=%__MODULE__{triggers: triggers, markov: markov}) do
     state.markov_handler.reload(state.triggers, state.markov)
     {:noreply, state}
   end
 
   def handle_info(msg, state) do
     {:noreply, state}
   end
 
   def handle_call({:random, file}, _from, state) do
     random = get_random(nil, state.triggers, file, [])
     {:reply, random, state}
   end
 
   def terminate(_reason, state) do
     if state.locks do
       :dets.sync(state.locks)
       :dets.close(state.locks)
     end
     :ok
   end
 
   # Load/Reloads text files from disk
   defp load() do
     triggers = Path.wildcard(directory() <> "/*.txt")
     |> Enum.reduce(%{}, fn(path, m) ->
       file = Path.basename(path)
       key = String.replace(file, ".txt", "")
       data = directory() <> file
       |> File.read!
       |> String.split("\n")
       |> Enum.reject(fn(line) ->
         cond do
           line == "" -> true
           !line -> true
           true -> false
         end
       end)
       |> Enum.with_index
       Map.put(m, key, data)
     end)
     |> Enum.sort
     |> Enum.into(Map.new)
 
     send(self(), :reload_markov)
     triggers
   end
 
   defp dump(trigger, data) do
     data = data
     |> Enum.sort_by(fn({_, idx}) -> idx end)
     |> Enum.map(fn({text, _}) -> text end)
     |> Enum.join("\n")
     File.write!(directory() <> "/" <> trigger <> ".txt", data<>"\n", [])
   end
 
   defp get_random(msg, triggers, trigger, []) do
     if data = Map.get(triggers, trigger) do
       {data, _idx} = Enum.random(data)
       data
     else
       nil
     end
   end
 
   defp get_random(msg, triggers, trigger, opt) do
     arg = case Integer.parse(opt) do
       {pos, ""} -> {:index, pos}
       {_pos, _some_string} -> {:grep, opt}
       _error -> {:grep, opt}
     end
     get_with_param(msg, triggers, trigger, arg)
   end
 
   defp get_with_param(msg, triggers, trigger, {:index, pos}) do
     data = Map.get(triggers, trigger, %{})
     case Enum.find(data, fn({_, index}) -> index+1 == pos end) do
       {text, _} -> text
       _ -> nil
     end
   end
 
   defp get_with_param(msg, triggers, trigger, {:grep, query}) do
     out = with_stateful_results(msg, {:grep, trigger, query}, fn() ->
       data = Map.get(triggers, trigger, %{})
       regex = Regex.compile!("#{query}", "i")
       Enum.filter(data, fn({txt, _}) -> Regex.match?(regex, txt) end)
       |> Enum.map(fn({txt, _}) -> txt end)
       |> Enum.shuffle()
     end)
     if out, do: out
   end
 
   defp create_file(name) do
     File.touch!(directory() <> "/" <> name <> ".txt")
     :ok
   end
 
   defp add(triggers, trigger_and_content) do
     case String.split(trigger_and_content, " ", parts: 2) do
       [trigger, content] ->
         {trigger, _} = clean_trigger(trigger)
 
 
         if Map.has_key?(triggers, trigger) do
           jaro = Enum.find(triggers[trigger], fn({string, idx}) -> String.jaro_distance(content, string) > 0.9 end)
 
           if jaro do
             {string, idx} = jaro
             {:error, {:jaro, string, idx}}
           else
             File.write!(directory() <> "/" <> trigger <> ".txt", content<>"\n", [:append])
             idx = Enum.count(triggers[trigger])+1
             {:ok, idx}
           end
         else
           {:error, :notxt}
         end
       _ -> {:error, :badarg}
     end
   end
 
   # fixme: this is definitely the ugliest thing i've ever done
   defp clean_trigger(trigger) do
     [trigger | opts] = trigger
     |> String.strip
     |> String.split(" ", parts: 2)
 
     trigger = trigger
     |> String.downcase
     |> :unicode.characters_to_nfd_binary()
     |> String.replace(~r/[^a-z0-9._]/, "")
     |> String.trim(".")
     |> String.trim("_")
 
     {trigger, opts}
   end
 
   def format_line(line, prefix, msg) do
     prefix = unless(prefix, do: "", else: prefix)
     prefix <> line
     |> String.split("\\\\")
     |> Enum.map(fn(line) ->
       String.split(line, "\\\\\\\\")
     end)
     |> List.flatten()
     |> Enum.map(fn(line) ->
       String.trim(line)
       |> Tmpl.render(msg)
     end)
   end
 
   def directory() do
-    Application.get_env(:lsg, :data_path) <> "/irc.txt/"
+    Application.get_env(:nola, :data_path) <> "/irc.txt/"
   end
 
   defp can_write?(%{rw: rw?, locks: locks}, msg = %{channel: nil, sender: sender}, trigger) do
     admin? = IRC.admin?(sender)
     locked? = case :dets.lookup(locks, trigger) do
       [{trigger}] -> true
       _ -> false
     end
     unlocked? = if rw? == false, do: false, else: !locked?
 
     can? = unlocked? || admin?
 
     if !can? do
       reason = if !rw?, do: "lecture seule", else: "fichier vérrouillé"
       msg.replyfun.("#{sender.nick}: permission refusée (#{reason})")
     end
     can?
   end
 
   defp can_write?(state = %__MODULE__{rw: rw?, locks: locks}, msg = %{channel: channel, sender: sender}, trigger) do
     admin? = IRC.admin?(sender)
     operator? = IRC.UserTrack.operator?(msg.network, channel, sender.nick)
     locked? = case :dets.lookup(locks, trigger) do
       [{trigger}] -> true
       _ -> false
     end
     unlocked? = if rw? == false, do: false, else: !locked?
     can? = admin? || operator? || unlocked?
 
     if !can? do
       reason = if !rw?, do: "lecture seule", else: "fichier vérrouillé"
       msg.replyfun.("#{sender.nick}: permission refusée (#{reason})")
     end
     can?
   end
 
 end
diff --git a/lib/lsg_irc/txt_plugin/markov_py_markovify.ex b/lib/lsg_irc/txt_plugin/markov_py_markovify.ex
index cda7853..b610ea8 100644
--- a/lib/lsg_irc/txt_plugin/markov_py_markovify.ex
+++ b/lib/lsg_irc/txt_plugin/markov_py_markovify.ex
@@ -1,39 +1,39 @@
 defmodule Nola.IRC.TxtPlugin.MarkovPyMarkovify do
 
   def start_link() do
     {:ok, nil}
   end
 
   def reload(_data, _markov) do
     :ok
   end
 
   def sentence(_) do
     {:ok, run()}
   end
 
   def complete_sentence(sentence, _) do
     {:ok, run([sentence])}
   end
 
   defp run(args \\ []) do
     {binary, script} = script()
     args = [script, Path.expand(Nola.IRC.TxtPlugin.directory()) | args]
     IO.puts "Args #{inspect args}"
     case MuonTrap.cmd(binary, args) do
       {response, 0} -> response
       {response, code} -> "error #{code}: #{response}"
     end
   end
 
   defp script() do
-    default_script = to_string(:code.priv_dir(:lsg)) <> "/irc/txt/markovify.py"
-    env = Application.get_env(:lsg, Nola.IRC.TxtPlugin, [])
+    default_script = to_string(:code.priv_dir(:nola)) <> "/irc/txt/markovify.py"
+    env = Application.get_env(:nola, Nola.IRC.TxtPlugin, [])
     |> Keyword.get(:py_markovify, [])
 
     {Keyword.get(env, :python, "python3"), Keyword.get(env, :script, default_script)}
   end
 
 
 
 end
diff --git a/lib/lsg_irc/wolfram_alpha_plugin.ex b/lib/lsg_irc/wolfram_alpha_plugin.ex
index b553e63..6ee06f0 100644
--- a/lib/lsg_irc/wolfram_alpha_plugin.ex
+++ b/lib/lsg_irc/wolfram_alpha_plugin.ex
@@ -1,47 +1,47 @@
 defmodule Nola.IRC.WolframAlphaPlugin do
   use GenServer
   require Logger
 
   @moduledoc """
   # wolfram alpha
 
   * **`!wa <requĂŞte>`** lance `<requĂŞte>` sur WolframAlpha
   """
 
   def irc_doc, do: @moduledoc
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def init(_) do
     {:ok, _} = Registry.register(IRC.PubSub, "trigger:wa", [plugin: __MODULE__])
     {:ok, nil}
   end
 
   def handle_info({:irc, :trigger, _, m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: query}}}, state) do
     query = Enum.join(query, " ")
     params = %{
-      "appid" => Keyword.get(Application.get_env(:lsg, :wolframalpha, []), :app_id, "NO_APP_ID"),
+      "appid" => Keyword.get(Application.get_env(:nola, :wolframalpha, []), :app_id, "NO_APP_ID"),
       "units" => "metric",
       "i" => query
     }
     url = "https://www.wolframalpha.com/input/?i=" <> URI.encode(query)
     case HTTPoison.get("http://api.wolframalpha.com/v1/result", [], [params: params]) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         m.replyfun.(["#{query} -> #{body}", url])
       {:ok, %HTTPoison.Response{status_code: code, body: body}} ->
         error = case {code, body} do
           {501, b} -> "input invalide: #{body}"
           {code, error} -> "erreur #{code}: #{body || ""}"
         end
         m.replyfun.("wa: #{error}")
       {:error, %HTTPoison.Error{reason: reason}} ->
         m.replyfun.("wa: erreur http: #{to_string(reason)}")
       _ ->
         m.replyfun.("wa: erreur http")
     end
     {:noreply, state}
   end
 
 end
diff --git a/lib/lsg_irc/youtube_plugin.ex b/lib/lsg_irc/youtube_plugin.ex
index 3d2acfb..fb9bea2 100644
--- a/lib/lsg_irc/youtube_plugin.ex
+++ b/lib/lsg_irc/youtube_plugin.ex
@@ -1,104 +1,104 @@
 defmodule Nola.IRC.YouTubePlugin do
   require Logger
 
   @moduledoc """
   # youtube
 
   * **!yt `<recherche>`**, !youtube `<recherche>`: retourne le premier résultat de la `<recherche>` YouTube
   """
 
   defstruct client: nil
 
   def irc_doc, do: @moduledoc
 
   def start_link() do
     GenServer.start_link(__MODULE__, [], name: __MODULE__)
   end
 
   def init([]) do
     for t <- ["trigger:yt", "trigger:youtube"], do: {:ok, _} = Registry.register(IRC.PubSub, t, [plugin: __MODULE__])
     {:ok, %__MODULE__{}}
   end
 
   def handle_info({:irc, :trigger, _, message = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: args}}}, state) do
     irc_search(Enum.join(args, " "), message)
     {:noreply, state}
   end
 
   def handle_info(info, state) do
     {:noreply, state}
   end
 
   defp irc_search(query, message) do
     case search(query) do
       {:ok, %{"items" => [item | _]}} ->
         url = "https://youtube.com/watch?v=" <> item["id"]
         snippet = item["snippet"]
         duration = item["contentDetails"]["duration"] |> String.replace("PT", "") |> String.downcase
         date = snippet["publishedAt"]
                |> DateTime.from_iso8601()
                |> elem(1)
                |> Timex.format("{relative}", :relative)
                |> elem(1)
 
         info_line = "— #{duration} — uploaded by #{snippet["channelTitle"]} — #{date}"
              <> " — #{item["statistics"]["viewCount"]} views, #{item["statistics"]["likeCount"]} likes,"
              <> " #{item["statistics"]["dislikeCount"]} dislikes"
         message.replyfun.("#{snippet["title"]} — #{url}")
         message.replyfun.(info_line)
       {:error, error} ->
         message.replyfun.("Erreur YouTube: "<>error)
       _ ->
         nil
     end
   end
 
   defp search(query) do
     query = query
     |> String.strip
-    key = Application.get_env(:lsg, :youtube)[:api_key]
+    key = Application.get_env(:nola, :youtube)[:api_key]
     params = %{
       "key" => key,
       "maxResults" => 1,
       "part" => "id",
       "safeSearch" => "none",
       "type" => "video",
       "q" => query,
     }
     url = "https://www.googleapis.com/youtube/v3/search"
     case HTTPoison.get(url, [], params: params) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         {:ok, json} = Jason.decode(body)
         item = List.first(json["items"])
         if item do
           video_id = item["id"]["videoId"]
           params = %{
             "part" => "snippet,contentDetails,statistics",
             "id" => video_id,
             "key" => key
           }
           headers = []
           options = [params: params]
           case HTTPoison.get("https://www.googleapis.com/youtube/v3/videos", [], options) do
             {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
               Jason.decode(body)
             {:ok, %HTTPoison.Response{status_code: code, body: body}} ->
               Logger.error "YouTube HTTP #{code}: #{inspect body}"
               {:error, "http #{code}"}
             error ->
               Logger.error "YouTube http error: #{inspect error}"
               :error
           end
         else
           :error
         end
       {:ok, %HTTPoison.Response{status_code: code, body: body}} ->
         Logger.error "YouTube HTTP #{code}: #{inspect body}"
         {:error, "http #{code}"}
       error ->
         Logger.error "YouTube http error: #{inspect error}"
         :error
     end
   end
 
 end
diff --git a/lib/lsg_telegram/room.ex b/lib/lsg_telegram/room.ex
index 794cca3..ca8a437 100644
--- a/lib/lsg_telegram/room.ex
+++ b/lib/lsg_telegram/room.ex
@@ -1,188 +1,188 @@
 defmodule Nola.TelegramRoom do
   require Logger
   @behaviour Telegram.ChatBot
   alias Telegram.Api
 
   @couch "bot-telegram-rooms"
 
   def rooms(), do: rooms(:with_docs)
 
   @spec rooms(:with_docs | :ids) :: [Map.t | integer( )]
   def rooms(:with_docs) do
     case Couch.get(@couch, :all_docs, include_docs: true) do
       {:ok, %{"rows" => rows}} -> {:ok, for(%{"doc" => doc} <- rows, do: doc)}
       error = {:error, _} -> error
     end
   end
 
   def rooms(:ids) do
     case Couch.get(@couch, :all_docs) do
       {:ok, %{"rows" => rows}} -> {:ok, for(%{"id" => id} <- rows, do: id)}
       error = {:error, _} -> error
     end
   end
 
   def room(id, opts \\ []) do
     Couch.get(@couch, id, opts)
   end
 
   # TODO: Create couch
   def setup() do
     :ok
   end
 
   def after_start() do
     for id <- room(:ids), do: Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(Nola.Telegram, Integer.parse(id) |> elem(0))
   end
 
   @impl Telegram.ChatBot
   def init(id) when is_integer(id) and id < 0 do
-    token = Keyword.get(Application.get_env(:lsg, :telegram, []), :key)
+    token = Keyword.get(Application.get_env(:nola, :telegram, []), :key)
     {:ok, chat} = Api.request(token, "getChat", chat_id: id)
     Logger.metadata(transport: :telegram, id: id, telegram_room_id: id)
     tg_room = case room(id) do
       {:ok, tg_room = %{"network" => _net, "channel" => _chan}} -> tg_room
       {:error, :not_found} ->
         [net, chan] = String.split(chat["title"], "/", parts: 2)
         {net, chan} = case IRC.Connection.get_network(net, chan) do
           %IRC.Connection{} -> {net, chan}
           _ -> {nil, nil}
         end
         {:ok, _id, _rev} = Couch.post(@couch, %{"_id" => id, "network" => net, "channel" => nil})
         {:ok, tg_room} = room(id)
         tg_room
     end
     %{"network" => net, "channel" => chan} = tg_room
     Logger.info("Starting ChatBot for room #{id} \"#{chat["title"]}\" #{inspect tg_room}")
     irc_plumbed = if net && chan do
         {:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:messages", plugin: __MODULE__)
         {:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:triggers", plugin: __MODULE__)
         {:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:outputs", plugin: __MODULE__)
         true
     else
       Logger.warn("Did not found telegram match for #{id} \"#{chat["title"]}\"")
       false
     end
     {:ok, %{id: id, net: net, chan: chan, irc: irc_plumbed}}
   end
 
   def init(id) do
     Logger.error("telegram_room: bad id (not room id)", transport: :telegram, id: id, telegram_room_id: id)
     :ignoree
   end
 
   defp find_or_create_meta_account(from = %{"id" => user_id}, state) do
     if account = IRC.Account.find_meta_account("telegram-id", user_id) do
       account
     else
       first_name = Map.get(from, "first_name")
       last_name = Map.get(from, "last_name")
       name = [first_name, last_name]
       |> Enum.filter(& &1)
       |> Enum.join(" ")
 
       username = Map.get(from, "username", first_name)
 
       account = username
       |> IRC.Account.new_account()
       |> IRC.Account.update_account_name(name)
       |> IRC.Account.put_meta("telegram-id", user_id)
 
       Logger.info("telegram_room: created account #{account.id} for telegram user #{user_id}")
       account
     end
   end
 
   def handle_update(%{"message" => %{"from" => from = %{"id" => user_id}, "text" => text}}, _token, state) do
     account = find_or_create_meta_account(from, state)
     connection = IRC.Connection.get_network(state.net)
     IRC.send_message_as(account, state.net, state.chan, text, true)
     {:ok, state}
   end
 
   def handle_update(data = %{"message" => %{"from" => from = %{"id" => user_id}, "location" => %{"latitude" => lat, "longitude" => lon}}}, _token, state) do
     account = find_or_create_meta_account(from, state)
     connection = IRC.Connection.get_network(state.net)
     IRC.send_message_as(account, state.net, state.chan, "@ #{lat}, #{lon}", true)
     {:ok, state}
   end
 
   for type <- ~w(photo voice video document animation) do
     def handle_update(data = %{"message" => %{unquote(type) => _}}, token, state) do
       upload(unquote(type), data, token, state)
     end
   end
 
   def handle_update(update, token, state) do
     {:ok, state}
   end
 
   def handle_info({:irc, _, _, message}, state) do
     handle_info({:irc, nil, message}, state)
   end
 
   def handle_info({:irc, _, message = %IRC.Message{sender: %{nick: nick}, text: text}}, state) do
     if Map.get(message.meta, :from) == self() do
     else
       body = if Map.get(message.meta, :self), do: text, else: "<#{nick}> #{text}"
       Nola.Telegram.send_message(state.id, body)
     end
     {:ok, state}
   end
 
   def handle_info(info, state) do
     Logger.info("UNhandled #{inspect info}")
     {:ok, state}
   end
 
   defp upload(_type, %{"message" => m = %{"chat" => %{"id" => chat_id}, "from" => from = %{"id" => user_id}}}, token, state) do
     account = find_or_create_meta_account(from, state)
     if account do
       {content, type} = cond do
         m["photo"] -> {m["photo"], "photo"}
         m["voice"] -> {m["voice"], "voice message"}
         m["video"] -> {m["video"], "video"}
         m["document"] -> {m["document"], "file"}
         m["animation"] -> {m["animation"], "gif"}
       end
 
       file = if is_list(content) && Enum.count(content) > 1 do
         Enum.sort_by(content, fn(p) -> p["file_size"] end, &>=/2)
         |> List.first()
       else
         content
       end
 
       file_id = file["file_id"]
       file_unique_id = file["file_unique_id"]
       text = if(m["caption"], do: m["caption"] <> " ", else: "")
 
       spawn(fn() ->
         with \
           {:ok, file} <- Telegram.Api.request(token, "getFile", file_id: file_id),
           path = "https://api.telegram.org/file/bot#{token}/#{file["file_path"]}",
           {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
           <<smol_body::binary-size(20), _::binary>> = body,
           {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
-          bucket = Application.get_env(:lsg, :s3, []) |> Keyword.get(:bucket),
+          bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
           ext = Path.extname(file["file_path"]),
           s3path = "#{account.id}/#{file_unique_id}#{ext}",
           s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
           {:ok, _} <- ExAws.request(s3req)
         do
           path = NolaWeb.Router.Helpers.url(NolaWeb.Endpoint) <> "/files/#{s3path}"
           txt = "#{type}: #{text}#{path}"
           connection = IRC.Connection.get_network(state.net)
           IRC.send_message_as(account, state.net, state.chan, txt, true)
         else
           error ->
             Telegram.Api.request(token, "sendMessage", chat_id: chat_id, text: "File upload failed, sorry.")
             Logger.error("Failed upload from Telegram: #{inspect error}")
         end
       end)
 
       {:ok, state}
     end
   end
 
 end
diff --git a/lib/lsg_telegram/telegram.ex b/lib/lsg_telegram/telegram.ex
index ef5c3b8..1c6a9a9 100644
--- a/lib/lsg_telegram/telegram.ex
+++ b/lib/lsg_telegram/telegram.ex
@@ -1,233 +1,233 @@
 defmodule Nola.Telegram do
   require Logger
   @behaviour Telegram.ChatBot
 
   def my_path() do
     "https://t.me/beauttebot"
   end
 
   def send_message(id, text, md2 \\ false) do
     md = if md2, do: "MarkdownV2", else: "Markdown"
-    token = Keyword.get(Application.get_env(:lsg, :telegram, []), :key)
+    token = Keyword.get(Application.get_env(:nola, :telegram, []), :key)
     Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(Nola.Telegram, id)
     Telegram.Api.request(token, "sendMessage", chat_id: id, text: text, parse_mode: "Markdown")
   end
 
   @impl Telegram.ChatBot
   def init(chat_id) when chat_id < 0 do
     {:ok, state} = Nola.TelegramRoom.init(chat_id)
     {:ok, %{room_state: state}}
   end
   def init(chat_id) do
     Logger.info("Telegram session starting: #{chat_id}")
     account = IRC.Account.find_meta_account("telegram-id", chat_id)
     account_id = if account, do: account.id
     {:ok, %{account: account_id}}
   end
 
   @impl Telegram.ChatBot
   def handle_update(update, token, %{room_state: room_state}) do
     {:ok, room_state} = Nola.TelegramRoom.handle_update(update, token, room_state)
     {:ok, %{room_state: room_state}}
   end
 
   def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/start"<>_}}, _token, state) do
     text = "*Welcome to beautte!*\n\nQuery the bot on IRC and say \"enable-telegram\" to continue."
     send_message(m["chat"]["id"], text)
     {:ok, %{account: nil}}
   end
 
   def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/enable"<>_}}, _token, state) do
     key = case String.split(text, " ") do
       ["/enable", key | _] -> key
       _ -> "nil"
     end
 
     #Handled message "1247435154:AAGnSSCnySn0RuVxy_SUcDEoOX_rbF6vdq0" %{"message" =>
     #  %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"},
     #    "date" => 1591027272, "entities" =>
     #    [%{"length" => 7, "offset" => 0, "type" => "bot_command"}],
     #    "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
     #    "message_id" => 11, "text" => "/enable salope"}, "update_id" => 764148578}
     account = IRC.Account.find_meta_account("telegram-validation-code", String.downcase(key))
     text = if account do
       net = IRC.Account.get_meta(account, "telegram-validation-target")
       IRC.Account.put_meta(account, "telegram-id", m["chat"]["id"])
       IRC.Account.put_meta(account, "telegram-username", m["chat"]["username"])
       IRC.Account.put_meta(account, "telegram-username", m["chat"]["username"])
       IRC.Account.delete_meta(account, "telegram-validation-code")
       IRC.Account.delete_meta(account, "telegram-validation-target")
       IRC.Connection.broadcast_message(net, account, "Telegram #{m["chat"]["username"]} account added!")
       "Yay! Linked to account **#{account.name}**."
     else
       "Token invalid"
     end
     send_message(m["chat"]["id"], text)
     {:ok, %{account: account.id}}
   end
 
   #[debug] Unhandled update: %{"message" =>
   #  %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"},
   #    "date" => 1591096015,
   #    "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
   #    "message_id" => 29,
   #    "photo" => [
   #      %{"file_id" => "AgACAgQAAxkBAAMdXtYyz4RQqLcpOlR6xKK3w3NayHAAAnCzMRuL4bFSgl_cTXMl4m5G_T0kXQADAQADAgADbQADZVMBAAEaBA",
   #        "file_size" => 9544, "file_unique_id" => "AQADRv09JF0AA2VTAQAB", "height" => 95, "width" => 320},
   #      %{"file_id" => "AgACAgQAAxkBAAMdXtYyz4RQqLcpOlR6xKK3w3NayHAAAnCzMRuL4bFSgl_cTXMl4m5G_T0kXQADAQADAgADeAADZFMBAAEaBA",
   #        "file_size" => 21420, "file_unique_id" => "AQADRv09JF0AA2RTAQAB", "height" => 148, "width" => 501}]},
   #  "update_id" => 218161546}
 
   for type <- ~w(photo voice video document animation) do
     def handle_update(data = %{"message" => %{unquote(type) => _}}, token, state) do
       start_upload(unquote(type), data, token, state)
     end
   end
 
   #[debug] Unhandled update: %{"callback_query" =>
   #  %{
   #    "chat_instance" => "-7948978714441865930", "data" => "evolu.net/#dmz",
   #      "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"},
   #      "id" => "8913804780149600",
   #       "message" => %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"},
   #                    "date" => 1591098553, "from" => %{"first_name" => "devbeautte", "id" => 1293058838, "is_bot" => true, "username" => "devbeauttebot"},
   #                    "message_id" => 62,
   #                    "reply_markup" => %{"inline_keyboard" => [[%{"callback_data" => "random/#", "text" => "random/#"},
   #                          %{"callback_data" => "evolu.net/#dmz", "text" => "evolu.net/#dmz"}]]},
   #                    "text" => "Where should I send the file?"}
   #   }
   #  , "update_id" => 218161568}
 
   #def handle_update(t, %{"callback_query" => cb = %{"data" => "resend", "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}) do
   #end
 
   def handle_update(%{"callback_query" => cb = %{"data" => "start-upload:"<>target, "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}, t, state) do
     account = IRC.Account.find_meta_account("telegram-id", chat_id)
     if account do
       target = case String.split(target, "/") do
         ["everywhere"] -> IRC.Membership.of_account(account)
         [net, chan] -> [{net, chan}]
       end
         Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Processing...", reply_markup: %{})
 
       {content, type} = cond do
         op["photo"] -> {op["photo"], ""}
         op["voice"] -> {op["voice"], " a voice message"}
         op["video"] -> {op["video"], ""}
         op["document"] -> {op["document"], ""}
         op["animation"] -> {op["animation"], ""}
       end
 
       file = if is_list(content) && Enum.count(content) > 1 do
         Enum.sort_by(content, fn(p) -> p["file_size"] end, &>=/2)
         |> List.first()
       else
         content
       end
       file_id = file["file_id"]
       file_unique_id = file["file_unique_id"]
         text = if(op["caption"], do: ": "<> op["caption"] <> "", else: "")
         resend = %{"inline_keyboard" => [ [%{"text" => "re-share", "callback_data" => "resend"}] ]}
       spawn(fn() ->
         with \
           {:ok, file} <- Telegram.Api.request(t, "getFile", file_id: file_id),
           path = "https://api.telegram.org/file/bot#{t}/#{file["file_path"]}",
           {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
           <<smol_body::binary-size(20), _::binary>> = body,
           {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}),
-          bucket = Application.get_env(:lsg, :s3, []) |> Keyword.get(:bucket),
+          bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket),
           ext = Path.extname(file["file_path"]),
           s3path = "#{account.id}/#{file_unique_id}#{ext}",
           Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "*Uploading...*", reply_markup: %{}, parse_mode: "MarkdownV2"),
           s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
           {:ok, _} <- ExAws.request(s3req)
         do
           path = NolaWeb.Router.Helpers.url(NolaWeb.Endpoint) <> "/files/#{s3path}"
           sent = for {net, chan} <- target do
             txt = "sent#{type}#{text} #{path}"
             IRC.send_message_as(account, net, chan, txt)
             "#{net}/#{chan}"
           end
           if caption = op["caption"], do: as_irc_message(chat_id, caption, account)
           text = "Sent on " <> Enum.join(sent, ", ") <> " !"
           Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "_Sent!_", reply_markup: %{}, parse_mode: "MarkdownV2")
         else
           error ->
             Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Something failed.", reply_markup: %{}, parse_mode: "MarkdownV2")
             Logger.error("Failed upload from Telegram: #{inspect error}")
         end
       end)
     end
     {:ok, state}
   end
 
   def handle_update(%{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}, "text" => text}}, _, state) do
     account = IRC.Account.find_meta_account("telegram-id", id)
     if account do
       as_irc_message(id, text, account)
     end
     {:ok, state}
   end
 
   def handle_update(m, _, state) do
     Logger.debug("Unhandled update: #{inspect m}")
     {:ok, state}
   end
 
   @impl Telegram.ChatBot
   def handle_info(info, %{room_state: room_state}) do
     {:ok, room_state} = Nola.TelegramRoom.handle_info(info, room_state)
     {:ok, %{room_state: room_state}}
   end
 
   def handle_info(_info, state) do
     {:ok, state}
   end
 
   defp as_irc_message(id, text, account) do
     reply_fun = fn(text) -> send_message(id, text) end
     trigger_text = cond do
       String.starts_with?(text, "/") ->
         "/"<>text = text
         "!"<>text
       Enum.any?(IRC.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(text, trigger) end) ->
         text
       true ->
         "!"<>text
     end
       message = %IRC.Message{
         id: FlakeId.get(),
         transport: :telegram,
       network: "telegram",
       channel: nil,
       text: text,
       account: account,
       sender: %ExIRC.SenderInfo{nick: account.name},
       replyfun: reply_fun,
         trigger: IRC.Connection.extract_trigger(trigger_text),
         at: nil
     }
     IRC.Connection.publish(message, ["messages:private", "messages:telegram", "telegram/#{account.id}:messages"])
     message
   end
 
   defp start_upload(_type, %{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}}}, token, state) do
     account = IRC.Account.find_meta_account("telegram-id", id)
     if account do
       text = if(m["text"], do: m["text"], else: nil)
       targets = IRC.Membership.of_account(account)
       |> Enum.map(fn({net, chan}) -> "#{net}/#{chan}" end)
       |> Enum.map(fn(i) -> %{"text" => i, "callback_data" => "start-upload:#{i}"} end)
       kb = if Enum.count(targets) > 1 do
         [%{"text" => "everywhere", "callback_data" => "start-upload:everywhere"}] ++ targets
       else
         targets
       end
       |> Enum.chunk_every(2)
       keyboard = %{"inline_keyboard" => kb}
       Telegram.Api.request(token, "sendMessage", chat_id: id, text: "Where should I send this file?", reply_markup: keyboard, reply_to_message_id: m["message_id"], parse_mode: "MarkdownV2")
     end
     {:ok, state}
   end
 
 end
diff --git a/lib/lsg_web/controllers/irc_controller.ex b/lib/lsg_web/controllers/irc_controller.ex
index 90d9853..c617e78 100644
--- a/lib/lsg_web/controllers/irc_controller.ex
+++ b/lib/lsg_web/controllers/irc_controller.ex
@@ -1,101 +1,101 @@
 defmodule NolaWeb.IrcController do
   use NolaWeb, :controller
 
   plug NolaWeb.ContextPlug
 
   def index(conn, params) do
     network = Map.get(params, "network")
     channel = if c = Map.get(params, "chan"), do: NolaWeb.reformat_chan(c)
     commands = for mod <- Enum.uniq([IRC.Account.AccountPlugin] ++ IRC.Plugin.enabled()) do
       if is_atom(mod) do
         identifier = Module.split(mod) |> List.last |> String.replace("Plugin", "") |> Macro.underscore
         {identifier, mod.irc_doc()}
       end
     end
     |> Enum.filter(& &1)
     |> Enum.filter(fn({_, doc}) -> doc end)
     members = cond do
       network && channel -> Enum.map(IRC.UserTrack.channel(network, channel), fn(tuple) -> IRC.UserTrack.User.from_tuple(tuple) end)
       true ->
         IRC.Membership.of_account(conn.assigns.account)
     end
     render conn, "index.html", network: network, commands: commands, channel: channel, members: members
   end
 
   def txt(conn, %{"name" => name}) do
     if String.contains?(name, ".txt") do
       name = String.replace(name, ".txt", "")
       data = data()
       if Map.has_key?(data, name) do
         lines = Enum.join(data[name], "\n")
         text(conn, lines)
       else
         conn
         |> put_status(404)
         |> text("Not found")
       end
     else
       do_txt(conn, name)
     end
   end
   def txt(conn, _), do: do_txt(conn, nil)
 
 
   defp do_txt(conn, nil) do
     doc = Nola.IRC.TxtPlugin.irc_doc()
     data = data()
     main = Enum.filter(data, fn({trigger, _}) -> !String.contains?(trigger, ".") end) |> Enum.into(Map.new)
     system = Enum.filter(data, fn({trigger, _}) -> String.contains?(trigger, ".") end) |> Enum.into(Map.new)
     lines = Enum.reduce(main, 0, fn({_, lines}, acc) -> acc + Enum.count(lines) end)
     conn
     |> assign(:title, "txt")
     |> render("txts.html", data: main, doc: doc, files: Enum.count(main), lines: lines, system: system)
   end
 
   defp do_txt(conn, txt) do
     data = data()
     base_url = cond do
       conn.assigns[:chan] -> "/#{conn.assigns.network}/#{NolaWeb.format_chan(conn.assigns.chan)}"
       true -> "/-"
     end
     if lines = Map.get(data, txt) do
       lines = Enum.map(lines, fn(line) ->
         line
         |> String.split("\\\\")
         |> Enum.intersperse(Phoenix.HTML.Tag.tag(:br))
       end)
       conn
       |> assign(:breadcrumbs, [{"txt", "#{base_url}/txt"}])
       |> assign(:title, "#{txt}.txt")
       |> render("txt.html", name: txt, data: lines, doc: nil)
     else
       conn
       |> put_status(404)
       |> text("Not found")
     end
   end
 
   defp data() do
-    dir = Application.get_env(:lsg, :data_path) <> "/irc.txt/"
+    dir = Application.get_env(:nola, :data_path) <> "/irc.txt/"
     Path.wildcard(dir <> "/*.txt")
     |> Enum.reduce(%{}, fn(path, m) ->
       path = String.split(path, "/")
       file = List.last(path)
       key = String.replace(file, ".txt", "")
       data = dir <> file
       |> File.read!
       |> String.split("\n")
       |> Enum.reject(fn(line) ->
         cond do
           line == "" -> true
           !line -> true
           true -> false
         end
       end)
       Map.put(m, key, data)
     end)
     |> Enum.sort
     |> Enum.into(Map.new)
   end
 
 end
diff --git a/lib/lsg_web/controllers/open_id_controller.ex b/lib/lsg_web/controllers/open_id_controller.ex
index 94166eb..d3fef5d 100644
--- a/lib/lsg_web/controllers/open_id_controller.ex
+++ b/lib/lsg_web/controllers/open_id_controller.ex
@@ -1,64 +1,64 @@
 defmodule NolaWeb.OpenIdController do
   use NolaWeb, :controller
   plug NolaWeb.ContextPlug, restrict: :public
   require Logger
 
   def login(conn, _) do
     url = OAuth2.Client.authorize_url!(new_client(), scope: "openid", state: Base.url_encode64(:crypto.strong_rand_bytes(32), padding: false))
     redirect(conn, external: url)
   end
 
   def callback(conn, %{"error" => error_code, "error_description" => error}) do
     Logger.warn("OpenId error: #{error_code} #{error}")
     render(conn, "error.html", error: error)
   end
 
   def callback(conn, %{"code" => code, "state" => state}) do
     with \
          client = %{token: %OAuth2.AccessToken{access_token: json}} = OAuth2.Client.get_token!(new_client(), state: state, code: code),
          {:ok, %{"access_token" => token}} <- Jason.decode(json),
            client = %OAuth2.Client{client | token: %OAuth2.AccessToken{access_token: token}},
          {:ok, %OAuth2.Response{body: body}} <- OAuth2.Client.get(client, "/userinfo"),
          {:ok, %{"sub" => id, "preferred_username" => username}} <- Jason.decode(body)
     do
       if account = conn.assigns.account do
           if !IRC.Account.get_meta(account, "identity-id") do # XXX: And oidc id not linked yet
             IRC.Account.put_meta(account, "identity-id", id)
           end
           IRC.Account.put_meta(account, "identity-username", username)
           conn
       else
         conn
       end
 
       conn
       |> put_session(:oidc_id, id)
       |> put_flash(:info, "Logged in!")
       |> redirect(to: Routes.path(conn, "/"))
     else
       {:error, %OAuth2.Response{status_code: 401}} ->
         Logger.error("OpenID: Unauthorized token")
         render(conn, "error.html", error: "The token is invalid.")
       {:error, %OAuth2.Error{reason: reason}} ->
         Logger.error("Error: #{inspect reason}")
         render(conn, "error.html", error: reason)
     end
   end
 
   def callback(conn, _params) do
     render(conn, "error.html", error: "Unspecified error.")
   end
 
   defp new_client() do
-    config = Application.get_env(:lsg, :oidc)
+    config = Application.get_env(:nola, :oidc)
     OAuth2.Client.new([
       strategy: OAuth2.Strategy.AuthCode,
       client_id: config[:client_id],
       client_secret: config[:client_secret],
       site: config[:base_url],
       authorize_url: config[:authorize_url],
       token_url: config[:token_url],
       redirect_uri: Routes.open_id_url(NolaWeb.Endpoint, :callback)
     ])
   end
 end
diff --git a/lib/lsg_web/endpoint.ex b/lib/lsg_web/endpoint.ex
index d8bf962..b0cf9c5 100644
--- a/lib/lsg_web/endpoint.ex
+++ b/lib/lsg_web/endpoint.ex
@@ -1,62 +1,62 @@
 defmodule NolaWeb.Endpoint do
   use Sentry.PlugCapture
-  use Phoenix.Endpoint, otp_app: :lsg
+  use Phoenix.Endpoint, otp_app: :nola
 
   # Serve at "/" the static files from "priv/static" directory.
   #
   # You should set gzip to true if you are running phoenix.digest
   # when deploying your static files in production.
   plug Plug.Static,
-    at: "/", from: :lsg, gzip: false,
+    at: "/", from: :nola, gzip: false,
     only: ~w(assets css js fonts images favicon.ico robots.txt)
 
   # Code reloading can be explicitly enabled under the
   # :code_reloader configuration of your endpoint.
   if 42==43 && code_reloading? do
     socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
     plug Phoenix.LiveReloader
     plug Phoenix.CodeReloader
   end
 
   plug Plug.RequestId
   plug Plug.Logger
 
   plug Plug.Parsers,
     parsers: [:urlencoded, :multipart, :json],
     pass: ["*/*"],
     json_decoder: Jason
 
   plug Sentry.PlugContext
   plug Plug.MethodOverride
   plug Plug.Head
 
   @session_options [store: :cookie,
     key: "_lsg_key",
     signing_salt: "+p7K3wrj"]
 
 
   socket "/live", Phoenix.LiveView.Socket,
     websocket: [connect_info: [session: @session_options]]
 
   # The session will be stored in the cookie and signed,
   # this means its contents can be read but not tampered with.
   # Set :encryption_salt if you would also like to encrypt it.
   plug Plug.Session, @session_options
 
   plug NolaWeb.Router
 
   @doc """
   Callback invoked for dynamically configuring the endpoint.
 
   It receives the endpoint configuration and checks if
   configuration should be loaded from the system environment.
   """
   def init(_key, config) do
     if config[:load_from_system_env] do
       port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
       {:ok, Keyword.put(config, :http, [:inet6, port: port])}
     else
       {:ok, config}
     end
   end
 end
diff --git a/lib/lsg_web/gettext.ex b/lib/lsg_web/gettext.ex
index e9a46e9..a43cb0d 100644
--- a/lib/lsg_web/gettext.ex
+++ b/lib/lsg_web/gettext.ex
@@ -1,24 +1,24 @@
 defmodule NolaWeb.Gettext do
   @moduledoc """
   A module providing Internationalization with a gettext-based API.
 
   By using [Gettext](https://hexdocs.pm/gettext),
   your module gains a set of macros for translations, for example:
 
       import NolaWeb.Gettext
 
       # Simple translation
       gettext "Here is the string to translate"
 
       # Plural translation
       ngettext "Here is the string to translate",
                "Here are the strings to translate",
                3
 
       # Domain-based translation
       dgettext "errors", "Here is the error message to translate"
 
   See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
   """
-  use Gettext, otp_app: :lsg
+  use Gettext, otp_app: :nola
 end
diff --git a/lib/open_ai.ex b/lib/open_ai.ex
index 81f12f4..cc0de27 100644
--- a/lib/open_ai.ex
+++ b/lib/open_ai.ex
@@ -1,20 +1,20 @@
 defmodule OpenAi do
 
   def post(path, data, options \\ []) do
-    config = Application.get_env(:lsg, :openai, [])
+    config = Application.get_env(:nola, :openai, [])
     url = "https://api.openai.com#{path}"
     headers = [{"user-agent", "internal private experiment bot, href@random.sh"},
                {"content-type", "application/json"},
                {"authorization", "Bearer " <> Keyword.get(config, :key, "unset-api-key")}]
     options = options ++ [timeout: :timer.seconds(180), recv_timeout: :timer.seconds(180)]
     with {:ok, json} <- Poison.encode(data),
          {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.post(url, json, headers, options),
          {:ok, data} <- Poison.decode(body) do
       {:ok, data}
     else
       {:ok, %HTTPoison.Response{status_code: code}} -> {:error, Plug.Conn.Status.reason_atom(code)}
       {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason}
     end
   end
 
 end
diff --git a/lib/untappd.ex b/lib/untappd.ex
index d5ac904..7ed3e66 100644
--- a/lib/untappd.ex
+++ b/lib/untappd.ex
@@ -1,94 +1,94 @@
 defmodule Untappd do
 
   @env Mix.env
   @version Mix.Project.config[:version]
   require Logger
 
   def auth_url() do
     client_id = Keyword.get(env(), :client_id)
     url = NolaWeb.Router.Helpers.untappd_callback_url(NolaWeb.Endpoint, :callback)
     "https://untappd.com/oauth/authenticate/?client_id=#{client_id}&response_type=code&redirect_url=#{URI.encode(url)}"
   end
 
   def auth_callback(code) do
     client_id = Keyword.get(env(), :client_id)
     client_secret = Keyword.get(env(), :client_secret)
     url = NolaWeb.Router.Helpers.untappd_callback_url(NolaWeb.Endpoint, :callback)
     params = %{
       "client_id" => client_id,
       "client_secret" => client_secret,
       "response_type" => code,
       "redirect_url" => url,
       "code" => code
     }
     case HTTPoison.get("https://untappd.com/oauth/authorize", headers(), params: params) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         json = Poison.decode!(body)
         {:ok, get_in(json, ["response", "access_token"])}
       error ->
         Logger.error("Untappd auth callback failed: #{inspect error}")
         :error
     end
   end
 
   def maybe_checkin(account, beer_id) do
     if token = IRC.Account.get_meta(account, "untappd-token") do
       checkin(token, beer_id)
     else
       {:error, :no_token}
     end
   end
 
   def checkin(token, beer_id) do
     params = get_params(token: token)
              |> Map.put("timezone", "CEST")
              |> Map.put("bid", beer_id)
     form_params = params
                   |> Enum.into([])
     case HTTPoison.post("https://api.untappd.com/v4/checkin/add", {:form, form_params}, headers(), params: params) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         body = Jason.decode!(body)
                |> Map.get("response")
         {:ok, body}
       {:ok, resp = %HTTPoison.Response{status_code: code, body: body}} ->
         Logger.warn "Untappd checkin error: #{inspect resp}"
         {:error, {:http_error, code}}
       {:error, error} -> {:error, {:http_error, error}}
     end
   end
 
   def search_beer(query, params \\ []) do
     params = get_params(params)
              |> Map.put("q", query)
              |> Map.put("limit", 10)
              #|> Map.put("sort", "name")
     case HTTPoison.get("https://api.untappd.com/v4/search/beer", headers(), params: params) do
       {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
         {:ok, Jason.decode!(body)}
       error ->
         Logger.error("Untappd search error: #{inspect error}")
     end
   end
 
   def get_params(params) do
     auth = %{"client_id" => Keyword.get(env(), :client_id), "client_secret" => Keyword.get(env(), :client_secret)}
     if token = Keyword.get(params, :token) do
       Map.put(auth, "access_token", token)
     else
       auth
     end
   end
 
   def headers(extra \\ []) do
     client_id = Keyword.get(env(), :client_id)
     extra
     ++ [
       {"user-agent", "dmzbot (#{client_id}; #{@version}-#{@env})"}
     ]
   end
 
   def env() do
-    Application.get_env(:lsg, :untappd)
+    Application.get_env(:nola, :untappd)
   end
 
 end