diff --git a/lib/irc/connection.ex b/lib/irc/connection.ex
index 7a0ca9d..62d52d8 100644
--- a/lib/irc/connection.ex
+++ b/lib/irc/connection.ex
@@ -1,521 +1,556 @@
 defmodule Nola.Irc.Connection do
   require Logger
   use Ecto.Schema
 
   @moduledoc """
   # IRC Connection
 
   Provides a nicer abstraction over ExIRC's handlers.
 
   ## Start connections
 
   ```
   Nola.Irc.Connection.start_link(host: "irc.random.sh", port: 6697, nick: "pouetbot", channels: ["#dev"])
 
   ## PubSub topics
 
   * `account` -- accounts change
     * {:account_change, old_account_id, new_account_id} # Sent when account merged
     * {:accounts, [{:account, network, channel, nick, account_id}] # Sent on bot join
     * {:account, network, nick, account_id} # Sent on user join
   * `message` -- aill messages (without triggers)
   * `message:private` -- all messages without a channel
   * `message:#CHANNEL` -- all messages within `#CHANNEL`
   * `triggers` -- all triggers
   * `trigger:TRIGGER` -- any message with a trigger `TRIGGER`
 
   ## Replying to %Nola.Message{}
 
   Each `Nola.Message` comes with a dedicated `replyfun`, to which you only have to pass either:
 
   """
   def irc_doc, do: nil
 
   @min_backoff :timer.seconds(5)
   @max_backoff :timer.seconds(2*60)
 
   embedded_schema do
     field :network, :string
     field :host, :string
     field :port, :integer
     field :nick, :string
     field :user, :string
     field :name, :string
     field :pass, :string
     field :tls, :boolean, default: false
     field :channels, {:array, :string}, default: []
   end
 
   defmodule Supervisor do
     use DynamicSupervisor
 
     def start_link() do
       DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__)
     end
 
     def start_child(%Nola.Irc.Connection{} = conn) do
       spec = %{id: conn.id, start: {Nola.Irc.Connection, :start_link, [conn]}, restart: :transient}
       DynamicSupervisor.start_child(__MODULE__, spec)
     end
 
     @impl true
     def init(_init_arg) do
       DynamicSupervisor.init(
         strategy: :one_for_one,
         max_restarts: 10,
         max_seconds: 1
       )
     end
   end
 
 
   def changeset(params) do
     import Ecto.Changeset
 
     %__MODULE__{id: EntropyString.large_id()}
     |> cast(params, [:network, :host, :port, :nick, :user, :name, :pass, :channels, :tls])
     |> validate_required([:host, :port, :nick, :user, :name])
     |> apply_action(:insert)
   end
 
   def to_tuple(%__MODULE__{} = conn) do
     {conn.id, conn.network, conn.host, conn.port, conn.nick, conn.user, conn.name, conn.pass, conn.tls, conn.channels, nil}
   end
 
   def from_tuple({id, network, host, port, nick, user, name, pass, tls, channels, _}) do
     %__MODULE__{id: id, network: network, host: host, port: port, nick: nick, user: user, name: name, pass: pass, tls: tls, channels: channels}
   end
 
   ## -- MANAGER API
 
   def setup() do
     :dets.open_file(dets(), [])
   end
 
   def dets(), do: to_charlist(Nola.data_path("/connections.dets"))
 
   def lookup(id) do
     case :dets.lookup(dets(), id) do
       [object | _] -> from_tuple(object)
       _ -> nil
     end
   end
 
   def connections() do
     :dets.foldl(fn(object, acc) -> [from_tuple(object) | acc] end, [], dets())
   end
 
   def start_all() do
     for conn <- connections(), do: {conn, Nola.Irc.Connection.Supervisor.start_child(conn)}
   end
 
   def get_network(network, channel \\ nil) do
     spec = [{{:_, :"$1", :_, :_, :_, :_, :_, :_, :_, :_, :_},
       [{:==, :"$1", {:const, network}}], [:"$_"]}]
     results = Enum.map(:dets.select(dets(), spec), fn(object) -> from_tuple(object) end)
     if channel do
       Enum.find(results, fn(conn) -> Enum.member?(conn.channels, channel) end)
     else
       List.first(results)
     end
   end
 
   def get_host_nick(host, port, nick) do
     spec = [{{:_, :_, :"$1", :"$2", :"$3", :_, :_, :_, :_, :_, :_},
       [{:andalso,
         {:andalso, {:==, :"$1", {:const, host}}, {:==, :"$2", {:const, port}}},
         {:==, :"$3", {:const, nick}}}],
       [:"$_"]}
     ]
     case :dets.select(dets(), spec) do
       [object] -> from_tuple(object)
       [] -> nil
     end
   end
 
   def delete_connection(%__MODULE__{id: id} = conn) do
     :dets.delete(dets(), id)
     stop_connection(conn)
     :ok
   end
 
   def start_connection(%__MODULE__{} = conn) do
     Nola.Irc.Connection.Supervisor.start_child(conn)
   end
 
   def stop_connection(%__MODULE__{id: id}) do
     case :global.whereis_name(id) do
       pid when is_pid(pid) ->
         GenServer.stop(pid, :normal)
       _ -> :error
     end
   end
 
   def add_connection(opts) do
     case changeset(opts) do
       {:ok, conn} ->
         if existing = get_host_nick(conn.host, conn.port, conn.nick) do
           {:error, {:existing, conn}}
         else
           :dets.insert(dets(), to_tuple(conn))
           Nola.Irc.Connection.Supervisor.start_child(conn)
         end
       error -> error
     end
   end
 
   def update_connection(connection) do
     :dets.insert(dets(), to_tuple(connection))
   end
 
   def start_link(conn) do
     GenServer.start_link(__MODULE__, [conn], name: {:global, conn.id})
   end
 
   def broadcast_message(net, chan, message) do
     dispatch("conn", {:broadcast, net, chan, message}, Nola.Irc.ConnectionPubSub)
   end
   def broadcast_message(list, message) when is_list(list) do
     for {net, chan} <- list do
       broadcast_message(net, chan, message)
     end
   end
 
   def privmsg(channel, line) do
     GenServer.cast(__MODULE__, {:privmsg, channel, line})
   end
 
   def init([conn]) do
     Logger.metadata(conn: conn.id)
     backoff = :backoff.init(@min_backoff, @max_backoff)
               |> :backoff.type(:jitter)
-    {:ok, %{client: nil, backoff: backoff, conn: conn, connected_server: nil, connected_port: nil, network: conn.network}, {:continue, :connect}}
+    {:ok, %{client: nil,
+    ignore_channel_for_a_while: conn.network == "snoonet",
+    backoff: backoff,
+    channels: Map.new,
+    conn: conn, connected_server: nil, connected_port: nil, network: conn.network}, {:continue, :connect}}
   end
 
   @triggers %{
     "!" => :bang,
     "+" => :plus,
     "-" => :minus,
     "?" => :query,
     "." => :dot,
     "~" => :tilde,
     "@" => :at,
     "++" => :plus_plus,
     "--" => :minus_minus,
     "!!" => :bang_bang,
     "??" => :query_query,
     ".." => :dot_dot,
     "~~" => :tilde_tilde,
     "@@" => :at_at
   }
 
   def handle_continue(:connect, state) do
     client_opts = []
                   |> Keyword.put(:network, state.conn.network)
     {:ok, _} = Registry.register(Nola.Irc.ConnectionPubSub, "conn", [])
     client = if state.client && Process.alive?(state.client) do
       Logger.info("Reconnecting client")
       state.client
     else
       Logger.info("Connecting")
       {:ok, client} = ExIRC.Client.start_link(debug: false)
       ExIRC.Client.add_handler(client, self())
       client
     end
 
     opts = [{:nodelay, true}]
     conn_fun = if state.conn.tls, do: :connect_ssl!, else: :connect!
     apply(ExIRC.Client, conn_fun, [client, to_charlist(state.conn.host), state.conn.port, opts])
 
     {:noreply, %{state | client: client}}
   end
 
   def handle_info(:disconnected, state) do
     {delay, backoff} = :backoff.fail(state.backoff)
     Logger.info("#{inspect(self())} Disconnected -- reconnecting in #{inspect delay}ms")
     Process.send_after(self(), :connect, delay)
-    {:noreply, %{state | backoff: backoff}}
+    {:noreply, %{state | channels: Map.new, backoff: backoff}}
   end
 
   def handle_info(:connect, state) do
     {:noreply, state, {:continue, :connect}}
   end
 
   def handle_cast({:privmsg, channel, line}, state) do
     irc_reply(state, {channel, nil}, line)
     {:noreply, state}
   end
-
+<<>>
   # Connection successful
   def handle_info({:connected, server, port}, state) do
     Logger.info("#{inspect(self())} Connected to #{inspect(server)}:#{port} #{inspect state}")
     {_, backoff} = :backoff.succeed(state.backoff)
     ExIRC.Client.logon(state.client, state.conn.pass || "", state.conn.nick, state.conn.user, state.conn.name)
-    {:noreply, %{state | backoff: backoff, connected_server: server, connected_port: port}}
+    {:noreply, %{state | channels: Map.new, backoff: backoff, connected_server: server, connected_port: port}}
   end
 
   # Logon successful
   def handle_info(:logged_in, state) do
     Logger.info("#{inspect(self())} Logged in")
     {_, backoff} = :backoff.succeed(state.backoff)
     Enum.map(state.conn.channels, &ExIRC.Client.join(state.client, &1))
     {:noreply, %{state | backoff: backoff}}
   end
 
   # ISUP
   def handle_info({:isup, network}, state) when is_binary(network) do
     Nola.UserTrack.clear_network(state.network)
     if network != state.network do
       Logger.warn("Possibly misconfigured network: #{network} != #{state.network}")
     end
     {:noreply, state}
   end
 
   # Been kicked
   def handle_info({:kicked, _sender, chan, _reason}, state) do
     ExIRC.Client.join(state.client, chan)
-    {:noreply, state}
+    {:noreply, %{state | channels: Map.delete(state.channels, chan)}}
   end
 
   # Received something in a channel
   def handle_info({:received, text, sender, chan}, state) do
-    user = if user = Nola.UserTrack.find_by_nick(state.network, sender.nick) do
-      user
-    else
-      Logger.error("Could not lookup user for message: #{inspect {state.network, chan, sender.nick}}")
-      user = Nola.UserTrack.joined(chan, sender, [])
-      ExIRC.Client.who(state.client, chan) # Rewho everything in case of need ? We shouldn't not know that user..
-      user
-    end
-    if !user do
-      ExIRC.Client.who(state.client, chan) # Rewho everything in case of need ? We shouldn't not know that user..
-      Logger.error("Could not lookup user nor create it for message: #{inspect {state.network, chan, sender.nick}}")
+    channel_state = Map.get(state.channels, chan, Map.new)
+    if state.ignore_channel_for_a_while && DateTime.diff(DateTime.utc_now(), Map.get(channel_state, :joined)) < 30 do
+      :ignore_channel
     else
-      if !Map.get(user.options, :puppet) do
-        reply_fun = fn(text) -> irc_reply(state, {chan, sender}, text) end
-        account = Nola.Account.lookup(sender)
-        message = %Nola.Message{id: FlakeId.get(), transport: :irc, at: NaiveDateTime.utc_now(), text: text, network: state.network,
-                               account: account, sender: sender, channel: chan, replyfun: reply_fun,
-                               trigger: extract_trigger(text)}
-        message = case Nola.UserTrack.messaged(message) do
-          :ok -> message
-          {:ok, message} -> message
+      user = if user = Nola.UserTrack.find_by_nick(state.network, sender.nick) do
+        user
+      else
+        Logger.error("Could not lookup user for message: #{inspect {state.network, chan, sender.nick}}")
+        user = Nola.UserTrack.joined(chan, sender, [])
+        ExIRC.Client.who(state.client, chan) # Rewho everything in case of need ? We shouldn't not know that user..
+        user
+      end
+      if !user do
+        ExIRC.Client.who(state.client, chan) # Rewho everything in case of need ? We shouldn't not know that user..
+        Logger.error("Could not lookup user nor create it for message: #{inspect {state.network, chan, sender.nick}}")
+      else
+        if !Map.get(user.options, :puppet) do
+          reply_fun = fn(text) -> irc_reply(state, {chan, sender}, text) end
+          account = Nola.Account.lookup(sender)
+          message = %Nola.Message{id: FlakeId.get(), transport: :irc, at: NaiveDateTime.utc_now(), text: text, network: state.network,
+                                account: account, sender: sender, channel: chan, replyfun: reply_fun,
+                                trigger: extract_trigger(text)}
+          message = case Nola.UserTrack.messaged(message) do
+            :ok -> message
+            {:ok, message} -> message
+          end
+          publish(message, ["#{message.network}/#{chan}:messages"])
         end
-        publish(message, ["#{message.network}/#{chan}:messages"])
       end
     end
     {:noreply, state}
   end
 
   # Received a private message
   def handle_info({:received, text, sender}, state) do
     reply_fun = fn(text) -> irc_reply(state, {sender.nick, sender}, text) end
     account = Nola.Account.lookup(sender)
     message = %Nola.Message{id: FlakeId.get(), transport: :irc, text: text, network: state.network, at: NaiveDateTime.utc_now(),
                            account: account, sender: sender, replyfun: reply_fun, trigger: extract_trigger(text)}
     message = case Nola.UserTrack.messaged(message) do
       :ok -> message
       {:ok, message} -> message
     end
     publish(message, ["messages:private", "#{message.network}/#{account.id}:messages"])
     {:noreply, state}
   end
 
   ## -- Broadcast
   def handle_info({:broadcast, net, account = %Nola.Account{}, message}, state) do
     if net == state.conn.network do
       user = Nola.UserTrack.find_by_account(net, account)
       if user do
         irc_reply(state, {user.nick, nil}, message)
       end
     end
     {:noreply, state}
   end
   def handle_info({:broadcast, net, chan, message}, state) do
     if net == state.conn.network && Enum.member?(state.conn.channels, chan) do
       irc_reply(state, {chan, nil}, message)
     end
     {:noreply, state}
   end
 
   ## -- UserTrack
 
   def handle_info({:joined, channel}, state) do
+    channels = Map.put(state.channels, channel, %{joined: DateTime.utc_now})
     ExIRC.Client.who(state.client, channel)
-    {:noreply, state}
+    {:noreply, %{state | channels: channels}}
   end
 
   def handle_info({:who, channel, whos}, state) do
     accounts = Enum.map(whos, fn(who = %ExIRC.Who{nick: nick, operator?: operator}) ->
       priv = if operator, do: [:operator], else: []
       # Don't touch -- on WHO the bot joined, not the users.
       Nola.UserTrack.joined(channel, who, priv, false)
       account = Nola.Account.lookup(who)
       if account do
         {:account, who.network, channel, who.nick, account.id}
       end
     end)
     |> Enum.filter(fn(x) -> x end)
     dispatch("account", {:accounts, accounts})
     {:noreply, state}
   end
 
   def handle_info({:quit, reason, sender}, state) do
     Nola.UserTrack.quitted(sender, reason)
     {:noreply, state}
   end
 
   def handle_info({:joined, channel, sender}, state) do
     Nola.UserTrack.joined(channel, sender, [])
     account = Nola.Account.lookup(sender)
     if account do
       dispatch("account", {:account, sender.network, channel, sender.nick, account.id})
     end
     {:noreply, state}
   end
 
   def handle_info({:kicked, nick, _by, channel, _reason}, state) do
     Nola.UserTrack.parted(state.network, channel, nick)
     {:noreply, state}
   end
 
   def handle_info({:parted, channel, %ExIRC.SenderInfo{nick: nick}}, state) do
     Nola.UserTrack.parted(state.network, channel, nick)
     {:noreply, state}
   end
 
   def handle_info({:mode, [channel, mode, nick]}, state) do
     track_mode(state.network, channel, nick, mode)
     {:noreply, state}
   end
 
   def handle_info({:nick_changed, old_nick, new_nick}, state) do
     Nola.UserTrack.renamed(state.network, old_nick, new_nick)
     {:noreply, state}
   end
 
   def handle_info(unhandled, client) do
     Logger.debug("unhandled: #{inspect unhandled}")
     {:noreply, client}
   end
 
   def publish(pub), do: publish(pub, [])
 
   def publish(m = %Nola.Message{trigger: nil}, keys) do
     dispatch(["messages"] ++ keys, {:irc, :text, m})
   end
 
   def publish(m = %Nola.Message{trigger: t = %Nola.Trigger{trigger: trigger}}, keys) do
     dispatch(["triggers", "#{m.network}/#{m.channel}:triggers", "trigger:"<>trigger], {:irc, :trigger, trigger, m})
   end
 
   def publish_event(net, event = %{type: _}) when is_binary(net) do
     event = event
     |> Map.put(:at, NaiveDateTime.utc_now())
     |> Map.put(:network, net)
     dispatch("#{net}:events", {:irc, :event, event})
   end
   def publish_event({net, chan}, event = %{type: type}) do
     event = event
     |> Map.put(:at, NaiveDateTime.utc_now())
     |> Map.put(:network, net)
     |> Map.put(:channel, chan)
     dispatch("#{net}/#{chan}:events", {:irc, :event, event})
   end
 
   def dispatch(keys, content, sub \\ Nola.PubSub)
 
   def dispatch(key, content, sub) when is_binary(key), do: dispatch([key], content, sub)
   def dispatch(keys, content, sub) when is_list(keys) do
-    Logger.debug("dispatch #{inspect keys} = #{inspect content}")
+    should_dispatch_data = should_dispatch_data(content)
     for key <- keys do
-      spawn(fn() -> Registry.dispatch(sub, key, fn h ->
-        for {pid, _} <- h, do: send(pid, content)
-      end) end)
+      spawn(fn() ->
+        Registry.dispatch(sub, key, fn h ->
+          for {pid, reg} <- h, do: if(should_dispatch?(key, should_dispatch_data, reg), do: send(pid, content))
+        end)
+      end)
+    end
+  end
+
+  defp should_dispatch_data({:irc, :trigger, _, msg}) do
+    should_dispatch_data(msg)
+  end
+  defp should_dispatch_data({:irc, :text, msg}) do
+    should_dispatch_data(msg)
+  end
+  defp should_dispatch_data(%Nola.Message{network: network, channel: channel}) do
+    Application.get_env(:nola, :channel_pubsub_plugin_ignore_list, %{})
+    |> Map.get("#{network}/#{channel}", [])
+  end
+  defp should_dispatch_data(_) do
+    []
+  end
+
+  def should_dispatch?(_, [], _), do: true
+  def should_dispatch?(_, data, reg) do
+    if plugin = Keyword.get(reg, :plugin) do
+      !Enum.member?(data, plugin)
+    else
+      true
     end
   end
 
   #
   # Triggers
   #
 
   def triggers, do: @triggers
 
   for {trigger, name} <- @triggers do
     def extract_trigger(unquote(trigger)<>text) do
       text = String.strip(text)
       [trigger | args] = String.split(text, " ")
       %Nola.Trigger{type: unquote(name), trigger: String.downcase(trigger), args: args}
     end
   end
 
   def extract_trigger(_), do: nil
 
   #
   # IRC Replies
   #
 
   # irc_reply(ExIRC.Client pid, {channel or nick, ExIRC.Sender}, binary | replies
   # replies :: {:kick, reason} | {:kick, nick, reason} | {:mode, mode, nick}
   defp irc_reply(state = %{client: client, network: network}, {target, _}, text) when is_binary(text) or is_list(text) do
     lines = Nola.Irc.Message.splitlong(text)
             |> Enum.map(fn(x) -> if(is_list(x), do: x, else: String.split(x, "\n")) end)
             |> List.flatten()
     outputs = for line <- lines do
       ExIRC.Client.msg(client, :privmsg, target, line)
       {:irc, :out, %Nola.Message{id: FlakeId.get(), transport: :irc, network: network,
               channel: target, text: line, sender: %ExIRC.SenderInfo{nick: state.conn.nick}, at: NaiveDateTime.utc_now(), meta: %{self: true}}}
     end
     for f <- outputs, do: dispatch(["irc:outputs", "#{network}/#{target}:outputs"], f)
   end
 
   defp irc_reply(%{client: client}, {target, %{nick: nick}}, {:kick, reason}) do
     ExIRC.Client.kick(client, target, nick, reason)
   end
 
   defp irc_reply(%{client: client}, {target, _}, {:kick, nick, reason}) do
     ExIRC.Client.kick(client, target, nick, reason)
   end
 
   defp irc_reply(%{client: client}, {target, %{nick: nick}}, {:mode, mode}) do
     ExIRC.Client.mode(%{client: client}, target, mode, nick)
   end
 
   defp irc_reply(%{client: client}, target, {:mode, mode, nick}) do
     ExIRC.Client.mode(client, target, mode, nick)
   end
 
   defp irc_reply(%{client: client}, target, {:channel_mode, mode}) do
     ExIRC.Client.mode(client, target, mode)
   end
 
   defp track_mode(network, channel, nick, "+o") do
     Nola.UserTrack.change_privileges(network, channel, nick, {[:operator], []})
     :ok
   end
 
   defp track_mode(network, channel, nick, "-o") do
     Nola.UserTrack.change_privileges(network, channel, nick, {[], [:operator]})
     :ok
   end
 
   defp track_mode(network, channel, nick, "+v") do
     Nola.UserTrack.change_privileges(network, channel, nick, {[:voice], []})
     :ok
   end
 
   defp track_mode(network, channel, nick, "-v") do
     Nola.UserTrack.change_privileges(network, channel, nick, {[], [:voice]})
     :ok
   end
 
   defp track_mode(network, channel, nick, mode) do
     Logger.warn("Unhandled track_mode: #{inspect {nick, mode}}")
     :ok
   end
 
   defp server(%{conn: %{host: host, port: port}}) do
     host <> ":" <> to_string(port)
   end
 
 end