TopggEx.Webhook (topgg_ex v0.1.3)

View Source

Top.gg Webhook handler for Plug-based HTTP servers.

This module provides webhook handling functionality for receiving vote notifications from Top.gg, compatible with Phoenix and other Plug-based HTTP servers.

Examples

# In your Phoenix router or Plug router:
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  import TopggEx.Webhook

  pipeline :webhook do
    plug :accepts, ["json"]
    plug TopggEx.Webhook, authorization: "your_webhook_auth_token"
  end

  scope "/webhooks" do
    pipe_through :webhook
    post "/topgg", YourController, :handle_vote
  end
end

# In your controller:
defmodule YourController do
  use MyAppWeb, :controller

          def handle_vote(conn, _params) do
    case conn.assigns.topgg_payload do
      %{"user" => user_id, "type" => "upvote"} ->
        # Handle the vote
        IO.puts("User #{user_id} voted!")
        send_resp(conn, 204, "")

      %{"user" => user_id, "type" => "test"} ->
        # Handle test webhook
        IO.puts("Test webhook from user #{user_id}")
        send_resp(conn, 204, "")

      _ ->
        send_resp(conn, 400, "Invalid payload")
    end
  end
end

Functional API

You can also use the functional API for more control:

# Verify and parse a webhook request
case TopggEx.Webhook.verify_and_parse(conn, "your_auth_token") do
  {:ok, payload} ->
    # Handle the vote payload
    IO.inspect(payload)

  {:error, :unauthorized} ->
    # Handle unauthorized request
    send_resp(conn, 403, Jason.encode!(%{error: "Unauthorized"}))

  {:error, :invalid_body} ->
    # Handle malformed request
    send_resp(conn, 400, Jason.encode!(%{error: "Invalid body"}))
end

Webhook Data Schema

The webhook payload typically contains:

  • user - The ID of the user who voted
  • type - The type of vote ("upvote" or "test")
  • bot - The ID of the bot that was voted for
  • isWeekend - Whether the vote was cast on a weekend (counts as 2 votes)
  • query - Query parameters from the vote page (if any)

Summary

Functions

Creates a functional webhook handler that can be used in controllers or other contexts.

Verifies the authorization header and parses the webhook payload.

Types

webhook_options()

@type webhook_options() :: [
  authorization: String.t(),
  error_handler: (any() -> any()),
  assign_key: atom()
]

webhook_payload()

@type webhook_payload() :: %{
  required(String.t()) => any(),
  optional(String.t()) => any()
}

Functions

listener(handler_fun, opts \\ [])

@spec listener((webhook_payload(), Plug.Conn.t() -> Plug.Conn.t()), webhook_options()) ::
  (Plug.Conn.t(), any() -> Plug.Conn.t())

Creates a functional webhook handler that can be used in controllers or other contexts.

Parameters

  • handler_fun - Function that takes the payload and connection
  • opts - Options including :authorization and :error_handler

Returns

A function that can be used as a Plug or called directly.

Examples

       webhook_handler = TopggEx.Webhook.listener(fn payload, conn ->
   IO.puts("User #{payload["user"]} voted!")
   send_resp(conn, 204, "")
 end, authorization: "my_auth_token")

# Use in a router
post "/webhook", webhook_handler

verify_and_parse(conn, expected_auth \\ nil)

@spec verify_and_parse(Plug.Conn.t(), String.t() | nil) ::
  {:ok, webhook_payload()} | {:error, atom()}

Verifies the authorization header and parses the webhook payload.

Parameters

  • conn - The Plug connection
  • expected_auth - The expected authorization token (optional)

Returns

  • {:ok, payload} - Successfully parsed webhook payload
  • {:error, :unauthorized} - Authorization header doesn't match
  • {:error, :invalid_body} - Request body is not valid JSON
  • {:error, :malformed_request} - Error reading request body

Examples

       case TopggEx.Webhook.verify_and_parse(conn, "my_auth_token") do
   {:ok, payload} ->
     IO.puts("Received vote from user: #{payload["user"]}")

   {:error, reason} ->
     IO.puts("Webhook error: #{inspect(reason)}")
 end