Skip to content

Commit 654f41f

Browse files
committed
chore: inital commit
0 parents  commit 654f41f

17 files changed

+1120
-0
lines changed

.formatter.exs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]

.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where 3rd-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Ignore .fetch files in case you like to edit your project deps locally.
14+
/.fetch
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump
18+
19+
# Also ignore archive artifacts (built via "mix archive.build").
20+
*.ez
21+
22+
# Ignore package tarball (built via "mix hex.build").
23+
crux_gateway-*.tar
24+

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 SpaceEEC
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Crux.Gateway
2+
3+
Package providing a flexible gateway connection to the Discord API.
4+
5+
## Installation
6+
7+
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
8+
by adding `crux_gateway` to your list of dependencies in `mix.exs`:
9+
10+
```elixir
11+
def deps do
12+
[
13+
{:crux_gateway, "~> 0.1.0"}
14+
]
15+
end
16+
```
17+
18+
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
19+
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
20+
be found at [https://hexdocs.pm/crux_gateway](https://hexdocs.pm/crux_gateway).
21+

config/config.exs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use Mix.Config
2+
3+
# Everything may be set / overriden via `Crux.Gateway.start/1´.
4+
# See documentation of the `Crux.Gateway` module.
5+
# config :crux_gateway,
6+
# # required
7+
# token: "your token",
8+
# # required, fetch via /gateway/bot
9+
# shard_count: 5,
10+
# # required, fetch via /gateway(/bot)
11+
# url: "wss://gateway.discord.gg",
12+
# # optional
13+
# shards: [1, 2, 3..5]
14+
# # optional
15+
# dispatcher: GenStage.BroadcastDispatcher

lib/gateway.ex

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
defmodule Crux.Gateway do
2+
@moduledoc """
3+
Main entry point to start the Gateway connection.
4+
5+
Required for this to run are:
6+
- `:token` to identify with, you can get your bot's from [here](https://discordapp.com/developers/applications/me).
7+
> You want to keep that token secret at all times.
8+
9+
- `:url` to connect to. Probably something like `wss://gateway.discord.gg`.
10+
(Do not append query strings)
11+
> You usually want to GET the url via `/gateway/bot` along the recommended shard count.
12+
13+
- `:shard_count` you plan to run altogether.
14+
> Can and probably should be retrieved via `/gateway/bot`.
15+
16+
- Optionally `:shards`, which has to be a list of numbers and ranges.
17+
18+
Examples: `[1..3]` `[1, 2, 3]` `[1..3, 8, 9]`
19+
> If omitted all shards will be run.
20+
21+
- Optionally `:dispatcher`, which has to be a valid `GenStage.Dispatcher` or a tuple of one and initial state.
22+
> See `Crux.Gateway.Connection.Producer` for more info.
23+
"""
24+
25+
@typedoc """
26+
Used to specify or override gateway options when initially starting the connection.
27+
28+
See `start/1`
29+
"""
30+
@type gateway_options :: %{
31+
optional(:token) => String.t(),
32+
optional(:url) => String.t(),
33+
optional(:shard_count) => pos_integer(),
34+
optional(:shards) => [non_neg_integer | Range.t()],
35+
optional(:dispatcher) => GenStage.Dispatcher.t() | {GenStage.Dispatcher.t(), term}
36+
}
37+
38+
@doc """
39+
Initialises the connection(s) and actually starts the gateway.
40+
41+
You can specify or override `:token`, `:url`, `:shard_count` and `:shards` here via `t:gateway_options`.
42+
"""
43+
@spec start(args :: gateway_options) :: [Supervisor.on_start_child()]
44+
def start(args \\ %{}) do
45+
producer = Map.get(args, :dispatcher, GenStage.BroadcastDispatcher)
46+
Application.put_env(:crux_gateway, :dispatcher, producer)
47+
48+
shard_count = fetch_or_put_env(args, :shard_count, &is_number/1)
49+
50+
shards =
51+
case Application.fetch_env(:crux_gateway, :shards) do
52+
:error ->
53+
shards = Enum.to_list(0..(shard_count - 1))
54+
Application.put_env(:crux_gateway, :shards, shards)
55+
56+
shards
57+
58+
{:ok, shards} when is_list(shards) ->
59+
shards =
60+
shards
61+
|> Enum.flat_map(&map_shard/1)
62+
|> Enum.uniq()
63+
|> Enum.sort()
64+
65+
if Enum.min(shards) < 0 do
66+
raise """
67+
Specified shards are out of range.
68+
A negative shard id is not valid
69+
70+
:shards resolved to:
71+
#{inspect(shards)}
72+
"""
73+
end
74+
75+
if Enum.max(shards) >= shard_count do
76+
raise """
77+
Specified shards are out of range.
78+
Shard ids must be lower than shard_count
79+
80+
:shards resolved to:
81+
#{inspect(shards)}
82+
"""
83+
end
84+
85+
Application.put_env(:crux_gateway, :shards, shards)
86+
87+
shards
88+
89+
_ ->
90+
raise_shards()
91+
end
92+
93+
%{
94+
url: fetch_or_put_env(args, :url, &is_bitstring/1),
95+
token: fetch_or_put_env(args, :token, &is_bitstring/1),
96+
shard_count: shard_count
97+
}
98+
|> Crux.Gateway.Supervisor.start_gateway(shards)
99+
end
100+
101+
defp fetch_or_put_env(args, atom, validator) do
102+
value =
103+
case args do
104+
%{^atom => value} ->
105+
Application.put_env(:crux_gateway, atom, value)
106+
107+
value
108+
109+
_ ->
110+
Application.fetch_env!(:crux_gateway, atom)
111+
end
112+
113+
if validator.(value) do
114+
value
115+
else
116+
raise """
117+
:#{inspect(atom)} is not of the correct type.
118+
119+
Received:
120+
#{inspect(value)}
121+
"""
122+
end
123+
end
124+
125+
defp map_shard(num) when is_number(num), do: [num]
126+
defp map_shard(%Range{} = range), do: range
127+
128+
defp map_shard(other) do
129+
"""
130+
131+
Faulty element:
132+
#{inspect(other)}
133+
"""
134+
|> raise_shards()
135+
end
136+
137+
defp raise_shards(suffix \\ "") do
138+
raise """
139+
:shards must be a list of numbers and/or ranges
140+
141+
Received :shards value:
142+
#{inspect(Application.fetch_env!(:crux_gateway, :shards))}
143+
""" <> suffix
144+
end
145+
end

lib/gateway/application.ex

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule Crux.Gateway.Application do
2+
@moduledoc """
3+
Root for the supervision tree.
4+
"""
5+
use Application
6+
7+
@doc """
8+
Starts the Gateway Supervision tree.
9+
10+
This does _NOT_ actually connect to the gateway.
11+
See `Crux.Gateway.start/1`
12+
"""
13+
def start(_type, _args), do: Crux.Gateway.Supervisor.start_link()
14+
end

0 commit comments

Comments
 (0)