alco/chatty

Posting multi-line messages

Opened this issue · 8 comments

Hi! I am currently writing an IRC bot (located at Queertoo/Alher) based on Beamie, and for the needs of various plugins (especially for the Urban Dictionary), I need to transmit a message overt multiple lines.
What would be the best solution? Hacking in chatty's core or hacking at the plugin/hook level?

alco commented

Hi. Chatty needs to support multi-line messages, I'll work on that.

In the meantime, you can return a list of messages from a hook like so:

defmodule MyHook do
  def run(_sender, _message) do
    text = """
      My
      multiline
      message
      """
    text
    |> String.split("\n")
    |> Enum.map(&{:msg, &1})
  end
end

Ooooooh :D. You're lovely, thank you! ❤️
An interesting feature would be, in the case of a :reply, to hilight the sender in the first message and then to paste the rest of the message over multiple lines without highlighting them. :)

According to RFC 2812

IRC messages are always lines of characters terminated with a CR-LF
(Carriage Return - Line Feed) pair, and these messages SHALL NOT
exceed 512 characters in length, counting all characters including
the trailing CR-LF. Thus, there are 510 characters maximum allowed
for the command and its parameters.  There is no provision for
continuation of message lines

I think it may help =)

And here is my version :

  def run(_sender, msg) do
    case String.downcase(msg) do
      "!define " <> term ->
      get_def(term)
        |> String.split_at(449)
          |> Tuple.to_list
            |> Enum.map(fn(x) -> {:msg, x} end)

      _ -> nil
    end
  end

This code has however a major drawback. Because Only the first half of the message is guaranteed to be inferior to 450 characters, but not the other half.
I've been told to use a recursive function to split the string into multiple strings in a list, but I can't figure out how to do that (though it may be stupid as hell…).

And thanks to the support of @ephe-meral, a working version is now in production:

  def run(_sender, msg) do
    case String.downcase(msg) do
      "!define " <> term ->
        get_def(term) |> send_msg
      _ -> nil
    end
  end

  def get_def(term) do
    definition = Chatty.Plugin.UrbanDict.what_is(term)
    case definition do
      :nodef ->
        "Sorry, there are no definitions for \""<> term <>"\" :("
      _ -> 
        definition
    end
  end

  def send_msg(msg) do
    msg
    |> split_at_rec(449)
    |> Enum.map(fn(x) -> {:msg, x} end)
  end

  def split_at_rec(string, at) when at > 0, 
  do: split_at_rec(string, at, []) |> Enum.reverse

  def split_at_rec(string, at, acc) do
    case string |> String.split_at(at) do
      {"", ""} -> acc
      {a, ""}  -> [a | acc]
      {a, b}   -> split_at_rec(b, at, [a | acc])
    end
  end

We will be working on an improvement to avoid 'middle-of-a-word-splitting', and then I think it should be mergeable in the core of Chatty. :)

alco commented

I have the following preliminary plan:

  • provide built-in support for multi-line messages in hooks, sending each line as a separate IRC message. If a message has a recipient, only the first line will include the name.
  • implement splitting of IRC messages at spaces so that each message fits into the maximum message limit (will be adjustable, also with the ability to specify the absolute limit, beyond which the message will simply be truncated). This will be implemented for PRIVMSG and NOTICE messages at the IRC protocol level.
alco commented

Implemented multi-line messages in master. Now you don't need to do anything special in your hook implementation, the following should work as expected:

defmodule MyHook do
  def run(_, _) do
    {:reply, """
      Have
      a
      nice
      day
      """}
  end
end

Thanks :)
However, the patch doesn't seem to support long messages splitting (which is what @ephe-meral and I intendend to support in Queertoo/Alher an explained in #5 (comment). I think you might be interested by UrbanHook which brings an interesting way to support 'more-than-450-chars-messages'.