commit c1f9b008d1a8ba524fa6aff5153d2ea05fcaa86e Author: mehbark Date: Sun Jan 18 16:17:55 2026 -0500 initial discrimination diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fc55a3 --- /dev/null +++ b/.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/ + +# Temporary files, for example, from tests. +/tmp/ + +# 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"). +top_poster_server-*.tar + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ff8db2 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# TopPosterServer + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `top_poster_server` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:top_poster_server, "~> 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 . + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..3b4807d --- /dev/null +++ b/config/config.exs @@ -0,0 +1,3 @@ +import Config + +config :top_poster_server, TopPosterServer.Repo, database: :memory diff --git a/lib/top_poster_server.ex b/lib/top_poster_server.ex new file mode 100644 index 0000000..45c86a9 --- /dev/null +++ b/lib/top_poster_server.ex @@ -0,0 +1,18 @@ +defmodule TopPosterServer do + @moduledoc """ + Documentation for `TopPosterServer`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> TopPosterServer.hello() + :world + + """ + def hello do + :world + end +end diff --git a/lib/top_poster_server/application.ex b/lib/top_poster_server/application.ex new file mode 100644 index 0000000..4ef37b5 --- /dev/null +++ b/lib/top_poster_server/application.ex @@ -0,0 +1,20 @@ +defmodule TopPosterServer.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + children = [ + {Bandit, plug: TopPosterServer.Plug}, + TopPosterServer.Repo + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: TopPosterServer.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lib/top_poster_server/plug.ex b/lib/top_poster_server/plug.ex new file mode 100644 index 0000000..0350a99 --- /dev/null +++ b/lib/top_poster_server/plug.ex @@ -0,0 +1,38 @@ +defmodule TopPosterServer.Plug do + import Plug.Conn + alias Plug.Conn + + alias TopPosterServer.{Repo, User} + + require Logger + + def init(options) do + options + end + + @spec call(Conn.t(), term()) :: Conn.t() + def call(%Conn{} = conn, _opts) do + conn = + conn + |> fetch_query_params() + |> put_resp_content_type("text/plain") + + discord_id = conn.query_params["id"] + + if is_valid_discord_id(discord_id) do + conn + |> send_resp(200, "valid id") + else + conn + |> send_resp(200, "default page") + end + end + + # Valid in format, not necessarily actually existing lol + @spec is_valid_discord_id(term()) :: boolean() + defp is_valid_discord_id(discord_id) when is_binary(discord_id) do + String.match?(discord_id, ~r/^\d{17,20}$/) + end + + defp is_valid_discord_id(_), do: false +end diff --git a/lib/top_poster_server/repo.ex b/lib/top_poster_server/repo.ex new file mode 100644 index 0000000..b553e95 --- /dev/null +++ b/lib/top_poster_server/repo.ex @@ -0,0 +1,5 @@ +defmodule TopPosterServer.Repo do + use Ecto.Repo, + otp_app: :top_poster_server, + adapter: Ecto.Adapters.SQLite3 +end diff --git a/lib/top_poster_server/user.ex b/lib/top_poster_server/user.ex new file mode 100644 index 0000000..86fbcf8 --- /dev/null +++ b/lib/top_poster_server/user.ex @@ -0,0 +1,13 @@ +defmodule TopPosterServer.User do + use Ecto.Schema + + @type t :: %__MODULE__{ + discord_id: String.t(), + posts: non_neg_integer() + } + + schema "users" do + field(:discord_id, :string) + field(:posts, :integer, default: 0) + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..b090215 --- /dev/null +++ b/mix.exs @@ -0,0 +1,29 @@ +defmodule TopPosterServer.MixProject do + use Mix.Project + + def project do + [ + app: :top_poster_server, + version: "0.1.0", + elixir: "~> 1.19", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger], + mod: {TopPosterServer.Application, []} + ] + end + + defp deps do + [ + {:bandit, "~> 1.10.1"}, + {:plug, "~> 1.19"}, + {:ecto, "~> 3.13"}, + {:ecto_sqlite3, "~> 0.22.0"} + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..9074b1e --- /dev/null +++ b/mix.lock @@ -0,0 +1,18 @@ +%{ + "bandit": {:hex, :bandit, "1.10.1", "6b1f8609d947ae2a74da5bba8aee938c94348634e54e5625eef622ca0bbbb062", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b4c35f273030e44268ace53bf3d5991dfc385c77374244e2f960876547671aa"}, + "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, + "db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"}, + "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, + "ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"}, + "ecto_sql": {:hex, :ecto_sql, "3.13.4", "b6e9d07557ddba62508a9ce4a484989a5bb5e9a048ae0e695f6d93f095c25d60", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b38cf0749ca4d1c5a8bcbff79bbe15446861ca12a61f9fba604486cb6b62a14"}, + "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.22.0", "edab2d0f701b7dd05dcf7e2d97769c106aff62b5cfddc000d1dd6f46b9cbd8c3", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "5af9e031bffcc5da0b7bca90c271a7b1e7c04a93fecf7f6cd35bc1b1921a64bd"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "exqlite": {:hex, :exqlite, "0.34.0", "ebca3570eb4c4eb4345d76c8e44ce31a62de7b24a54fd118164480f2954bd540", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "bcdc58879a0db5e08cd5f6fbe07a0692ceffaaaa617eab46b506137edf0a2742"}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, + "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, + "thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, +} diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/top_poster_server_test.exs b/test/top_poster_server_test.exs new file mode 100644 index 0000000..c86b1ed --- /dev/null +++ b/test/top_poster_server_test.exs @@ -0,0 +1,8 @@ +defmodule TopPosterServerTest do + use ExUnit.Case + doctest TopPosterServer + + test "greets the world" do + assert TopPosterServer.hello() == :world + end +end