initial commit
commit
f8bd3432e0
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{lib,test}/**/*.{ex,exs}"]
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
discordirc-*.tar
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# Discordirc
|
||||
|
||||
## Installation
|
||||
|
||||
you need to configure this with a config.exs
|
||||
```elixir
|
||||
import Config
|
||||
|
||||
config :discordirc,
|
||||
channels: [
|
||||
%{ircnetwork: "net1",
|
||||
ircchannel: "#mychannel",
|
||||
discordid: 123456789234}
|
||||
],
|
||||
networks: [
|
||||
%{
|
||||
network: "net1",
|
||||
server: "irc.example.net",
|
||||
pass: "",
|
||||
port: 6697,
|
||||
ssl?: true,
|
||||
nick: "discordirc",
|
||||
user: "discord",
|
||||
name: "Relay bot for my discord"
|
||||
}
|
||||
]
|
||||
|
||||
config :nostrum,
|
||||
# The token of your bot as a string
|
||||
token: "666",
|
||||
# The number of shards you want to run your bot under, or :auto.
|
||||
num_shards: :auto
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at [https://hexdocs.pm/discordirc](https://hexdocs.pm/discordirc).
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
defmodule Discordirc.ChannelMap do
|
||||
def discord(network, channel) do
|
||||
cmap = Application.fetch_env!(:discordirc, :channels)
|
||||
|
||||
id =
|
||||
cmap
|
||||
|> Enum.filter(&(&1.ircnetwork == network and &1.ircchannel == channel))
|
||||
|> List.first()
|
||||
|
||||
case id do
|
||||
x when is_map(x) ->
|
||||
{:ok, Map.get(x, :discordid)}
|
||||
|
||||
nil ->
|
||||
{:error, "no mapping"}
|
||||
end
|
||||
end
|
||||
|
||||
def irc(id) do
|
||||
cmap = Application.fetch_env!(:discordirc, :channels)
|
||||
|
||||
channel =
|
||||
cmap
|
||||
|> Enum.filter(&(&1.discordid == id))
|
||||
|> List.first()
|
||||
|
||||
case channel do
|
||||
x when is_map(x) ->
|
||||
{:ok, channel.ircnetwork, channel.ircchannel}
|
||||
|
||||
nil ->
|
||||
{:error, "no mapping"}
|
||||
end
|
||||
end
|
||||
|
||||
def getircchannels(network) do
|
||||
Application.fetch_env!(:discordirc, :channels)
|
||||
|> Enum.filter(&(&1.ircnetwork == network))
|
||||
|> Enum.map(& &1.ircchannel)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
defmodule Discordirc.DiscordHandler do
|
||||
use Nostrum.Consumer
|
||||
alias Nostrum.Api
|
||||
alias Discordirc.ChannelMap
|
||||
|
||||
def start_link do
|
||||
Consumer.start_link(__MODULE__)
|
||||
end
|
||||
|
||||
def handle_event({:MESSAGE_CREATE, msg, _ws_state}) do
|
||||
unless msg.author.username == "discord-irc" and msg.author.discriminator == "8465" do
|
||||
case ChannelMap.irc(msg.channel_id) do
|
||||
{:ok, net, _} ->
|
||||
pid = String.to_atom(net)
|
||||
send(pid, {:discordmsg, msg})
|
||||
|
||||
_ ->
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event(_event) do
|
||||
:noop
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
defmodule Discordirc do
|
||||
use Application
|
||||
|
||||
alias Discordirc.IRC
|
||||
|
||||
def start(_type, _args) do
|
||||
import Supervisor.Spec
|
||||
|
||||
ircnets =
|
||||
Application.get_env(:discordirc, :networks) |> Enum.map(fn net -> worker(IRC, [net]) end)
|
||||
|
||||
children =
|
||||
ircnets ++
|
||||
[
|
||||
Discordirc.DiscordHandler
|
||||
]
|
||||
|
||||
options = [strategy: :one_for_one, name: Discordirc.Supervisor]
|
||||
Supervisor.start_link(children, options)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
defmodule Discordirc.Formatter do
|
||||
def from_irc(nick, msg) do
|
||||
from_irc(nick, msg, false)
|
||||
end
|
||||
|
||||
def from_irc(nick, msg, ctcp) do
|
||||
# strip or replace IRC formatting.
|
||||
fmsg =
|
||||
msg
|
||||
|> :re.replace("\x02(.*?)\x02", "**\\g1**", [:global])
|
||||
|> :re.replace("\x02(.*)", "**\\g1**")
|
||||
|> :re.replace("\x01|\x03[0123456789]*(,[0123456789]*)?", "", [:global])
|
||||
|
||||
case ctcp do
|
||||
true ->
|
||||
case fmsg do
|
||||
x when is_binary(x) -> "\\* #{nick} _#{x}_"
|
||||
x when is_list(x) -> "\\* #{nick} _#{List.to_string(x)}_"
|
||||
end
|
||||
|
||||
false ->
|
||||
case fmsg do
|
||||
x when is_binary(x) -> "<#{nick}> #{x}"
|
||||
x when is_list(x) -> "<#{nick}> #{List.to_string(x)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def from_discord(user, msg) do
|
||||
usr = "#{user.username}\##{user.discriminator}"
|
||||
messages = String.split(msg, "\n")
|
||||
|
||||
# discord may give... many lines. split and format.
|
||||
case Enum.count(messages) do
|
||||
0 ->
|
||||
"<#{usr}> #{messages[0]}"
|
||||
|
||||
x ->
|
||||
messages
|
||||
|> Enum.map(fn m -> "<#{usr}> #{m}" end)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,127 @@
|
|||
defmodule Discordirc.IRC do
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
defmodule State do
|
||||
defstruct server: nil,
|
||||
ssl?: nil,
|
||||
port: nil,
|
||||
pass: nil,
|
||||
nick: nil,
|
||||
user: nil,
|
||||
name: nil,
|
||||
channels: nil,
|
||||
client: nil,
|
||||
network: nil
|
||||
|
||||
def from_params(params) when is_map(params) do
|
||||
Enum.reduce(params, %State{}, fn {k, v}, acc ->
|
||||
case Map.has_key?(acc, k) do
|
||||
true -> Map.put(acc, k, v)
|
||||
false -> acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
alias ExIRC.Client
|
||||
alias ExIRC.SenderInfo
|
||||
alias Discordirc.ChannelMap
|
||||
alias Discordirc.Formatter
|
||||
alias Nostrum.Api, as: DiscordAPI
|
||||
|
||||
def start_link(%{:network => network} = params) when is_map(params) do
|
||||
state = %State{State.from_params(params) | :channels => ChannelMap.getircchannels(network)}
|
||||
GenServer.start_link(__MODULE__, [state], name: String.to_atom(network))
|
||||
end
|
||||
|
||||
def init([state]) do
|
||||
{:ok, client} = ExIRC.start_link!()
|
||||
|
||||
Client.add_handler(client, self())
|
||||
|
||||
Logger.debug(
|
||||
"connecting #{
|
||||
if state.ssl? do
|
||||
"ssl"
|
||||
else
|
||||
"unsecured"
|
||||
end
|
||||
} on #{state.network} (#{state.server} #{state.port})"
|
||||
)
|
||||
|
||||
if state.ssl? do
|
||||
Client.connect_ssl!(client, state.server, state.port)
|
||||
else
|
||||
Client.connect!(client, state.server, state.port)
|
||||
end
|
||||
|
||||
{:ok, %State{state | :client => client}}
|
||||
end
|
||||
|
||||
def handle_info(:logged_in, state) do
|
||||
Logger.debug("Logged in to #{state.server}:#{state.port}")
|
||||
for c <- state.channels, do: Client.join(state.client, c)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:discordmsg, msg}, state) do
|
||||
channel = ChannelMap.irc(msg.channel_id)
|
||||
response = Formatter.from_discord(msg.author, msg.content)
|
||||
|
||||
case channel do
|
||||
{:ok, _, chan} ->
|
||||
case response do
|
||||
x when is_binary(x) ->
|
||||
ExIRC.Client.msg(state.client, :privmsg, chan, x)
|
||||
|
||||
x when is_list(x) ->
|
||||
for m <- x, do: ExIRC.Client.msg(state.client, :privmsg, chan, m)
|
||||
end
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:connected, server, port}, state) do
|
||||
Logger.debug("Connected to #{server}:#{port}")
|
||||
Logger.debug("Logging to #{server}:#{port} as #{state.nick}..")
|
||||
Client.logon(state.client, state.pass, state.nick, state.user, state.name)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:received, msg, %SenderInfo{:nick => nick}, channel}, state) do
|
||||
discordid = ChannelMap.discord(state.network, channel)
|
||||
fmsg = Formatter.from_irc(nick, msg, false)
|
||||
|
||||
case discordid do
|
||||
{:ok, x} ->
|
||||
DiscordAPI.create_message(x, fmsg)
|
||||
end
|
||||
|
||||
{: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)
|
||||
|
||||
case discordid do
|
||||
{:ok, x} ->
|
||||
DiscordAPI.create_message(x, fmsg)
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(_event, state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def terminate(_, state) do
|
||||
Logger.debug("Qutting..")
|
||||
Client.quit(state.client, "discordirc.ex")
|
||||
Client.stop!(state.client)
|
||||
:ok
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
defmodule Discordirc.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :discordirc,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.10",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
mod: {Discordirc, []},
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:nostrum, "~> 0.4"},
|
||||
{:exirc, "~> 1.1.0"}
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
%{
|
||||
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowlib": {:hex, :cowlib, "2.6.0", "8aa629f81a0fc189f261dc98a42243fa842625feea3c7ec56c48f4ccdb55490f", [:rebar3], [], "hexpm"},
|
||||
"exirc": {:hex, :exirc, "1.1.0", "212a86124a113d87752ce528d52f8e2e2dab3accada66e5330591b9de2c628f9", [:mix], [], "hexpm"},
|
||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
||||
"gun": {:hex, :gun, "1.3.2", "542064cbb9f613650b8a8100b3a927505f364fbe198b7a5a112868ff43f3e477", [:rebar3], [{:cowlib, "~> 2.6.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"httpoison": {:hex, :httpoison, "1.7.0", "abba7d086233c2d8574726227b6c2c4f6e53c4deae7fe5f6de531162ce9929a0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||
"nostrum": {:hex, :nostrum, "0.4.3", "393f22d9ede8f24aded2eaef9d9637c67512a6c6579c23a5c45a37e1466f7f1b", [:mix], [{:gen_stage, "~> 0.11", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.7", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm"},
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
defmodule DiscordircTest do
|
||||
use ExUnit.Case
|
||||
doctest Discordirc
|
||||
|
||||
test "greets the world" do
|
||||
assert Discordirc.hello() == :world
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Reference in New Issue