WIP: Wordbot
Wordbot is a little more complex of a bot module and I've been working on it here. Other than wordbot module, a few minor tweaks have been added all around that don't really affect anything. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
28
lib/contrib/wordbot.ex
Normal file
28
lib/contrib/wordbot.ex
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
defmodule Omnibot.Contrib.Wordbot do
|
||||||
|
use Omnibot.Module.Base
|
||||||
|
use Supervisor
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias Omnibot.Contrib.Wordbot
|
||||||
|
|
||||||
|
@default_config wordbot_source: "words.txt", wordbot_db: "wordbot.db"
|
||||||
|
|
||||||
|
def start_link(opts) do
|
||||||
|
Supervisor.start_link(__MODULE__, opts[:cfg], opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(cfg) do
|
||||||
|
children = [
|
||||||
|
{Task.Supervisor, name: Omnibot.Contrib.Wordbot.Watchers, strategy: :one_for_one},
|
||||||
|
Wordbot.Db.child_spec(cfg[:wordbot_db]),
|
||||||
|
{Wordbot.Bot, cfg: cfg, name: Omnibot.Contrib.Wordbot.Bot},
|
||||||
|
]
|
||||||
|
|
||||||
|
Supervisor.init(children, strategy: :one_for_all)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_msg(irc, msg), do: Wordbot.Bot.on_msg(irc, msg)
|
||||||
|
|
||||||
|
def on_channel_msg(irc, channel, nick, msg), do: Wordbot.Bot.on_channel_msg(irc, channel, nick, msg)
|
||||||
|
end
|
||||||
35
lib/contrib/wordbot/bot.ex
Normal file
35
lib/contrib/wordbot/bot.ex
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
defmodule Omnibot.Contrib.Wordbot.Bot do
|
||||||
|
use Omnibot.Module
|
||||||
|
|
||||||
|
alias Omnibot.Contrib.Wordbot
|
||||||
|
|
||||||
|
@split_pattern ~r/[\s\b]+/
|
||||||
|
|
||||||
|
command "!wordbot", ["leaderboard"] do
|
||||||
|
Irc.send_to(irc, channel, "leaderboard logic here")
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def on_init(cfg) do
|
||||||
|
Wordbot.Db.ensure_db()
|
||||||
|
File.read!(cfg[:wordbot_source])
|
||||||
|
|> String.split("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def on_channel_msg(irc, channel, nick, msg) do
|
||||||
|
words = Regex.split(@split_pattern, msg) |> MapSet.new()
|
||||||
|
game_words = Wordbot.Db.unmatched_words(channel) |> MapSet.new()
|
||||||
|
MapSet.intersection(words, game_words)
|
||||||
|
|> Enum.each(fn word ->
|
||||||
|
Wordbot.Db.add_score(channel, nick, word, msg)
|
||||||
|
Irc.send_to(irc, channel, "#{nick}: Congrats! '#{word}' is good for 1 point.")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def on_join(_irc, _channel, _who) do
|
||||||
|
# TODO start games
|
||||||
|
# * Tasks for watching games(?)
|
||||||
|
end
|
||||||
|
end
|
||||||
189
lib/contrib/wordbot/db.ex
Normal file
189
lib/contrib/wordbot/db.ex
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
defmodule Omnibot.Contrib.Wordbot.Db do
|
||||||
|
alias Omnibot.Util
|
||||||
|
|
||||||
|
# SQL for creating the new database
|
||||||
|
@database_sql ~S"""
|
||||||
|
CREATE TABLE IF NOT EXISTS game (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
start INTEGER NOT NULL,
|
||||||
|
end INTEGER NOT NULL,
|
||||||
|
channel VARCHAR(40) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS word (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
game INTEGER NOT NULL,
|
||||||
|
word VARCHAR(40) NOT NULL,
|
||||||
|
FOREIGN KEY (game) REFERENCES game(id),
|
||||||
|
UNIQUE(game, word)
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS score (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
game INTEGER NOT NULL,
|
||||||
|
word INTEGER NOT NULL,
|
||||||
|
user VARCHAR(40) NOT NULL,
|
||||||
|
line VARCHAR(1024) NOT NULL,
|
||||||
|
FOREIGN KEY (game) REFERENCES game(id),
|
||||||
|
FOREIGN KEY (word) REFERENCES word(id),
|
||||||
|
UNIQUE(game, word)
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
|
||||||
|
def ensure_db() do
|
||||||
|
Sqlitex.Server.exec(__MODULE__, @database_sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Gets the words for the current round in the given channel."
|
||||||
|
def words(channel) do
|
||||||
|
id = game_id!(channel)
|
||||||
|
{:ok, rows} =
|
||||||
|
Sqlitex.Server.query(__MODULE__, "SELECT word FROM word WHERE game = ?1", bind: [id])
|
||||||
|
|
||||||
|
for row <- rows, do: row[:word]
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Starts a new round for the given channel in the database.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
* `:end_early` - if `true`, this will create a new game even though there is
|
||||||
|
a game already running. Default is false.
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
* `:ok` if a new round was started.
|
||||||
|
* `{:error, :game_running}` if a game couldn't be started because one was already running.
|
||||||
|
"""
|
||||||
|
def start_round(channel, words, duration, opts \\ []) do
|
||||||
|
end_early = Access.get(opts, :end_early, false)
|
||||||
|
|
||||||
|
if !game_active?(channel) or end_early do
|
||||||
|
start = Util.now_unix()
|
||||||
|
end_ = start + duration
|
||||||
|
|
||||||
|
with {:ok, _} <-
|
||||||
|
Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"INSERT INTO game (start, end, channel) VALUES (?1, ?2, ?3)",
|
||||||
|
bind: [start, end_, channel]
|
||||||
|
),
|
||||||
|
{:ok, game_id} = game_id(channel) do
|
||||||
|
Enum.each(
|
||||||
|
words,
|
||||||
|
fn word ->
|
||||||
|
{:ok, _} =
|
||||||
|
Sqlitex.Server.query(__MODULE__, "INSERT INTO word (game, word) VALUES(?1, ?2)",
|
||||||
|
bind: [game_id, word]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, :game_running}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def game_active?(channel) do
|
||||||
|
case game_id(channel) do
|
||||||
|
{:ok, _id} -> true
|
||||||
|
{:error, :no_game} -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def child_spec(wordbot_db) do
|
||||||
|
%{
|
||||||
|
id: Sqlitex.Server,
|
||||||
|
start: {Sqlitex.Server, :start_link, [wordbot_db, [name: __MODULE__]]}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets the ID of the currently running game.
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
* {:ok, id} on success
|
||||||
|
* {:error, :no_game} when no game is running for this channel
|
||||||
|
"""
|
||||||
|
def game_id(channel) do
|
||||||
|
now = Util.now_unix()
|
||||||
|
|
||||||
|
id =
|
||||||
|
with {:ok, rows} <-
|
||||||
|
Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"SELECT id FROM game WHERE channel = ?1 AND end > ?2 ORDER BY id DESC",
|
||||||
|
bind: [channel, now]
|
||||||
|
),
|
||||||
|
[id | _] <- Enum.map(rows, & &1[:id]),
|
||||||
|
do: id
|
||||||
|
|
||||||
|
case id do
|
||||||
|
[] -> {:error, :no_game}
|
||||||
|
id -> {:ok, id}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def game_id!(channel) do
|
||||||
|
{:ok, id} = game_id(channel)
|
||||||
|
id
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_game_id(channel) do
|
||||||
|
id =
|
||||||
|
with {:ok, rows} <-
|
||||||
|
Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"SELECT id FROM game WHERE channel = ?1 ORDER BY id DESC",
|
||||||
|
bind: [channel]
|
||||||
|
),
|
||||||
|
[id | _] <- for(row <- rows, do: row[:id]),
|
||||||
|
do: id
|
||||||
|
|
||||||
|
case id do
|
||||||
|
[] -> {:error, :no_game}
|
||||||
|
id -> {:ok, id}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_score(channel, user, word, line) do
|
||||||
|
id = game_id!(channel)
|
||||||
|
{:ok, _} = Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"""
|
||||||
|
INSERT INTO score (game, word, user, line)
|
||||||
|
VALUES (?1, (SELECT word.id FROM word WHERE game = ?1 AND word = ?2), ?3, ?4)
|
||||||
|
""", bind: [id, word, user, line])
|
||||||
|
end
|
||||||
|
|
||||||
|
def scores(channel) do
|
||||||
|
id = game_id!(channel)
|
||||||
|
{:ok, rows} = Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"""
|
||||||
|
SELECT user, COUNT(score.id) AS score FROM score
|
||||||
|
JOIN game ON score.game = game.id
|
||||||
|
WHERE game.id = ?1
|
||||||
|
GROUP BY user
|
||||||
|
""",
|
||||||
|
bind: [id]
|
||||||
|
)
|
||||||
|
Enum.map(rows, &Map.new/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Gets all words that have not been scored on from the given channel."
|
||||||
|
def unmatched_words(channel) do
|
||||||
|
id = game_id!(channel)
|
||||||
|
{:ok, rows} = Sqlitex.Server.query(
|
||||||
|
__MODULE__,
|
||||||
|
"""
|
||||||
|
SELECT word FROM word
|
||||||
|
WHERE word.game = ?1
|
||||||
|
AND id NOT IN (SELECT score.word FROM score WHERE game = ?1)
|
||||||
|
""",
|
||||||
|
bind: [id]
|
||||||
|
)
|
||||||
|
Enum.map(rows, &(&1[:word]))
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
# REWRITE
|
|
||||||
defmodule Omnibot.Irc do
|
defmodule Omnibot.Irc do
|
||||||
require Logger
|
require Logger
|
||||||
alias Omnibot.Irc.Msg
|
alias Omnibot.Irc.Msg
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ defmodule Omnibot.Supervisor do
|
|||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
use Supervisor
|
use Supervisor
|
||||||
|
require IEx
|
||||||
|
|
||||||
def start_link(opts) do
|
def start_link(opts) do
|
||||||
Supervisor.start_link(__MODULE__, :ok, opts)
|
Supervisor.start_link(__MODULE__, :ok, opts)
|
||||||
|
|||||||
@@ -2,4 +2,8 @@ defmodule Omnibot.Util do
|
|||||||
def string_empty?(s), do: String.length(s) == 0
|
def string_empty?(s), do: String.length(s) == 0
|
||||||
|
|
||||||
def string_or_nil(s), do: if(string_empty?(s), do: nil, else: s)
|
def string_or_nil(s), do: if(string_empty?(s), do: nil, else: s)
|
||||||
|
|
||||||
|
def now_unix, do: now_unix("Etc/UTC")
|
||||||
|
|
||||||
|
def now_unix(tz), do: DateTime.now!(tz) |> DateTime.to_unix()
|
||||||
end
|
end
|
||||||
|
|||||||
6
mix.exs
6
mix.exs
@@ -29,6 +29,10 @@ defmodule Omnibot.MixProject do
|
|||||||
|
|
||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[{:tesla, "~> 1.3.0"}]
|
# TODO : figure out how to make contrib modules optional (umbrella project?) and enable specific requirements
|
||||||
|
[
|
||||||
|
{:tesla, "~> 1.3.0"}, # Used by Omnibot.Contrib.Linkbot
|
||||||
|
{:sqlitex, "~> 1.7"}, # Used by Omnibot.Contrib.Wordbot
|
||||||
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
12
mix.lock
12
mix.lock
@@ -1,4 +1,16 @@
|
|||||||
%{
|
%{
|
||||||
|
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"},
|
||||||
|
"esqlite": {:hex, :esqlite, "0.4.1", "ba5d0bab6b9c8432ffe1bf12fee8e154a50f1c3c40eadc3a9c870c23ca94d961", [:rebar3], [], "hexpm"},
|
||||||
|
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [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]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||||
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||||
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||||
|
"sqlitex": {:hex, :sqlitex, "1.7.1", "022d477aab2ae999c43ae6fbd1782ff1457e0e95c251c7b5fa6f7b7b102040ff", [:mix], [{:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm"},
|
||||||
"tesla": {:hex, :tesla, "1.3.3", "26ae98627af5c406584aa6755ab5fc96315d70d69a24dd7f8369cfcb75094a45", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
"tesla": {:hex, :tesla, "1.3.3", "26ae98627af5c406584aa6755ab5fc96315d70d69a24dd7f8369cfcb75094a45", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
"tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm"},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ config = %Config {
|
|||||||
modules: [
|
modules: [
|
||||||
{Omnibot.Contrib.Linkbot, channels: :all},
|
{Omnibot.Contrib.Linkbot, channels: :all},
|
||||||
{Omnibot.Contrib.Fortune, channels: ["#idleville"]},
|
{Omnibot.Contrib.Fortune, channels: ["#idleville"]},
|
||||||
|
{Omnibot.Contrib.Wordbot, channels: ["#idleville"]},
|
||||||
],
|
],
|
||||||
|
|
||||||
#module_paths: [{"modules", recurse: true}]
|
#module_paths: [{"modules", recurse: true}]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
defmodule LinkbotTest do
|
defmodule LinkbotTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
alias Omnibot.Contrib.Linkbot
|
alias Omnibot.Contrib.Linkbot
|
||||||
|
|
||||||
|
|||||||
54
test/contrib/wordbot/db_test.exs
Normal file
54
test/contrib/wordbot/db_test.exs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
defmodule WordbotDbTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
alias Omnibot.Contrib.Wordbot
|
||||||
|
|
||||||
|
setup do
|
||||||
|
start_supervised!(Wordbot.Db.child_spec(":memory:"))
|
||||||
|
Wordbot.Db.ensure_db()
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "game starts round correctly" do
|
||||||
|
:ok = Wordbot.Db.start_round("test", [], 60)
|
||||||
|
assert Wordbot.Db.game_id!("test") == 1
|
||||||
|
:ok = Wordbot.Db.start_round("foo", [], 60)
|
||||||
|
assert Wordbot.Db.game_id!("foo") == 2
|
||||||
|
|
||||||
|
{:error, :game_running} = Wordbot.Db.start_round("foo", [], 60)
|
||||||
|
:ok = Wordbot.Db.start_round("foo", [], 60, end_early: true)
|
||||||
|
assert Wordbot.Db.game_id!("foo") == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "game keeps track of words" do
|
||||||
|
:ok = Wordbot.Db.start_round("test", ~w(a b c d), 60)
|
||||||
|
assert Wordbot.Db.words("test") == ~w(a b c d)
|
||||||
|
|
||||||
|
:ok = Wordbot.Db.start_round("foo", ~w(e f g h), 60)
|
||||||
|
assert Wordbot.Db.words("foo") == ~w(e f g h)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "game keeps track of scores" do
|
||||||
|
:ok = Wordbot.Db.start_round("test", ~w(a b c d), 60)
|
||||||
|
Wordbot.Db.add_score("test", "user1", "a", "this is a line")
|
||||||
|
Wordbot.Db.add_score("test", "user1", "b", "this is b line")
|
||||||
|
Wordbot.Db.add_score("test", "user2", "c", "this is b line")
|
||||||
|
|
||||||
|
scores = Wordbot.Db.scores("test")
|
||||||
|
assert Enum.member?(scores, %{user: "user1", score: 2})
|
||||||
|
assert Enum.member?(scores, %{user: "user2", score: 1})
|
||||||
|
|
||||||
|
:ok = Wordbot.Db.start_round("test", ~w(a b c d), 60, end_early: true)
|
||||||
|
scores = Wordbot.Db.scores("test")
|
||||||
|
assert scores == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "game keeps track of unmatched words" do
|
||||||
|
:ok = Wordbot.Db.start_round("test", ~w(a b c d), 60)
|
||||||
|
assert Wordbot.Db.unmatched_words("test") == ~w(a b c d)
|
||||||
|
Wordbot.Db.add_score("test", "user1", "a", "this is a line")
|
||||||
|
Wordbot.Db.add_score("test", "user1", "b", "this is a line")
|
||||||
|
Wordbot.Db.add_score("test", "user1", "d", "this is a line")
|
||||||
|
assert Wordbot.Db.unmatched_words("test") == ~w(c)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
alias Omnibot.Util
|
alias Omnibot.Util
|
||||||
|
|
||||||
defmodule Omnibot.UtilTest do
|
defmodule Omnibot.UtilTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
test "string_empty?" do
|
test "string_empty?" do
|
||||||
assert Util.string_empty?("")
|
assert Util.string_empty?("")
|
||||||
|
|||||||
Reference in New Issue
Block a user