Discord customer logger

This commit is contained in:
Owen
2024-12-17 15:28:34 +01:00
parent dbf651c19e
commit 2652a8f561
13 changed files with 126 additions and 16 deletions
+23 -2
View File
@@ -13,13 +13,34 @@ If you wish to invite this bot to your server, use [this link.](https://discord.
- Plant names are given in latin with a list of possible common names
- Provides links to [GBIF], [PFAF], and [POWO] for the identified plant
## Prerequisites
## Dependencies
```elixir
# mix.exs
defp deps do
[
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:nostrum, "~> 0.10"},
{:httpoison, "~> 2.2"},
{:image, "~> 0.55"},
{:jason, "~> 1.4"},
{:plug_cowboy, "~> 2.7"}
{:plug_cowboy, "~> 2.7"},
{:quantum, "~> 3.5"},
{:tesla, "~> 1.13"}
]
end
```
## Environment Variables
Below are the environment variables that you need to set for the program to function:
```
DISCORD_TOKEN= client secret from the bot's application
LOGS_DISCORD_WEBHOOK_URL= webhook url for the log channel
PLANTNET_API_KEY= API key for the PlantNet service
```
## Installation
-4
View File
@@ -11,8 +11,4 @@ config :plantid_discord_bot, PlantIdDiscordBot.Scheduler,
{"@daily", {PlantIdDiscordBot.RateLimiter, :reset_counters, []}}
]
config :logger, :console,
format: "$time $metadata[$level] $message\n",
level: :info
import_config "#{config_env()}.exs"
+3 -1
View File
@@ -12,4 +12,6 @@ config :plantid_discord_bot,
"https://discord.com/api/oauth2/authorize?client_id=948227126094598204&permissions=19520&scope=bot",
api: Nostrum.Api
config :logger, level: :debug
config :logger, :console,
format: "$time $metadata[$level] $message\n",
level: :debug
+1 -1
View File
@@ -13,4 +13,4 @@ config :plantid_discord_bot,
config :logger, :console,
format: "$time $metadata[$level] $message\n",
level: :info
level: :error
+8
View File
@@ -15,6 +15,14 @@ config :nostrum,
token: System.get_env("DISCORD_TOKEN"),
ffmpeg: nil
config :logger,
backends: [{PlantIdDiscordBot.DiscordLogger, :discord_logger}, :console]
config :logger, :console,
format: "$time $metadata[$level] $message\n",
level: :info
config :logger, :discord_logger,
webhook_url: System.get_env("LOGS_DISCORD_WEBHOOK_URL"),
level: :error,
bot_token: System.get_env("DISCORD_TOKEN")
+78
View File
@@ -0,0 +1,78 @@
defmodule PlantIdDiscordBot.DiscordLogger do
require Logger
use Tesla
plug(Tesla.Middleware.JSON)
@behaviour :gen_event
def init({__MODULE__, name}) do
{:ok, configure(name, [])}
end
def handle_call({:configure, opts}, %{name: name} = state) do
{:ok, :ok, configure(name, opts, state)}
end
def handle_event({_level, gl, {Logger, _, _, _}}, state) when node(gl) != node() do
{:ok, state}
end
def handle_event({level, _gl, {Logger, msg, ts, md}}, %{} = state) do
if is_level_okay(level, state.level) do
log_to_discord(state.webhook_url, level, msg, ts, md)
end
{:ok, state}
end
def handle_event(:flush, state) do
{:ok, state}
end
def handle_info(_, state) do
{:ok, state}
end
defp is_level_okay(lvl, min_level) do
is_nil(min_level) or Logger.compare_levels(lvl, min_level) != :lt
end
def log_to_discord(webhook_url, level, msg, ts, md) do
formatted_msg = format_message(level, msg, ts, md)
body = %{content: formatted_msg}
post(webhook_url, body)
|> case do
{:ok, %{status: status}} when status in 200..299 -> :ok
{:error, reason} -> Logger.error("Error sending log to Discord: #{inspect(reason)}")
_any -> Logger.error("Error sending log to Discord")
end
end
def format_message(level, msg, _ts, md) do
timestamp = DateTime.utc_now()
source = md[:application]
msg = IO.iodata_to_binary(msg) |> String.slice(0..1900)
"[#{timestamp}] [#{source}] [#{level}] `#{msg}`"
end
defp configure(name, opts) do
state = %{name: name, format: nil, level: nil, metadata: nil, metadata_filter: nil}
configure(name, opts, state)
end
defp configure(name, opts, state) do
env = Application.get_env(:logger, name, [])
opts = Keyword.merge(env, opts)
Application.put_env(:logger, name, opts)
new_state = %{
webhook_url: Keyword.get(opts, :webhook_url, nil),
level: Keyword.get(opts, :level)
}
Map.merge(state, new_state)
end
end
-1
View File
@@ -2,7 +2,6 @@ defmodule PlantIdDiscordBot.Consumer do
@moduledoc """
A Discord bot that identifies plants from photos of their organs.
"""
use Nostrum.Consumer
alias Nostrum.Api
alias PlantIdDiscordBot.Cog
+2
View File
@@ -3,6 +3,8 @@ defmodule PlantIdDiscordBot.Cog.Info do
import Nostrum.Struct.Embed
alias PlantIdDiscordBot.Utils
require Logger
# reference to Nostrum.Api in non-test environments, reference to mock in test
@api Application.compile_env(:plantid_discord_bot, :api)
+8 -6
View File
@@ -1,4 +1,5 @@
defmodule PlantIdDiscordBot.Cog.PlantNet do
require Logger
use Nostrum.Consumer
alias PlantIdDiscordBot.RateLimiter
alias PlantIdDiscordBot.PlantNet.Parser
@@ -50,7 +51,7 @@ defmodule PlantIdDiscordBot.Cog.PlantNet do
})
end
IO.inspect(saved_images)
Logger.debug(saved_images)
# TODO use actual image data
image1 =
@@ -73,13 +74,13 @@ defmodule PlantIdDiscordBot.Cog.PlantNet do
})
{:ok, %HTTPoison.Response{status_code: 400}} ->
# TODO add logger
Logger.error("Malformed request sent to the PlantNet API")
Api.create_followup_message(interaction.application_id, interaction.token, %{
content: "Bad Request"
})
{:ok, %HTTPoison.Response{status_code: 404}} ->
# TODO add logger
RateLimiter.increase_counter(interaction.guild_id)
Api.create_followup_message(interaction.application_id, interaction.token, %{
@@ -87,19 +88,20 @@ defmodule PlantIdDiscordBot.Cog.PlantNet do
})
{:ok, %HTTPoison.Response{status_code: 429}} ->
# TODO add logger
Logger.warning("Request limit exceeded for the PlantNet API")
Api.create_followup_message(interaction.application_id, interaction.token, %{
content: "Too Many Requests"
})
{_, _} ->
# TODO add logger
Logger.error("Internal server error when contacting the PlantNet API")
Api.create_followup_message(interaction.application_id, interaction.token, %{
content: "Internal Server Error"
})
end
# TODO make into a task for async deletion, deal with errors
Enum.map(saved_images, fn {:ok, filename} -> filename end)
|> File.delete_files!()
end
+2 -1
View File
@@ -28,7 +28,8 @@ defmodule PlantidDiscordBot.MixProject do
{:image, "~> 0.55"},
{:jason, "~> 1.4"},
{:plug_cowboy, "~> 2.7"},
{:quantum, "~> 3.5"}
{:quantum, "~> 3.5"},
{:tesla, "~> 1.13"}
]
end
end
+1
View File
@@ -33,6 +33,7 @@
"sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_registry": {:hex, :telemetry_registry, "0.3.2", "701576890320be6428189bff963e865e8f23e0ff3615eade8f78662be0fc003c", [:mix, :rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7ed191eb1d115a3034af8e1e35e4e63d5348851d556646d46ca3d1b4e16bab9"},
"tesla": {:hex, :tesla, "1.13.2", "85afa342eb2ac0fee830cf649dbd19179b6b359bec4710d02a3d5d587f016910", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "960609848f1ef654c3cdfad68453cd84a5febecb6ed9fed9416e36cd9cd724f9"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"vix": {:hex, :vix, "0.31.1", "2b1d379393060ee8e4e1f1c9a621811c4091d8f063221c1ff24a41a4f0c97edc", [:make, :mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "766856b52bec222cb5fd301f645a7a9869b61e0ec6e87dc0789ae9657356a8ea"},
}