refactor: move commands && add queue subcommands

This commit is contained in:
Roman 2023-06-06 17:01:17 -04:00
parent da0f899eac
commit 7d57478dda
4 changed files with 158 additions and 133 deletions

129
lib/sergei/commands.ex Normal file
View file

@ -0,0 +1,129 @@
defmodule Sergei.Commands do
require Logger
# Translate params to list of maps
opt = fn type, name, desc, opts ->
%{type: type, name: name, description: desc}
|> Map.merge(Enum.into(opts, %{}))
end
@play_opts [
opt.(3, "url", "URL of the audio to play", [])
]
@queue_add_opts [
opt.(3, "url", "URL of the audio to queue", required: true)
]
@queue_opts [
opt.(1, "add", "Add a song to the queue", options: @queue_add_opts)
]
@slash_commands [
{"ping", "Pong", []},
{"play", "Play a song or resume playback", @play_opts},
{"queue", "Manage the song queue", @queue_opts},
{"pause", "Pause media playback", []},
{"stop", "Stop media playback and leave the voice channel", []},
{"song", "What song is currently playing?", []}
]
def commands() do
Enum.map(@slash_commands, fn {name, description, options} ->
%{
name: name,
description: description,
options: options
}
end)
end
# /ping
def do_command(%{data: %{name: "ping"}}) do
{:ok, "Pong"}
end
# /play <url>
def do_command(%{
guild_id: guild_id,
user: %{id: invoker_id},
data: %{name: "play", options: [%{name: "url", value: url}]}
}) do
case Sergei.VoiceStateCache.get_state(invoker_id) do
%{guild_id: id} = _res when guild_id != id ->
{:error, "You're not connected to a voice channel in this server."}
%{channel_id: channel_id} = _res ->
Sergei.Player.play(guild_id, channel_id, url)
nil ->
{:error, "You are not in a voice channel."}
end
end
# /play
def do_command(%{guild_id: guild_id, data: %{name: "play"}}) do
case Sergei.Player.resume(guild_id) do
:ok ->
{:ok, "Resuming playback..."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to resume media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /queue <subcommand>
def do_command(%{
guild_id: guild_id,
data: %{name: "queue", options: opts}
}) do
subcommand = List.first(opts)
Sergei.Commands.Queue.handle(guild_id, subcommand.name, subcommand.options)
end
# /pause
def do_command(%{guild_id: guild_id, data: %{name: "pause"}}) do
case Sergei.Player.pause(guild_id) do
:ok ->
{:ok, "Pausing..."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to pause media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /stop
def do_command(%{guild_id: guild_id, data: %{name: "stop"}}) do
case Sergei.Player.stop(guild_id) do
:ok ->
{:ok, "Bye!"}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to stop media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /song
def do_command(%{guild_id: guild_id, data: %{name: "song"}}) do
case Sergei.Player.get_current_song(guild_id) do
:not_playing ->
{:ok, "I'm not playing anything right now."}
url ->
{:ok, url}
end
end
end

View file

@ -0,0 +1,23 @@
defmodule Sergei.Commands.Queue do
require Logger
@spec handle(integer(), String.t(), [%{name: String.t(), value: String.t()}]) ::
{:ok, String.t()} | {:err, String.t()}
def handle(guild_id, "add", opts) do
[
%{name: "url", value: url}
] = opts
case Sergei.Player.queue_add(guild_id, url) do
:ok ->
{:ok, "Song queued."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to queue media: #{err}")
{:error, "This is embarassing..."}
end
end
end

View file

@ -4,42 +4,12 @@ defmodule Sergei.Consumer do
require Logger
alias Nostrum.Api
# Translate params to list of maps
opt = fn type, name, desc, opts ->
%{type: type, name: name, description: desc}
|> Map.merge(Enum.into(opts, %{}))
end
@play_opts [
opt.(3, "url", "URL of the audio to play", [])
]
@queue_opts [
opt.(3, "url", "URL of the audio to queue", required: true)
]
@slash_commands [
{"ping", "Pong", []},
{"play", "Play a song or resume playback", @play_opts},
{"queue", "Queue a song to play next", @queue_opts},
{"pause", "Pause media playback", []},
{"stop", "Stop media playback and leave the voice channel", []},
{"song", "What song is currently playing?", []}
]
# Initialization of the Discord Client
def handle_event({:READY, %{guilds: guilds} = _event, _ws_state}) do
# Playing some tunes
Api.update_status(:online, "some tunes", 0)
commands =
Enum.map(@slash_commands, fn {name, description, options} ->
%{
name: name,
description: description,
options: options
}
end)
commands = Sergei.Commands.commands()
case Application.get_env(:sergei, :env) do
:prod ->
@ -72,7 +42,7 @@ defmodule Sergei.Consumer do
# Handle interactions
def handle_event({:INTERACTION_CREATE, interaction, _ws_state}) do
response =
case do_command(interaction) do
case Sergei.Commands.do_command(interaction) do
{:ok, msg} ->
%{type: 4, data: %{content: msg, flags: 2 ** 6}}
@ -93,101 +63,4 @@ defmodule Sergei.Consumer do
def handle_event(_event) do
:noop
end
# /ping
def do_command(%{data: %{name: "ping"}}) do
{:ok, "Pong"}
end
# /play <url>
def do_command(%{
guild_id: guild_id,
user: %{id: invoker_id},
data: %{name: "play", options: [%{name: "url", value: url}]}
}) do
case Sergei.VoiceStateCache.get_state(invoker_id) do
%{guild_id: id} = _res when guild_id != id ->
{:error, "You're not connected to a voice channel in this server."}
%{channel_id: channel_id} = _res ->
Sergei.Player.play(guild_id, channel_id, url)
nil ->
{:error, "You are not in a voice channel."}
end
end
# /play
def do_command(%{guild_id: guild_id, data: %{name: "play"}}) do
case Sergei.Player.resume(guild_id) do
:ok ->
{:ok, "Resuming playback..."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to resume media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /queue <url>
def do_command(%{
guild_id: guild_id,
data: %{name: "queue", options: [%{name: "url", value: url}]}
}) do
case Sergei.Player.queue(guild_id, url) do
:ok ->
{:ok, "Song queued."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to queue media: #{err}")
{:error, "This is embarassing..."}
end
end
# /pause
def do_command(%{guild_id: guild_id, data: %{name: "pause"}}) do
case Sergei.Player.pause(guild_id) do
:ok ->
{:ok, "Pausing..."}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to pause media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /stop
def do_command(%{guild_id: guild_id, data: %{name: "stop"}}) do
case Sergei.Player.stop(guild_id) do
:ok ->
{:ok, "Bye!"}
:not_playing ->
{:ok, "I'm not playing anything right now."}
{:error, err} ->
Logger.error("Failed to stop media: #{err}")
{:error, "This is embarrasing..."}
end
end
# /song
def do_command(%{guild_id: guild_id, data: %{name: "song"}}) do
case Sergei.Player.get_current_song(guild_id) do
:not_playing ->
{:ok, "I'm not playing anything right now."}
url ->
{:ok, url}
end
end
end

View file

@ -24,9 +24,9 @@ defmodule Sergei.Player do
GenServer.call(__MODULE__, {:play, guild_id, channel_id, url})
end
@spec queue(integer(), String.t()) :: :ok | :not_playing | {:error, String.t()}
def queue(guild_id, url) do
GenServer.call(__MODULE__, {:queue, guild_id, url})
@spec queue_add(integer(), String.t()) :: :ok | :not_playing | {:error, String.t()}
def queue_add(guild_id, url) do
GenServer.call(__MODULE__, {:queue_add, guild_id, url})
end
@spec pause(integer()) :: :ok | :not_playing | {:error, String.t()}
@ -128,7 +128,7 @@ defmodule Sergei.Player do
# Queue
@impl true
def handle_call({:queue, guild_id, url}, _from, state) do
def handle_call({:queue_add, guild_id, url}, _from, state) do
%{queue: queue} = Map.fetch!(state, guild_id)
{