fix #4
This fix required extra work than i was expecting. I ended up moving the split functionality into its own module because it's more involved than i expected. And to enable testing of the code, for which i addeed tests.master
parent
dff5ec0bde
commit
98bb443e94
|
@ -4,6 +4,7 @@ defmodule Discordirc.IRC do
|
|||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
import Discordirc.ByteSplit
|
||||
|
||||
defmodule State do
|
||||
defstruct server: nil,
|
||||
|
@ -15,7 +16,8 @@ defmodule Discordirc.IRC do
|
|||
name: nil,
|
||||
channels: nil,
|
||||
client: nil,
|
||||
network: nil
|
||||
network: nil,
|
||||
me: nil
|
||||
|
||||
def from_params(params) when is_map(params) do
|
||||
Enum.reduce(params, %State{}, fn {k, v}, acc ->
|
||||
|
@ -29,6 +31,7 @@ defmodule Discordirc.IRC do
|
|||
|
||||
alias ExIRC.Client
|
||||
alias ExIRC.SenderInfo
|
||||
alias ExIRC.Whois
|
||||
alias Discordirc.ChannelMap
|
||||
alias Discordirc.Formatter
|
||||
alias Nostrum.Api, as: DiscordAPI
|
||||
|
@ -60,71 +63,36 @@ defmodule Discordirc.IRC do
|
|||
|
||||
def handle_info(:logged_in, state) do
|
||||
Logger.debug("Logged in to #{state.server}:#{state.port}")
|
||||
Client.whois(state.client, state.nick)
|
||||
for c <- state.channels, do: Client.join(state.client, c)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def ircsplit(str, pfxlen) do
|
||||
str
|
||||
|> String.split(" ")
|
||||
|> Enum.chunk_while(
|
||||
[],
|
||||
fn ele, acc ->
|
||||
if Enum.join(Enum.reverse([ele | acc]), " ") |> byte_size() > 512 - pfxlen do
|
||||
{:cont, Enum.reverse(acc), [ele]}
|
||||
else
|
||||
{:cont, [ele | acc]}
|
||||
end
|
||||
end,
|
||||
fn
|
||||
[] -> {:cont, []}
|
||||
acc -> {:cont, Enum.reverse(acc), []}
|
||||
end
|
||||
)
|
||||
|> Enum.map(fn x -> Enum.join(x, " ") end)
|
||||
|> Enum.map(fn x ->
|
||||
case byte_size(x) do
|
||||
n when is_integer(n) and n > 512 ->
|
||||
x
|
||||
|> String.to_charlist()
|
||||
|> Enum.chunk_every(512 - pfxlen)
|
||||
|> Enum.map(&List.to_string(&1))
|
||||
|
||||
_ ->
|
||||
x
|
||||
end
|
||||
end)
|
||||
|> List.flatten()
|
||||
|> Enum.filter(&(&1 !== ""))
|
||||
end
|
||||
|
||||
def discord_ircsplit(msg, nick, target) do
|
||||
pfx = "PRIVMSG #{target} :" |> byte_size()
|
||||
nkl = "<#{nick}> " |> byte_size()
|
||||
|
||||
msg
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&ircsplit(&1, pfx + nkl))
|
||||
|> List.flatten()
|
||||
end
|
||||
|
||||
def handle_info({:discordmsg, msg}, state) do
|
||||
channel = ChannelMap.irc(msg.channel_id)
|
||||
{usr, response} = Formatter.from_discord(msg)
|
||||
|
||||
case channel do
|
||||
{:ok, _, chan} ->
|
||||
pfx = ":#{state.me} PRIVMSG #{chan} :" |> byte_size()
|
||||
nkl = "<#{usr}> " |> byte_size()
|
||||
prefixlen = pfx + nkl
|
||||
# irc messages can only be 512b in length
|
||||
split_response =
|
||||
case response do
|
||||
x when is_binary(x) ->
|
||||
discord_ircsplit(x, usr, chan)
|
||||
|
||||
x when is_list(x) ->
|
||||
x
|
||||
|> Enum.map(&discord_ircsplit(&1, usr, chan))
|
||||
|> List.flatten()
|
||||
|
||||
x when is_binary(x) ->
|
||||
[x]
|
||||
end
|
||||
|> Enum.map(fn x ->
|
||||
x
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&ircsplit(&1, prefixlen))
|
||||
|> List.flatten()
|
||||
end)
|
||||
|> List.flatten()
|
||||
|
||||
case split_response do
|
||||
x when is_binary(x) ->
|
||||
|
@ -172,6 +140,24 @@ defmodule Discordirc.IRC do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:whois, whois = %Whois{:hostname => host}}, state) do
|
||||
case whois do
|
||||
%Whois{nick: n, user: user} when n == state.nick ->
|
||||
me = "#{state.nick}!#{user}@#{host}"
|
||||
Logger.debug("Setting host to #{me} #{inspect(whois)}")
|
||||
{:noreply, %State{state | :me => me}}
|
||||
|
||||
_ ->
|
||||
{:noreply, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info({:unrecognized, "396", %{args: args}}, state) do
|
||||
Logger.debug("Received UnrealIRCD host change notification, double checking host")
|
||||
Client.whois(state.client, state.nick)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:me, msg, %SenderInfo{:nick => nick}, channel}, state) do
|
||||
discordid = ChannelMap.discord(state.network, channel)
|
||||
fmsg = Formatter.from_irc(nick, msg, true)
|
||||
|
@ -203,7 +189,8 @@ defmodule Discordirc.IRC do
|
|||
# {:noreply, state}
|
||||
# end
|
||||
|
||||
def handle_info(_event, state) do
|
||||
def handle_info(event, state) do
|
||||
Logger.debug("unknown event: inspect output: " <> inspect(event))
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
defmodule Discordirc.ByteSplit do
|
||||
@moduledoc """
|
||||
Module that splits text by bytes, Unicode Aware.
|
||||
"""
|
||||
# use 510 to \r\n newline in mind
|
||||
@irclen 510
|
||||
|
||||
@doc """
|
||||
split a string into a number `bytes`, optionally subtracting a number of `hold` bytes for prefix/suffix
|
||||
"""
|
||||
def byte_split(str, bytes, hold \\ 0) do
|
||||
case byte_size(str) do
|
||||
n when is_integer(n) and n > bytes ->
|
||||
str
|
||||
|> String.split("")
|
||||
|> Enum.chunk_while(
|
||||
[],
|
||||
fn ele, acc ->
|
||||
if Enum.join(Enum.reverse([ele | acc])) |> byte_size() > bytes - hold do
|
||||
{:cont, Enum.reverse(acc), [ele]}
|
||||
else
|
||||
{:cont, [ele | acc]}
|
||||
end
|
||||
end,
|
||||
fn
|
||||
[] -> {:cont, []}
|
||||
acc -> {:cont, Enum.reverse(acc), []}
|
||||
end
|
||||
)
|
||||
|> Enum.map(&Enum.join(&1))
|
||||
|
||||
_ ->
|
||||
str
|
||||
end
|
||||
end
|
||||
|
||||
def ircsplit(str, pfxlen) do
|
||||
str
|
||||
|> String.split(" ")
|
||||
|> Enum.chunk_while(
|
||||
[],
|
||||
fn ele, acc ->
|
||||
if Enum.join(Enum.reverse([ele | acc]), " ") |> byte_size() > @irclen - pfxlen do
|
||||
{:cont, Enum.reverse(acc), [ele]}
|
||||
else
|
||||
{:cont, [ele | acc]}
|
||||
end
|
||||
end,
|
||||
fn
|
||||
[] -> {:cont, []}
|
||||
acc -> {:cont, Enum.reverse(acc), []}
|
||||
end
|
||||
)
|
||||
|> Enum.map(fn x -> Enum.join(x, " ") end)
|
||||
|> Enum.map(fn x ->
|
||||
case byte_size(x) do
|
||||
n when is_integer(n) and n > @irclen - pfxlen ->
|
||||
byte_split(x, @irclen - pfxlen)
|
||||
|
||||
_ ->
|
||||
x
|
||||
end
|
||||
end)
|
||||
|> List.flatten()
|
||||
|> Enum.filter(&(&1 !== ""))
|
||||
end
|
||||
end
|
|
@ -1,8 +1,4 @@
|
|||
defmodule DiscordircTest do
|
||||
use ExUnit.Case
|
||||
doctest Discordirc
|
||||
|
||||
test "greets the world" do
|
||||
assert Discordirc.hello() == :world
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
defmodule Discordirc.SplitterTest do
|
||||
use ExUnit.Case
|
||||
import Discordirc.ByteSplit
|
||||
doctest Discordirc.ByteSplit
|
||||
|
||||
test "split by bytes" do
|
||||
assert byte_split("test", 2) == ["te", "st"]
|
||||
end
|
||||
|
||||
test "split with emoji" do
|
||||
assert byte_split("test🦀", 4) == ["test", "🦀"]
|
||||
end
|
||||
|
||||
test "ircsplit without emoji" do
|
||||
lorem_ipsum =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam lorem nunc, vestibulum ac magna et, egestas accumsan felis. " <>
|
||||
"Morbi dolor quam, venenatis in molestie ullamcorper, fringilla nec tellus. Cras viverra purus ut ante iaculis consequat. " <>
|
||||
"Donec convallis id velit id vulputate. Nullam vel libero at sem consequat dapibus non in lectus. Nunc nec lectus aliquet, " <>
|
||||
"faucibus erat eget, feugiat justo. Duis imperdiet ligula at sem consectetur, porta semper massa sagittis. Duis sit amet risus sit amet nisi lectus."
|
||||
|
||||
irc_result = [
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam lorem nunc, vestibulum ac magna et, egestas accumsan felis. " <>
|
||||
"Morbi dolor quam, venenatis in molestie ullamcorper, fringilla nec tellus. Cras viverra purus ut ante iaculis consequat. " <>
|
||||
"Donec convallis id velit id vulputate. Nullam vel libero at sem consequat dapibus non in lectus. Nunc nec lectus aliquet, " <>
|
||||
"faucibus erat eget, feugiat justo. Duis imperdiet ligula at sem consectetur, porta semper massa sagittis. Duis sit amet risus",
|
||||
"sit amet nisi lectus."
|
||||
]
|
||||
|
||||
prefix = "PRIVMSG #test :"
|
||||
prefix_len = prefix |> byte_size()
|
||||
irc_after_split = ircsplit(lorem_ipsum, prefix_len)
|
||||
assert irc_after_split == irc_result
|
||||
|
||||
assert Enum.map(irc_after_split, &byte_size(prefix <> &1)) |> Enum.map(&(&1 <= 512)) == [
|
||||
true,
|
||||
true
|
||||
]
|
||||
end
|
||||
|
||||
test "ircsplit with emoji" do
|
||||
lorem_ipsum =
|
||||
"Lorem 📨🐰🐇🌀🕣👻 gravida enim suspendisse vel 💵 🔷🔃🌙🍦 blandit 🌝🎌🌈 quis 🐬🔫🐟 tincidunt odio quis a morbi ipsum, " <>
|
||||
"lectus at nunc, nunc 📖🍷 morbi amet velit mattis netus est id 👭🌛 mauris id massa massa lorem feugiat et 🌃🐥👇 📴 tellus. Purus " <>
|
||||
"pulvinar sed integer ipsum, porta 📕🏆 posuere nunc mauris, elit vitae volutpat lacinia nulla et pellentesque elit 💙💝🍍📗🐛🌰 🍑 " <>
|
||||
"hendrerit sit 💴📣💁 etiam 🐅🏈 📗 🌹 curabitur purus"
|
||||
|
||||
irc_result = [
|
||||
"Lorem 📨🐰🐇🌀🕣👻 gravida enim suspendisse vel 💵 🔷🔃🌙🍦 blandit 🌝🎌🌈 quis 🐬🔫🐟 tincidunt odio quis a morbi ipsum, " <>
|
||||
"lectus at nunc, nunc 📖🍷 morbi amet velit mattis netus est id 👭🌛 mauris id massa massa lorem feugiat et 🌃🐥👇 📴 tellus. Purus " <>
|
||||
"pulvinar sed integer ipsum, porta 📕🏆 posuere nunc mauris, elit vitae volutpat lacinia nulla et pellentesque elit 💙💝🍍📗🐛🌰 🍑 " <>
|
||||
"hendrerit sit 💴📣💁 etiam 🐅🏈",
|
||||
"📗 🌹 curabitur purus"
|
||||
]
|
||||
|
||||
prefix = "PRIVMSG #test :"
|
||||
prefix_len = prefix |> byte_size()
|
||||
irc_after_split = ircsplit(lorem_ipsum, prefix_len)
|
||||
assert irc_after_split == irc_result
|
||||
|
||||
assert Enum.map(irc_after_split, &byte_size(prefix <> &1)) |> Enum.map(&(&1 <= 512)) == [
|
||||
true,
|
||||
true
|
||||
]
|
||||
end
|
||||
|
||||
test "ircsplit only emoji" do
|
||||
crab = "🦀"
|
||||
crabs = for _ <- 1..129, into: "", do: crab
|
||||
|
||||
good_split = [
|
||||
for(_ <- 1..123, into: "", do: crab),
|
||||
for(_ <- 1..6, into: "", do: crab)
|
||||
]
|
||||
|
||||
prefix = "PRIVMSG #test :"
|
||||
prefix_len = prefix |> byte_size()
|
||||
|
||||
irc_split = ircsplit(crabs, prefix_len)
|
||||
assert irc_split == good_split
|
||||
assert Enum.map(irc_split, &byte_size(prefix <> &1)) |> Enum.map(&(&1 <= 512)) == [true, true]
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue