hedwig-im/hedwig

the robot does not seem to react to messages sent by himself

docteurklein opened this issue · 14 comments

I'm not exactly sure why, but I'd like this to implement the !! operator.

> gif me party hard
> http://giphy.net/funny-gif
> @alfred !!
> gif me party hard
> http://giphy.net/another-funny-gif

Currently, the bot sends the message, it is displayed, and the logs show it has arrived.
But the bot does not react. It seems to ignore himself.

Any ideas?

I'm not sure I understand what you're trying to do. Wouldn't that cause a loop?

it wouldn't.
I really only want the bot to re-interpret the last thing I messaged him.
So if I message him "!!", he should execute any responder that matches the last message I sent him.

There is an implementation of that in hubot, I just can't find the sources yet.

Shouldn't that just be a case of saving history in a separate process? Then '!!' could just query the latest history entry, and '!43' could re-apply entry #43, etc. It's probably better to feed it back to the bot internally, than take the indirect route over the chat room.

If the bot were to trigger on its own messages, it would indeed be very easy to cause a loop..

@jwarlander Agreed, and that's how I tried to implement it.

I have implemented a brain, (that is a simple KV store, named process).
I have a responder that hears any command (and store the last thing he hears in the brain).
I have another responder that listens to "!!" and answers with the last heard thing.

Strangely, it does not react to the last message (sent by himself)

I still didn't get it to work, but I'll post my attempts here.

Instead of making a round-trip and let the bot hear what he just said, I tried to directly ask him to interpret what was stored:

defmodule Edgar.Responder.NotNot do
  use Hedwig.Responder

  respond ~r/(.*)/i, msg do
    case msg.matches[1] do
      "!!" -> ""
      _ -> Edgar.Brain.memorize(:lastmsg, msg.matches[1])
    end
  end

  respond ~r/!!/i, msg do
    remembered_msg = %{ msg | text: Edgar.Brain.remember(:lastmsg) }
    pid = Hedwig.whereis("edgar")
    send msg, Edgar.Robot.send(pid, remembered_msg)
  end
end

getting an error:

16:58:20.481 [error] Task #PID<0.306.0> started from #PID<0.264.0> terminating
** (ArgumentError) argument error
    :erlang.send(:undefined, {:"$gen_cast", {:send, %Hedwig.Message{adapter: {Hedwig.Adapters.Console, #PID<0.265.0>}, matches: %{0 => "@edgar !!"}, private: %{}, ref: #Reference<0.0.3.3237>, robot: %Hedwig.Robot{adapter: #PID<0.265.0>, aka: "@edgar", name: "Edgar Bot", opts: [robot: Edgar.Robot, otp_app: :edgar, responders: [{Hedwig.Responders.Help, []}, {Hedwig.Responders.Panzy, []}, {Hedwig.Responders.GreatSuccess, []}, {Hedwig.Responders.ShipIt, []}, {Edgar.Responder.Giphy, []}, {Edgar.Responder.NotNot, []}, {Edgar.Responder.Weather, []}, {Edgar.Responder.ItIsSomething, []}, {Edgar.Responder.OhYou, []}, {Edgar.Responder.TIL, []}, {Edgar.Responder.Bookmark, []}], adapter: Hedwig.Adapters.Console], responders: []}, room: nil, text: "it's something", type: "chat", user: "florian"}}})
    (elixir) lib/gen_server.ex:639: GenServer.do_send/2
    (edgar) lib/edgar/responder/NotNot.ex:26: Edgar.Responder.NotNot."__!!__"/2
    (elixir) lib/task/supervised.ex:89: Task.Supervised.do_apply/2
    (elixir) lib/task/supervised.ex:40: Task.Supervised.reply/5
    (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<2.28577963/0 in Hedwig.Responder.run_aysnc/2>
    Args: []

Any idea how I should implement that?

And it looks like in your case that Hedwig.whereis("edgar") is returning :undefined. So it's not registered.

You should probably store the whole message in your brain process, instead of just the matches. Then you can run the whole message through Hedwig.Robot.handle_message(robot, msg)

Looks like the Console adapter doesn't register the bot...so that's why you're getting :undefined

Thanks! I will try your propositions.

Hedwig.Robot.handle_message(robot, msg) is casting, so I just get :ok as a return value.
How could I get what is sent back by the responder that matches msg ?

mmh, now I get a working test with this code, but not with the console adapter:

code

  respond ~r/!!/i, msg do
    pid = Hedwig.whereis("Edgar Bot")
    remembered = Edgar.Brain.remember(:lastmsg)
    send msg, Hedwig.Robot.handle_message(pid, %{
      remembered | robot: %{
        remembered.robot | adapter: msg.robot.adapter
      }
    })
  end

test (green)

  @tag start_robot: true, name: "edgar", responders: [{Edgar.Responder.NotNot, []}, {Hedwig.Responders.Help, []}]
  test "!! repeats last command", %{adapter: adapter, msg: msg} do
    send adapter, {:message, %{msg | text: "edgar help"}}
    send adapter, {:message, %{msg | text: "edgar !!"}}
    assert_receive {:message, %{text: text}}
    assert String.contains?(text, "help")
  end

console adapter error:

00:53:02.115 [error] Task #PID<0.266.0> started from #PID<0.265.0> terminating
** (ArgumentError) invalid ANSI sequence specification: :ok
    (elixir) lib/io/ansi.ex:167: IO.ANSI.format_sequence/1
    (elixir) lib/io/ansi.ex:218: IO.ANSI.do_format/5
    (hedwig) lib/hedwig/adapters/console.ex:109: Hedwig.Adapters.Console.Connection.print/1
    (hedwig) lib/hedwig/adapters/console.ex:87: Hedwig.Adapters.Console.Connection.loop/3
    (elixir) lib/task/supervised.ex:89: Task.Supervised.do_apply/2
    (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: &Hedwig.Adapters.Console.Connection.loop/3
    Args: [#PID<0.265.0>, "florian", "Edgar Bot"]

Not sure I'm on the good path.
Also, if you think this issue is getting nowhere, or should be discussed elsewhere, don't hesitate to tell me :)

PS: I've locally modified the console adapter to do like the test one, i.e registering itself :after_init, so that i don't get an :undefined pid.

The bot is now registered in the console adapter under the :name field in the config.

5f91e1d

Ok...so I"ve been able to get this all working...

Hedwig Console - press Ctrl+C to exit.

The console adapter is useful for quickly verifying how your
bot will respond based on the current installed responders

scrogson> alfred hey
alfred> sup?
scrogson> alfred !!
alfred> sup?
scrogson> 

Here's the NotNot responder:

defmodule Alfred.Responders.NotNot do
  use Hedwig.Responder

  respond ~r/(.*)/i, msg do
    case msg.matches[1] do
      "!!" -> ""
      _ -> Alfred.Brain.put(:lastmsg, msg)
    end
  end

  respond ~r/!!/i, _msg do
    msg = Alfred.Brain.get(:lastmsg)
    pid = Hedwig.whereis("alfred")
    Hedwig.Robot.handle_message(pid, msg)
  end
end

Awesome! many thanks. Closing now.