From 39d9292f1cee69b64af7b0159d5d2da64ff4649c Mon Sep 17 00:00:00 2001 From: Adam Millerchip Date: Fri, 30 Nov 2018 15:36:39 +0900 Subject: [PATCH] Initial commit --- .formatter.exs | 5 ++++ .gitignore | 24 ++++++++++++++++ README.md | 3 ++ config/config.exs | 31 +++++++++++++++++++++ lib/advent.ex | 58 +++++++++++++++++++++++++++++++++++++++ lib/advent/application.ex | 19 +++++++++++++ lib/advent/router.ex | 26 ++++++++++++++++++ mix.exs | 31 +++++++++++++++++++++ mix.lock | 23 ++++++++++++++++ test/advent_test.exs | 4 +++ test/test_helper.exs | 1 + 11 files changed, 225 insertions(+) create mode 100644 .formatter.exs create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config/config.exs create mode 100644 lib/advent.ex create mode 100644 lib/advent/application.ex create mode 100644 lib/advent/router.ex create mode 100644 mix.exs create mode 100644 mix.lock create mode 100644 test/advent_test.exs create mode 100644 test/test_helper.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..2998aea --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,5 @@ +# Used by "mix format" +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], + locals_without_parens: [plug: 1, plug: 2] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53dfb3f --- /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 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# 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"). +advent-*.tar + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c2af9c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Advent + +An Example Clova Skill written in Elixir diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..5ea9acb --- /dev/null +++ b/config/config.exs @@ -0,0 +1,31 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :advent, force_signature_valid: Mix.env() !== :prod +# This configuration is loaded before any dependency and is restricted +# to this project. If another project depends on this project, this +# file won't be loaded nor affect the parent project. For this reason, +# if you want to provide default values for your application for +# 3rd-party users, it should be done in your "mix.exs" file. + +# You can configure your application as: +# +# config :advent, key: :value +# +# and access this configuration in your application as: +# +# Application.get_env(:advent, :key) +# +# You can also configure a 3rd-party app: +# +# config :logger, level: :info +# + +# It is also possible to import configuration files, relative to this +# directory. For example, you can emulate configuration per environment +# by uncommenting the line below and defining dev.exs, test.exs and such. +# Configuration from the imported file will override the ones defined +# here (which is why it is important to import them last). +# +# import_config "#{Mix.env()}.exs" diff --git a/lib/advent.ex b/lib/advent.ex new file mode 100644 index 0000000..b2e64a4 --- /dev/null +++ b/lib/advent.ex @@ -0,0 +1,58 @@ +defmodule Advent do + use Clova + + def handle_launch(_req, resp) do + resp + |> add_speech("今日は#{say_date(today())}です") + |> add_speech(days_to_christmas() |> say_days_to_christmas()) + |> add_speech(prompt_for_another()) + end + + def handle_intent("different_day", req, resp) do + with potential_date when not is_nil(potential_date) <- get_slot(req, "date"), + {:ok, date} <- Date.from_iso8601(potential_date) do + resp + |> add_speech("#{say_date(date)}まで計算しますか?") + |> put_session_attributes(%{"date" => date}) + |> add_reprompt("#{date.month}月#{date.day}日まで計算したい場合、「はい」と言ってください。") + |> add_reprompt(prompt_for_another()) + else + _ -> add_speech(resp, "それは日付だと思いますがもっとわかりやすく言ってくださいね") + end + end + + def handle_intent("Clova.YesIntent", req, resp) do + with %{"date" => iso_date} <- get_session_attributes(req), + {:ok, date} <- Date.from_iso8601(iso_date) do + resp + |> add_speech(say_days_to(date)) + |> add_speech(prompt_for_another()) + else + _ -> end_session(resp) + end + end + + def handle_intent(_name, _req, resp), do: end_session(resp) + + defp prompt_for_another(), do: "違う日まで計算したい場合、日付けを言ってください" + + defp say_days_to_christmas(0), do: "クリスマスの日です!メリークリスマス!" + defp say_days_to_christmas(days) when days < 0, do: "クリスマスはもうすぎました。来年まで楽しみましょう。" + defp say_days_to_christmas(days), do: "クリスマスまであと#{days}日です!" + + defp today, do: Timex.now("Asia/Tokyo") |> DateTime.to_date() + + defp say_date(date), do: "#{date.year}年#{date.month}月#{date.day}日" + + defp days_to(date), do: Date.diff(date, today()) + + defp say_days_to(date), do: say_days_to(date, days_to(date)) + defp say_days_to(date, 0), do: "#{say_date(date)}は今日です!" + defp say_days_to(date, days) when days < 0, do: "#{say_date(date)}から#{-days}日すぎました。" + defp say_days_to(date, days), do: "#{say_date(date)}まであと#{days}日です。" + + defp days_to_christmas do + {:ok, xmas} = Date.new(today().year, 12, 25) + days_to(xmas) + end +end diff --git a/lib/advent/application.ex b/lib/advent/application.ex new file mode 100644 index 0000000..55115bd --- /dev/null +++ b/lib/advent/application.ex @@ -0,0 +1,19 @@ +defmodule Advent.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + # List all child processes to be supervised + children = [ + {Plug.Adapters.Cowboy, scheme: :http, plug: Advent.Router, options: [port: 4000]} + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Advent.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lib/advent/router.ex b/lib/advent/router.ex new file mode 100644 index 0000000..552a40a --- /dev/null +++ b/lib/advent/router.ex @@ -0,0 +1,26 @@ +defmodule Advent.Router do + use Plug.Router + use Plug.ErrorHandler + + plug Plug.Logger + + plug Clova.SkillPlug, + dispatch_to: Advent, + app_id: "com.example.advent", + json_module: Poison, + force_signature_valid: Application.get_env(:advent, :force_signature_valid, false) + + plug :match + plug :dispatch + + post "/clova" do + send_resp(conn) + end + + match("/clova", do: send_resp(conn, :method_not_allowed, "")) + match(_, do: send_resp(conn, :not_found, "")) + + def handle_errors(conn, %{kind: :error, reason: reason}) do + send_resp(conn, conn.status, Exception.message(reason)) + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..935719a --- /dev/null +++ b/mix.exs @@ -0,0 +1,31 @@ +defmodule Advent.MixProject do + use Mix.Project + + def project do + [ + app: :advent, + version: "0.1.0", + elixir: "~> 1.7", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {Advent.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:clova, "~> 0.5.0"}, + {:plug_cowboy, "~> 2.0"}, + {:poison, "~> 3.1"}, + {:timex, "~> 3.4.2"} + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..e16d985 --- /dev/null +++ b/mix.lock @@ -0,0 +1,23 @@ +%{ + "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "clova": {:hex, :clova, "0.5.0", "899ddfda81fcc2a10d0cceb571f8f429d4c0ca152b8c83cc33769b1b28a631c9", [:mix], [{:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, + "cowboy": {:hex, :cowboy, "2.6.0", "dc1ff5354c89e36a3e3ef8d10433396dcff0dcbb1d4223b58c64c2d51a6d88d9", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"}, + "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.14.3", "b5f6f5dcc4f1fba340762738759209e21914516df6be440d85772542d4a5e412", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [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"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.0.0", "ab0c92728f2ba43c544cce85f0f220d8d30fc0c90eaa1e6203683ab039655062", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "ranch": {:hex, :ranch, "1.7.0", "9583f47160ca62af7f8d5db11454068eaa32b56eeadf984d4f46e61a076df5f2", [:rebar3], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "timex": {:hex, :timex, "3.4.2", "d74649c93ad0e12ce5b17cf5e11fbd1fb1b24a3d114643e86dba194b64439547", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, +} diff --git a/test/advent_test.exs b/test/advent_test.exs new file mode 100644 index 0000000..fc2e5c3 --- /dev/null +++ b/test/advent_test.exs @@ -0,0 +1,4 @@ +defmodule AdventTest do + use ExUnit.Case + doctest Advent +end 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()