diff options
| author | Nathan Perry <np@nathanperry.dev> | 2020-01-28 16:06:48 -0500 |
|---|---|---|
| committer | Nathan Perry <np@nathanperry.dev> | 2020-01-28 16:06:48 -0500 |
| commit | 5dadc47eb07be076a8b270458badb9d8cd7dfead (patch) | |
| tree | 69918ab33ded1e98905ebdf99c6f88f373a7a396 | |
| parent | dde5eb52706a9ca8ee257a6cbd75e243582d0a86 (diff) | |
start elixir conversion
32 files changed, 473 insertions, 0 deletions
diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..6915976 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +[ + inputs: ["mix.exs", "config/*.exs"], + subdirectories: ["apps/*"] +] @@ -5,3 +5,29 @@ *.log user_id_mapping.json restrict.json +config.toml + +# 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"). +thulani_bot-*.tar + diff --git a/.thulani_root b/.thulani_root new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/.thulani_root diff --git a/apps/thulani_bot/.formatter.exs b/apps/thulani_bot/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/apps/thulani_bot/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/apps/thulani_bot/.gitignore b/apps/thulani_bot/.gitignore new file mode 100644 index 0000000..302c179 --- /dev/null +++ b/apps/thulani_bot/.gitignore @@ -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"). +thulani_bot-*.tar + diff --git a/apps/thulani_bot/README.md b/apps/thulani_bot/README.md new file mode 100644 index 0000000..77f6232 --- /dev/null +++ b/apps/thulani_bot/README.md @@ -0,0 +1,2 @@ +# Thulani.Bot +The actual bot running in discord. diff --git a/apps/thulani_bot/lib/thulani/bot/application.ex b/apps/thulani_bot/lib/thulani/bot/application.ex new file mode 100644 index 0000000..63bd89e --- /dev/null +++ b/apps/thulani_bot/lib/thulani/bot/application.ex @@ -0,0 +1,13 @@ +defmodule Thulani.Bot.Application do + alias Thulani.Bot.Config + use Application + + def start(_type, _args) do + Config.init!() + + children = [] + + opts = [strategy: :one_for_one, name: Thulani.Bot.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/apps/thulani_bot/lib/thulani/bot/config.ex b/apps/thulani_bot/lib/thulani/bot/config.ex new file mode 100644 index 0000000..5542752 --- /dev/null +++ b/apps/thulani_bot/lib/thulani/bot/config.ex @@ -0,0 +1,58 @@ +defmodule Thulani.Bot.Config do + @env_vars %{ + database_url: nil, + spreadsheet_id: nil, + sheets_api_key: nil, + steam_api_key: nil, + max_sheet_column: "zz", + default_hist: "5", + max_hist: "30" + } + + require Logger + + def init!() do + load_env() + |> Enum.each(fn {application, vals} -> + Enum.each(vals, fn {key, val} -> Application.put_env(application, key, val) end) + end) + + if System.get_env("THULANI_DEBUG") do + Application.put_env(:logger, :level, :debug) + end + end + + def load_env() do + if Application.get_env(:thulani, :env) == :dev, do: load_dotenv() + + %{ + nostrum: [ + token: System.fetch_env!("THULANI_TOKEN"), + shards: System.get_env("THULANI_DISCORD_SHARDS", "1") |> Integer.parse() + ], + thulani: thulani_env() + } + end + + defp thulani_env do + @env_vars + |> Enum.map(fn {env_var, default} -> + canonical_env_var = + env_var + |> to_string + |> String.upcase() + |> (fn x -> "THULANI_" <> x end).() + + value = + canonical_env_var + |> System.get_env(default) + + if value == nil do + raise "required environment variable not found: #{canonical_env_var}" + end + + {env_var, value} + end) + end + +end diff --git a/apps/thulani_bot/lib/thulani/bot/consumer.ex b/apps/thulani_bot/lib/thulani/bot/consumer.ex new file mode 100644 index 0000000..f5deec6 --- /dev/null +++ b/apps/thulani_bot/lib/thulani/bot/consumer.ex @@ -0,0 +1,23 @@ +defmodule Thulani.Bot.Consumer do + use Nostrum.Consumer + + alias Nostrum.Api + require Logger + + def start_link do + Consumer.start_link(__MODULE__) + end + + def handle_event({:MESSAGE_CREATE, {msg}, ws_state}, state) do + case msg.content do + "!thulani " <> command -> Logger.debug("got command", command: command) + _ -> :ignore + end + + {:ok, state} + end + + def handle_event(_, state) do + {:ok, state} + end +end diff --git a/apps/thulani_bot/mix.exs b/apps/thulani_bot/mix.exs new file mode 100644 index 0000000..13afa4f --- /dev/null +++ b/apps/thulani_bot/mix.exs @@ -0,0 +1,32 @@ +defmodule Thulani.Bot.MixProject do + use Mix.Project + + def project do + [ + app: :thulani_bot, + version: "0.1.0", + build_path: "../../_build", + config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.9", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger], + mod: {Thulani.Bot.Application, []} + ] + end + + defp deps do + [ + # we look up the token based on an environment variable, so we can't do this + {:nostrum, "~> 0.4"}, + {:util, in_umbrella: true} + ] + end +end diff --git a/apps/thulani_bot/test/test_helper.exs b/apps/thulani_bot/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/apps/thulani_bot/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/apps/thulani_bot/test/thulani/bot/application_test.exs b/apps/thulani_bot/test/thulani/bot/application_test.exs new file mode 100644 index 0000000..2cc18fb --- /dev/null +++ b/apps/thulani_bot/test/thulani/bot/application_test.exs @@ -0,0 +1,4 @@ +defmodule Thulani.Bot.ApplicationTest do + use ExUnit.Case + doctest Thulani.Bot.Application +end diff --git a/apps/util/.formatter.exs b/apps/util/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/apps/util/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/apps/util/.gitignore b/apps/util/.gitignore new file mode 100644 index 0000000..d8dbee4 --- /dev/null +++ b/apps/util/.gitignore @@ -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"). +util-*.tar + diff --git a/apps/util/README.md b/apps/util/README.md new file mode 100644 index 0000000..4b69d4c --- /dev/null +++ b/apps/util/README.md @@ -0,0 +1,21 @@ +# Thulani.Util + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `util` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:util, "~> 0.1.0"} + ] +end +``` + +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/util](https://hexdocs.pm/util). + diff --git a/apps/util/lib/thulani/util.ex b/apps/util/lib/thulani/util.ex new file mode 100644 index 0000000..fad5e39 --- /dev/null +++ b/apps/util/lib/thulani/util.ex @@ -0,0 +1,2 @@ +defmodule Thulani.Util do +end diff --git a/apps/util/lib/thulani/util/compose.ex b/apps/util/lib/thulani/util/compose.ex new file mode 100644 index 0000000..cd4435e --- /dev/null +++ b/apps/util/lib/thulani/util/compose.ex @@ -0,0 +1,13 @@ +defmodule Thulani.Util.Compose do + import Thulani.Util.Curry + + def f <|> g, do: compose(f, g) + + def compose(f, g) when is_function(g) do + fn arg -> compose(curry(f), curry(g).(arg)) end + end + + def compose(f, arg) do + f.(arg) + end +end diff --git a/apps/util/lib/thulani/util/curry.ex b/apps/util/lib/thulani/util/curry.ex new file mode 100644 index 0000000..b03afab --- /dev/null +++ b/apps/util/lib/thulani/util/curry.ex @@ -0,0 +1,16 @@ +defmodule Thulani.Util.Curry do + @moduledoc false + + def curry(f) do + {_, arity} = :erlang.fun_info(f, :arity) + curry(f, arity, []) + end + + defp curry(f, 0, args) do + apply(f, Enum.reverse(args)) + end + + defp curry(f, arity, args) do + fn arg -> curry(f, arity - 1, [arg | args]) end + end +end diff --git a/apps/util/mix.exs b/apps/util/mix.exs new file mode 100644 index 0000000..a0caff9 --- /dev/null +++ b/apps/util/mix.exs @@ -0,0 +1,27 @@ +defmodule Thulani.Util.MixProject do + use Mix.Project + + def project do + [ + app: :util, + version: "0.1.0", + build_path: "../../_build", + config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.9", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger] + ] + end + + defp deps do + [] + end +end diff --git a/apps/util/test/test_helper.exs b/apps/util/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/apps/util/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/apps/util/test/thulani/util_test.exs b/apps/util/test/thulani/util_test.exs new file mode 100644 index 0000000..9246f40 --- /dev/null +++ b/apps/util/test/thulani/util_test.exs @@ -0,0 +1,4 @@ +defmodule Thulani.UtilTest do + use ExUnit.Case + doctest Thulani.Util +end diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..3ff5459 --- /dev/null +++ b/config.example.toml @@ -0,0 +1,10 @@ +[thulani] +token = "aaaaaaa" +database_url = "bbbbbbb" +spreadsheet_id = "google_sheets_id" +sheets_api_key = "cccccccccc" +max_sheet_column = "ZZZ" +steam_api_key = "ddddddddddd" +max_hist = 30 +default_hist = 5 +prefixes = ["thulani", "thulando", "thulando madando"] diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..f0746d1 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,9 @@ +import Config + +config :thulani, + env: Mix.env() + +config :logger, + level: :info + +import_config "#{Mix.env}.exs"
\ No newline at end of file diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 0000000..9cba782 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1,14 @@ +import Config + +config :logger, + level: :debug + +config :nostrum, + dev: true + +IO.puts("hello") + +Thulani.Bot.Config.load_env() +|> Enum.each(fn {key, vals} -> + config key, vals +end) diff --git a/config/load_from_dotenv.exs b/config/load_from_dotenv.exs new file mode 100644 index 0000000..244e2bc --- /dev/null +++ b/config/load_from_dotenv.exs @@ -0,0 +1,36 @@ +def load_dotenv!() do + {:ok, projroot} = __DIR__ |> get_projroot + + contents = + case projroot |> Path.join(".env") |> File.read() do + {:error, :enoent} -> + Logger.warn("skipping dotenv file: doesn't exist") + "" + + {:ok, contents} -> + contents + end + + contents + |> String.split("\n") + |> Enum.map(&String.trim/1) + |> Enum.filter(fn x -> x != "" end) + |> Enum.map(fn x -> + result = String.split(x, "=") + {Enum.at(result, 0), Enum.at(result, 1)} + end) + |> System.put_env() +end + +def get_projroot(base) do + result = + with {:ok, info} <- base |> Path.join(".thulani_root") |> File.stat(), + :regular <- info.type, + do: {:ok, base} + + case result do + {:error, :enoent} -> get_projroot(base |> Path.join("..") |> Path.expand()) + {:ok, "/"} -> {:error, "couldn't find .thulani_root"} + x -> x + end +end diff --git a/config/prod.exs b/config/prod.exs new file mode 100644 index 0000000..ba90609 --- /dev/null +++ b/config/prod.exs @@ -0,0 +1,5 @@ +import Config + +config :logger, + utc_log: true, + level: :warn diff --git a/config/release.exs b/config/release.exs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/config/release.exs @@ -0,0 +1 @@ + @@ -0,0 +1,43 @@ +defmodule Thulani.MixProject do + use Mix.Project + + def project do + [ + apps_path: "apps", + version: "0.1.0", + start_permanent: Mix.env() == :prod, + deps: deps(), + releases: releases(), + default_release: :thulani_flex + ] + end + + defp deps do + [ + {:toml, "~> 0.6.1"} + ] + end + + defp releases do + config_providers = [ + # {Toml.Provider, ["./config.toml", "/etc/thulani/config.toml"]} + ] + + applications = [thulani_bot: :permanent] + + [ + thulani_flex: [ + include_executables_for: [:unix, :windows], + strip_beams: false, + config_providers: config_providers, + applications: applications + ], + thulani_prod: [ + include_executables_for: [:unix], + strip_beams: true, + config_providers: config_providers, + applications: applications + ] + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..93687d1 --- /dev/null +++ b/mix.lock @@ -0,0 +1,17 @@ +%{ + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "2.6.0", "8aa629f81a0fc189f261dc98a42243fa842625feea3c7ec56c48f4ccdb55490f", [:rebar3], [], "hexpm"}, + "gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"}, + "gun": {:hex, :gun, "1.3.1", "1489fd96018431b89f401041a9ce0b02b45265247f0fdcf71273bf087c64ea4f", [:rebar3], [{:cowlib, "~> 2.6.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [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]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [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.1", "da5f1ad98146117f6b7884e144c38fdc2b2a413d863d88a73297c67ef86fc863", [:mix], [{:gen_stage, "~> 0.11", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.5", [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.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, + "toml": {:hex, :toml, "0.6.1", "e1c3c91c1fc1ed87e7315a5a88e4bd1eeddf3e61dca2b708724a6a2d1a013844", [:mix], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, +} diff --git a/rel/env.bat.eex b/rel/env.bat.eex new file mode 100644 index 0000000..2ecd77f --- /dev/null +++ b/rel/env.bat.eex @@ -0,0 +1,7 @@ +@echo off +rem Set the release to work across nodes. If using the long name format like +rem the one below (my_app@127.0.0.1), you need to also uncomment the +rem RELEASE_DISTRIBUTION variable below. + +set RELEASE_DISTRIBUTION=name +set RELEASE_NODE=<%= @release.name %>@0.0.0.0 diff --git a/rel/env.sh.eex b/rel/env.sh.eex new file mode 100644 index 0000000..365fa18 --- /dev/null +++ b/rel/env.sh.eex @@ -0,0 +1,18 @@ +#!/bin/sh + +# Sets and enables heart (recommended only in daemon mode) +case $RELEASE_COMMAND in + daemon*) + export HEART_COMMAND="$RELEASE_ROOT/bin/$RELEASE_NAME $RELEASE_COMMAND" + export ELIXIR_ERL_OPTIONS="-heart" + ;; + + *) + ;; +esac + +# Set the release to work across nodes. If using the long name format like +# the one below (my_app@127.0.0.1), you need to also uncomment the +# RELEASE_DISTRIBUTION variable below. +export RELEASE_DISTRIBUTION=name +export RELEASE_NODE="<%= @release.name %>@0.0.0.0" diff --git a/rel/vm.args.eex b/rel/vm.args.eex new file mode 100644 index 0000000..71e8032 --- /dev/null +++ b/rel/vm.args.eex @@ -0,0 +1,11 @@ +## Customize flags given to the VM: http://erlang.org/doc/man/erl.html +## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here + +## Number of dirty schedulers doing IO work (file, sockets, etc) +##+SDio 5 + +## Increase number of concurrent ports/sockets +##+Q 65536 + +## Tweak GC to run more often +##-env ERL_FULLSWEEP_AFTER 10 |
