60 | );
61 | } else {
62 | return (
63 | Connecting...
64 | );
65 | }
66 | }
67 |
68 | componentDidUpdate() {
69 | if(!bowser.mobile && !bowser.tablet){
70 | window.scrollTo(0,document.body.scrollHeight);
71 | }
72 | }
73 |
74 | _handleKeyDown(e) {
75 | if(e.code === 'Backspace' || e.key === 'Backspace' || e.keyIdentifier === "U+0008"){
76 | e.preventDefault();
77 | let { input } = this.state;
78 | this.setState({
79 | input: input.substring(0, input.length - 1)
80 | });
81 | }
82 | }
83 |
84 | _handleKeyPress(e) {
85 | let { input } = this.state;
86 | let { chatSession } = this.props;
87 |
88 | if (e.charCode === 13) {
89 | let message = { body: input, author: "me" };
90 | chatSession.sendMessage(message);
91 | this.setState({input: ""});
92 | } else {
93 | this.setState({
94 | input: input + (e.key || String.fromCharCode(e.keyCode))
95 | });
96 | }
97 |
98 | e.preventDefault();
99 | e.stopPropagation();
100 | return false;
101 | }
102 | }
103 |
104 | export default ChatBox;
105 |
--------------------------------------------------------------------------------
/apps/bot_engine/fixture/vcr_cassettes/sponsors.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "body": "",
5 | "headers": [],
6 | "method": "get",
7 | "options": [],
8 | "request_body": "",
9 | "url": "https://2016.fullstackfest.com/sponsors.json"
10 | },
11 | "response": {
12 | "body": "{\n \"categories\": [\n {\n \"name\": \"hackerman\",\n \"sponsors\": [\n ]\n\n }\n ,\n {\n \"name\": \"black_hat\",\n \"sponsors\": [\n {\n \"name\": \"Pusher\",\n \"description\": \"Hosted dev tools, API and libraries to add realtime, interactive features in minutes. Scales instantly & massively.\",\n \"logo\": \"pusher.png\",\n \"twitter\": \"pusher\",\n \"website\": \"https://pusher.com\"\n },\n {\n \"name\": \"Jobandtalent\",\n \"description\": \"Jobandtalent is the leading job marketplace for people to list and discover local jobs and talent online.\",\n \"logo\": \"job_and_talent.png\",\n \"twitter\": \"jobandtalenteng\",\n \"website\": \"https://www.jobandtalent.com/\"\n }\n ]\n\n }\n ,\n {\n \"name\": \"grey_hat\",\n \"sponsors\": [\n {\n \"name\": \"Xing\",\n \"description\": \"XING is the social network for business professionals. More than 11 million members worldwide use XING to boost their business, job, and career.\",\n \"logo\": \"xing.png\",\n \"twitter\": \"xing_bcn\",\n \"website\": \"https://www.xing.com/\"\n },\n {\n \"name\": \"Hitch\",\n \"description\": \"Building great developer communities is hard. Hitch is here to help you build the virtuous cycle of a healthy API ecosystem.\",\n \"logo\": \"hitch.png\",\n \"twitter\": \"hitchhq\",\n \"website\": \"https://www.hitchhq.com/\"\n }\n ]\n\n }\n ,\n {\n \"name\": \"white_hat\",\n \"sponsors\": [\n {\n \"name\": \"Drooms\",\n \"logo\": \"drooms.png\",\n \"twitter\": \"drooms_software\",\n \"website\": \"https://www.drooms.com/\"\n },\n {\n \"name\": \"Gemnasium\",\n \"logo\": \"gemnasium.png\",\n \"twitter\": \"gemnasiumapp\",\n \"website\": \"https://gemnasium.com/\"\n }\n ]\n\n }\n ]\n}\n",
13 | "headers": {
14 | "Content-Type": "application/json",
15 | "Content-Length": "2306",
16 | "Connection": "keep-alive",
17 | "Date": "Thu, 09 Jun 2016 10:35:47 GMT",
18 | "Cache-Control": "max-age=31536000",
19 | "x-amz-meta-content-md5": "90c567fe690b054be3970e36d4b31060",
20 | "Last-Modified": "Thu, 09 Jun 2016 10:27:36 GMT",
21 | "ETag": "\"90c567fe690b054be3970e36d4b31060\"",
22 | "Server": "AmazonS3",
23 | "Vary": "Accept-Encoding",
24 | "X-Cache": "Miss from cloudfront",
25 | "Via": "1.1 eb1377e62e606e544123a131efe632b8.cloudfront.net (CloudFront)",
26 | "X-Amz-Cf-Id": "zM-zt_EUlHN7ZldQ8z2NbtgLsWj9-ONk4uErC8KSLVfz-DQNAZhZaA=="
27 | },
28 | "status_code": 200,
29 | "type": "ok"
30 | }
31 | }
32 | ]
--------------------------------------------------------------------------------
/apps/bot_engine/lib/bot_engine/similarity.ex:
--------------------------------------------------------------------------------
1 | defmodule BotEngine.Similarity do
2 | @moduledoc """
3 | A module to retrieve a document by similarity within a corpus of documents.
4 | """
5 |
6 | @doc """
7 | Retrieves one, many or no documents from a `corpus` using a query `q`.
8 |
9 | The algorithm is as follows:
10 |
11 | 1. Score every document with `score_function/2` (which takes the
12 | query and the document)
13 | 2. Rejects the documents whose score is less than the `min_confidence`
14 | threshold
15 | 3. Reject the documents that are below the best score by more than `max_distance_from_best`
16 | 4. Return either:
17 | a) {:none, nil} if there was no reasonable match,
18 | b) {:one, best_candidate} if there was one clear match,
19 | c) {:many, up_to_three_best_candidates} if there was more than one good candidate
20 |
21 | ## Examples
22 |
23 | iex> BotEngine.Similarity.query( \
24 | "John", \
25 | ["Jonathan", "Rick", "Amanda", "Johnny"], \
26 | fn(q, name) -> String.jaro_distance(q, name) end, \
27 | %{min_confidence: 0.8, \
28 | max_distance_from_best: 0.2} \
29 | )
30 | {:one, "Johnny"}
31 |
32 | """
33 | def query(q, corpus, score_function, %{min_confidence: min_confidence,
34 | max_distance_from_best: max_distance_from_best}) do
35 | candidates = corpus |>
36 | Enum.map(fn(document) -> {score_function.(q, document), document} end) |>
37 | Enum.sort |>
38 | Enum.reject(fn({score, _}) -> score < min_confidence end) |>
39 | Enum.reverse |>
40 | Enum.reduce({0, []}, fn({score, document}, {max_score, acc}) ->
41 | new_score = if score > max_score, do: score, else: max_score
42 | new_acc = if (new_score - score) > max_distance_from_best, do: acc, else: [document | acc]
43 | {new_score, new_acc}
44 | end) |>
45 | Tuple.to_list |>
46 | List.last |>
47 | Enum.reverse
48 |
49 | case Enum.count(candidates) do
50 | 0 -> {:none, nil}
51 | 1 -> {:one, List.first(candidates)}
52 | _ -> {:many, Enum.take(candidates, 3)}
53 | end
54 | end
55 |
56 | defmodule Text do
57 | @moduledoc """
58 | Text-specific functions to determine similarity between keywords and text documents.
59 | """
60 |
61 | @doc """
62 | Returns the frequency of a token within a list of tokens.
63 |
64 | ## Examples
65 |
66 | iex> BotEngine.Similarity.Text.frequency("foo", ["bar", "foo", "foo"])
67 | 2
68 |
69 | """
70 | def frequency(token, tokens) do
71 | tokens |>
72 | Enum.filter(fn(t) -> t == token end) |>
73 | Enum.count
74 | end
75 |
76 | @doc """
77 | Tokenize a string into tokens of length 3 or more.
78 |
79 | ## Examples
80 |
81 | iex> BotEngine.Similarity.Text.tokenize("foo, Bar? baz. heyo in a box!!")
82 | ["foo", "bar", "baz", "heyo", "box"]
83 |
84 | """
85 | def tokenize(string) do
86 | string |>
87 | String.downcase |>
88 | String.replace(~r/[\.,!\?\(\)-]/, "") |>
89 | String.replace("/", " ") |>
90 | String.split |>
91 | Enum.reject(fn(t) -> String.length(t) < 3 end)
92 | end
93 |
94 | @doc """
95 | Returns the sum of frequencies of a query `q`'s tokens within a text.
96 |
97 | ## Examples
98 |
99 | iex> BotEngine.Similarity.Text.freq_sum("foo bar", "foo, bar, baz, bar haha")
100 | 3
101 |
102 | """
103 | def freq_sum(q, text) do
104 | text_tokens = tokenize(text)
105 | tokenize(q) |> Enum.map(fn(t) -> frequency(t, text_tokens) end) |> Enum.sum
106 | end
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []},
2 | "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
3 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
4 | "exactor": {:hex, :exactor, "2.2.0", "2a7418b82d974fe8276edd62c1facf4a9dc06339cdf11b5dcd10359e107fe5c3", [:mix], []},
5 | "excheck": {:hex, :excheck, "0.3.3", "ce2353afc7616cb8e41e03246448a2ca3d31c1492366687927f4dfaba21c155b", [:mix], []},
6 | "exjsx": {:hex, :exjsx, "3.2.0", "7136cc739ace295fc74c378f33699e5145bead4fdc1b4799822d0287489136fb", [:mix], [{:jsx, "~> 2.6.2", [hex: :jsx, optional: false]}]},
7 | "extwitter": {:hex, :extwitter, "0.7.1", "35c18c3947e7fd15dce977b8b366846b411d8ff4e53c95201b6d22f8af12cdef", [:mix], [{:poison, "~> 1.5", [hex: :poison, optional: false]}]},
8 | "exvcr": {:hex, :exvcr, "0.7.4", "9e7b4cf5f91ecfabdac7a375610633b28288795503bdff0eb8763c275d367657", [:mix], [{:meck, "~> 0.8.3", [hex: :meck, optional: false]}, {:httpotion, "~> 2.1", [hex: :httpotion, optional: true]}, {:httpoison, "~> 0.8", [hex: :httpoison, optional: true]}, {:exjsx, "~> 3.2", [hex: :exjsx, optional: false]}, {:exactor, "~> 2.2", [hex: :exactor, optional: false]}]},
9 | "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
10 | "gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []},
11 | "hackney": {:hex, :hackney, "1.6.0", "8d1e9440c9edf23bf5e5e2fe0c71de03eb265103b72901337394c840eec679ac", [:rebar3], [{:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:certifi, "0.4.0", [hex: :certifi, optional: false]}]},
12 | "httpoison": {:hex, :httpoison, "0.8.3", "b675a3fdc839a0b8d7a285c6b3747d6d596ae70b6ccb762233a990d7289ccae4", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]},
13 | "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []},
14 | "jsx": {:hex, :jsx, "2.6.2", "213721e058da0587a4bce3cc8a00ff6684ced229c8f9223245c6ff2c88fbaa5a", [:mix, :rebar], []},
15 | "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []},
16 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
17 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
18 | "oauth": {:git, "https://github.com/tim/erlang-oauth.git", "fca8163e8f7af867015e2413c4b03a4f8f4df0c9", []},
19 | "phoenix": {:hex, :phoenix, "1.1.6", "7bf19002669c8f692f5a9c8d30dab7b49f3dc56228d5bde92a12fb426b4783c2", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}]},
20 | "phoenix_html": {:hex, :phoenix_html, "2.5.1", "631053f9e345fecb5c87d9e0ccd807f7266d27e2ee4269817067af425fd81ba8", [:mix], [{:plug, "~> 0.13 or ~> 1.0", [hex: :plug, optional: false]}]},
21 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.5", "829218c4152ba1e9848e2bf8e161fcde6b4ec679a516259442561d21fde68d0b", [:mix], [{:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}, {:fs, "~> 0.9.1", [hex: :fs, optional: false]}]},
22 | "plug": {:hex, :plug, "1.1.5", "de5645c18170415a72b18cc3d215c05321ddecac27a15acb923742156e98278b", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}]},
23 | "poison": {:hex, :poison, "1.5.2", "560bdfb7449e3ddd23a096929fb9fc2122f709bcc758b2d5d5a5c7d0ea848910", [:mix], []},
24 | "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []},
25 | "spex": {:hex, :spex, "0.1.2", "e44edc55b3de645078eb2b81273d50ff573b7b171b5f564ef0c5b0c99cf302b3", [:mix], []},
26 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []},
27 | "triq": {:git, "https://github.com/krestenkrab/triq.git", "c7306b8eaea133d52140cb828817efb5e50a3d52", []}}
28 |
--------------------------------------------------------------------------------
/apps/bot_engine/test/bot_engine/responder_test.exs:
--------------------------------------------------------------------------------
1 | defmodule BotEngine.ResponderTest do
2 | use ExUnit.Case, async: false
3 | doctest BotEngine.Responder
4 | use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
5 |
6 | alias BotEngine.Responder
7 | alias BotEngine.Responder.Query
8 |
9 | setup_all do
10 | BotEngine.FullStackFest.start
11 | end
12 |
13 | test "it describes speakers by full name" do
14 | joe = describe_speaker("Joe Armstrong")
15 | assert(joe =~ "Erlang language & platform co-creator")
16 | assert(joe =~ "Keynote")
17 | assert(joe =~ "twitter.com/joeerl")
18 | assert(joe =~ "fullstackfest.com/speakers/joe-armstrong")
19 | assert(joe =~ "interview is worth a read")
20 | end
21 |
22 | test "it describes speakers by first name only" do
23 | joe = describe_speaker("simon")
24 | assert(joe =~ "Leading Shopify's core architecture team")
25 | end
26 |
27 | test "it allows for slight misspellings in the speaker's name" do
28 | joe = describe_speaker("joel amstong")
29 | assert(joe =~ "Erlang language & platform co-creator")
30 | assert(joe =~ "Keynote")
31 | assert(joe =~ "twitter.com/joeerl")
32 | assert(joe =~ "interview is worth a read")
33 | end
34 |
35 | test "it allows for ambiguities matching several speakers" do
36 | joe = describe_speaker("david")
37 | assert(joe =~ "Do you mean David Simons or David Wells?")
38 | end
39 |
40 | test "it doesn't recognize unknown speakers" do
41 | unknown = describe_speaker("Mark Zuckerberg")
42 | assert(unknown =~ "don't know" or unknown =~ "don't think")
43 | end
44 |
45 | test "it describes talks" do
46 | talk = describe_talk("Unison")
47 | assert(talk =~ "Unison: a new programming platform")
48 | assert(talk =~ "Paul Chiusano")
49 | assert(talk =~ "huge increases in productivity")
50 | end
51 |
52 | test "it allows for ambiguous talk queries" do
53 | assert(describe_talk("elm") =~ "Confident Frontend with Elm")
54 | end
55 |
56 | test "it allows for slight misspellings" do
57 | assert(describe_talk("server react programming") =~ "Make Reactive Programming on the Server Great Again")
58 | assert(describe_talk("performance") =~ "Ines Sombra")
59 | assert(describe_talk("IPFS") =~ "Juan Benet")
60 | end
61 |
62 | test "it doesn't recognize unknown talks" do
63 | assert(describe_talk("baking sourdough") =~ "I can't recall")
64 | end
65 |
66 | test "it knows who speaks about a specific topic" do
67 | assert(who_speaks_about("unison") =~ "Paul Chiusano")
68 | assert(whats_x_talking_about("paul chiusano") =~ "Unison")
69 | end
70 |
71 | test "it lists all our sponsors" do
72 | sponsors = list_sponsors
73 | assert(sponsors =~ "Pusher")
74 | assert(sponsors =~ "Black Hat")
75 | assert(sponsors =~ "pusher.com")
76 | end
77 |
78 | defp describe_talk(keyword) do
79 | use_cassette "speakers" do
80 | Responder.dispatch(%Query {
81 | intent: "what's the talk about %",
82 | action: "talk",
83 | text: "what's the talk about #{keyword}",
84 | params: %{
85 | "talk-keyword" => keyword
86 | }})
87 | end
88 | end
89 |
90 | defp describe_speaker(name) do
91 | use_cassette "speakers" do
92 | Responder.dispatch(%Query {
93 | intent: "who is %",
94 | action: "whois",
95 | text: "who is #{name}",
96 | params: %{
97 | "full-name" => name
98 | }})
99 | end
100 | end
101 |
102 | defp who_speaks_about(topic) do
103 | use_cassette "speakers" do
104 | Responder.dispatch(%Query {
105 | intent: "Who speaks about X",
106 | action: "whospeaksabout",
107 | text: "who speaks about #{topic}?",
108 | params: %{"talk-keyword" => topic}})
109 | end
110 | end
111 |
112 | defp whats_x_talking_about(name) do
113 | use_cassette "speakers" do
114 | Responder.dispatch(%Query {
115 | intent: "What's x talking about",
116 | action: "whatsxtalkingabout",
117 | text: "what's #{name} talking about?",
118 | params: %{"full-name" => name}})
119 | end
120 | end
121 |
122 | defp list_sponsors do
123 | use_cassette "sponsors" do
124 | Responder.dispatch(%Query {
125 | intent: "Sponsors",
126 | action: "sponsors",
127 | text: "who are your sponsors?",
128 | params: %{}})
129 | end
130 | end
131 | end
132 |
--------------------------------------------------------------------------------
/apps/bot_engine/lib/bot_engine/responder.ex:
--------------------------------------------------------------------------------
1 | defmodule BotEngine.Responder do
2 | alias BotEngine.FullStackFest
3 | alias BotEngine.Similarity
4 | alias BotEngine.Utils.Sentence
5 |
6 | defmodule Query do
7 | defstruct [:intent, :text, :action, :params]
8 | end
9 |
10 | @wats [
11 | "Can you rephrase? I'm not sure what you mean.",
12 | "Uhm what's that again?",
13 | "No idea what you mean."
14 | ]
15 |
16 | def dispatch(%Query{action: "agenda"}) do
17 | "Check out the full agenda at https://2016.fullstackfest.com/agenda."
18 | end
19 |
20 | def dispatch(%Query{action: "accommodation"}) do
21 | "The venue is so cool that there are 3 hotels within 5 minutes walking distance! Check out https://2016.fullstackfest.com/tickets/#accommodation to see the available options."
22 | end
23 |
24 | # TODO: During the conference, provide a phone number
25 | def dispatch(%Query{action: "contact"}) do
26 | "You can contact the organizers through conferences@codegram.com or via Twitter (@fullstackfest)"
27 | end
28 |
29 | def dispatch(%Query{action: "discount"}) do
30 | "Just because it's you. Use the code FMASTER for a 5% discount or just go to https://ti.to/codegram/full-stack-fest-2016/discount/FMASTER"
31 | end
32 |
33 | def dispatch(%Query{action: "commands"}) do
34 | "Ask me about Full Stack Fest's agenda, speakers, contact information, a specific talk topic you'd like to know more about -- pretty much anything. Don't be shy."
35 | end
36 |
37 | def dispatch(%Query{action: "buy"}) do
38 | "Looking for tickets? That's great! You can get more information about available tickets at https://2016.fullstackfest.com/tickets/ or buy them here: https://ti.to/codegram/full-stack-fest-2016"
39 | end
40 |
41 | # TODO: During the conference, point people to the right party.
42 | def dispatch(%Query{action: "party"}) do
43 | "There will be, not one, but two parties! And also two meetups! Every day after the talks you get to hang out with everyone! Check out the agenda https://2016.fullstackfest.com/agenda/ and the venues: https://2016.fullstackfest.com/tickets/#venue"
44 | end
45 |
46 | def dispatch(%Query{action: "sponsoring"}) do
47 | "Nice to hear you're interested in sponsoring us! You can check our sponsorship packages here: https://2016.fullstackfest.com/sponsors/"
48 | end
49 |
50 | def dispatch(%Query{action: "sponsors"}) do
51 | list_sponsors
52 | end
53 |
54 | def dispatch(%Query{action: "whois", params: %{"full-name" => fullname}}) do
55 | case lookup_speaker(fullname) do
56 | {:none, _} -> i_dont_know(fullname)
57 | {:one, speaker} -> describe_speaker(speaker)
58 | {:many, speakers} -> disambiguate(speakers, &(&1["name"]))
59 | end
60 | end
61 |
62 | def dispatch(%Query{action: "whospeaksabout", params: %{"talk-keyword" => keyword}}) do
63 | case lookup_talk(keyword) do
64 | {:none, _} ->
65 | "I can't recall any talk about #{keyword}, but definitely double-check on the agenda: https://2016.fullstackfest.com/agenda"
66 | {:one, speaker} -> describe_speaker(speaker)
67 | {:many, speakers} -> disambiguate(speakers, &(&1["talk"]["title"]))
68 | end
69 | end
70 |
71 | def dispatch(%Query{action: "whatsxtalkingabout", params: %{"full-name" => fullname}}) do
72 | case lookup_speaker(fullname) do
73 | {:none, _} -> i_dont_know(fullname)
74 | {:one, speaker} -> describe_talk(speaker)
75 | {:many, speakers} -> disambiguate(speakers, &(&1["name"]))
76 | end
77 | end
78 |
79 | def dispatch(%Query{action: "talk", params: %{"talk-keyword" => keyword}}) do
80 | case lookup_talk(keyword) do
81 | {:none, _} ->
82 | "I can't recall any talk about #{keyword}, but definitely double-check on the agenda: https://2016.fullstackfest.com/agenda"
83 | {:one, speaker} -> describe_talk(speaker)
84 | {:many, speakers} -> disambiguate(speakers, &(&1["talk"]["title"]))
85 | end
86 | end
87 |
88 | def dispatch(_), do: wat
89 |
90 | defp list_sponsors do
91 | resp = FullStackFest.get!("/sponsors.json").body["categories"] |>
92 | Enum.reject(&(Enum.empty?(&1["sponsors"]))) |>
93 | Enum.map(fn(%{"sponsors" => sponsors, "name" => category_name}) ->
94 | formatted_sponsors = sponsors |> Enum.map(fn(%{"name" => name, "website" => website}) ->
95 | "#{name} (#{website})"
96 | end) |> Sentence.to_sentence
97 | formatted_category = category_name |>
98 | String.split("_") |>
99 | Enum.map(&String.capitalize/1) |>
100 | Enum.join(" ")
101 |
102 | "Our #{formatted_category} sponsors are #{formatted_sponsors}."
103 | end) |>
104 | Enum.join(" ")
105 |
106 | resp <> " We're very lucky to have them :)"
107 | end
108 |
109 | defp lookup_talk(query) do
110 | speakers = FullStackFest.get!("/speakers.json").body["speakers"] |>
111 | Enum.reject(fn(%{"talk" => %{"title" => title}}) -> String.contains?(title, "Master of Cerimonies") end)
112 |
113 | Similarity.query(query, speakers,
114 | fn(query, %{"talk" => %{"title" => title, "description" => description, "keywords" => keywords}}) ->
115 | talk_similarity(query, title, description, keywords)
116 | end,
117 | %{min_confidence: 0.5,
118 | max_distance_from_best: 0.1}
119 | )
120 | end
121 |
122 | defp lookup_speaker(name) do
123 | speakers = FullStackFest.get!("/speakers.json").body["speakers"]
124 | Similarity.query(name, speakers,
125 | fn(name, %{"name" => speaker_name}) ->
126 | String.jaro_distance(String.downcase(name), String.downcase(speaker_name))
127 | end,
128 | %{min_confidence: 0.7,
129 | max_distance_from_best: 0.2}
130 | )
131 | end
132 |
133 | defp describe_speaker(%{"tagline" => tagline,
134 | "name" => name,
135 | "slug" => slug,
136 | "twitter" => twitter,
137 | "talk" => %{ "title" => talk_title }} = talk) do
138 | "#{name} (#{tagline}) will be speaking about #{talk_title}. " <>
139 | "Read more about them at https://2016.fullstackfest.com/speakers/#{slug} ." <>
140 | (if twitter, do: " Oh, and you should follow them on twitter at #{twitter} !", else: "") <>
141 | (if talk["interview"], do: " Their interview is worth a read as well: #{talk["interview"]}", else: "")
142 | end
143 |
144 | defp describe_talk(%{"name" => name,
145 | "talk" => %{"description" => description,
146 | "title" => talk_title}}) do
147 | "#{name} is going to talk about #{talk_title}." <>
148 | (if String.length(description) != 0 do
149 | " Here's the description of the talk: \"#{description}\"."
150 | else
151 | ""
152 | end)
153 | end
154 |
155 | defp wat do
156 | Enum.random(@wats)
157 | end
158 |
159 | defp i_dont_know(name) do
160 | Enum.random([
161 | "I don't think I've ever heard of #{name}... I'm sure they're great fun though.",
162 | "Uhm... #{name}? I'm afraid if you're not talking about that TV presenter from the mid-80s, I don't know who that is.",
163 | "The name rings a bell, but I don't think I've ever met #{name}, sorry."
164 | ])
165 | end
166 |
167 | defp disambiguate([value | values], show_function) do
168 | alternatives = (values |>
169 | Enum.map(&(show_function.(&1))) |>
170 | Enum.join(", ")) <>
171 | " or #{show_function.(value)}"
172 | "Do you mean #{alternatives}? Ask me again."
173 | end
174 |
175 | defp talk_similarity(query, title, description, keywords) do
176 | title_distance = String.jaro_distance(String.downcase(query), String.downcase(title))
177 | title_fsum = 1 - ( 1 / (1 + (Similarity.Text.freq_sum(query, title) * 2)))
178 | desc_fsum = 1 - ( 1 / (1 + (Similarity.Text.freq_sum(query, description))))
179 | keywords_fsum = 1 - (1 / (1 + Similarity.Text.freq_sum(query, keywords) * 5))
180 |
181 | title_fsum * 0.5 + title_distance * 0.2 + desc_fsum * 0.3 + keywords_fsum
182 | end
183 | end
184 |
--------------------------------------------------------------------------------
/apps/bot_engine/fixture/vcr_cassettes/speakers.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "request": {
4 | "body": "",
5 | "headers": [],
6 | "method": "get",
7 | "options": [],
8 | "request_body": "",
9 | "url": "https://2016.fullstackfest.com/speakers.json"
10 | },
11 | "response": {
12 | "body": "{\n \"speakers\": [\n {\n \"talk\": {\n \"title\": \"Front-end Master of Cerimonies\",\n \"description\": \"\",\n \"keywords\": \"presenter mc master of cerimonies\"\n },\n \"name\": \"Bruce Lawson\",\n \"slug\": \"bruce-lawson\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/brucel\",\n \"tagline\": \"Master of Ceremonies. Web standards lovegod. Co-wrote Introducing HTML5.\"\n }\n ,\n\n {\n \"website\": \"http://joearms.github.io/\",\n \"interview\": \"https://medium.com/@FullStackFest/interviewing-joe-armstrong-8b7d2023d975\",\n \"talk\": {\n \"title\": \"Keynote\",\n \"description\": \"\",\n \"keywords\": \"erlang resiliency software architecture\"\n },\n \"name\": \"Joe Armstrong\",\n \"slug\": \"joe-armstrong\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/joeerl\",\n \"tagline\": \"The Erlang language & platform co-creator.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"The future of ES6\",\n \"description\": \"\",\n \"keywords\": \"javascript es6\"\n },\n \"name\": \"Jafar Husain\",\n \"slug\": \"jafar-husain\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/jhusain\",\n \"tagline\": \"Architect of the Falcor protocol. Member of the TC39.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Back-end Master of Cerimonies\",\n \"description\": \"\",\n \"keywords\": \"mc master of cerimonies\"\n },\n \"name\": \"Rachel Laycock\",\n \"slug\": \"rachel-laycock\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/rachellaycock\",\n \"tagline\": \"Master of Ceremonies. Head of Technology of ThoughtWorks North America. Editor-in-Chief of the P2 Magazine.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Unison: a new programming platform\",\n \"description\": \"Unison is a new programming language and platform that rethinks just about every aspect of the programming experience, with the goal of achieving huge increases in productivity. Paul believes much of the complexity that arises when building large systems is inessential and can be sidestepped just by making different foundational assumptions. In this talk Paul will give an overview of Unison and show a demo of it in action.\",\n \"keywords\": \"unison productivity language design user experience\"\n },\n \"name\": \"Paul Chiusano\",\n \"slug\": \"paul-chiusano\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/pchiusano\",\n \"tagline\": \"Unison project creator, and author of Functional Programming in Scala.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"The Physical Web: building on top of the open web\",\n \"description\": \"The Physical Web is a very simple idea: find URLs around you easily. However, this unassuming idea unlocks a whole new open ecosystem. As the web gets better, it makes the Physical Web look brilliant, but honestly, it's the web that's being awesome, the Physical Web is just getting a free ride. This talk will discuss some of the amazing concepts and future directions from the web community that are making the Physical Web look so promising.\",\n \"keywords\": \"physical web iot\"\n },\n \"name\": \"Scott Jenson\",\n \"slug\": \"scott-jenson\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/scottjenson\",\n \"tagline\": \"Interface design guru. Leader of the Physical Web project.\"\n }\n ,\n\n {\n \"website\": \"http://jackfranklin.co.uk/\",\n \"interview\": \"https://medium.com/@FullStackFest/interviewing-jack-franklin-b563660cc012#.cxtkn0c0d\",\n \"talk\": {\n \"title\": \"Confident Frontend with Elm\",\n \"description\": \"In this talk, we'll explore Elm, the programming language that brings an entirely new approach to front-end development. We'll study the language but, more importantly, the characteristics that make it such a great language to build reliable, robust client-side applications and how we can take these properties and apply them to JavaScript applications.\",\n \"keywords\": \"elm javascript transpiling frontend\"\n },\n \"name\": \"Jack Franklin\",\n \"slug\": \"jack-franklin\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/jack_franklin\",\n \"tagline\": \"Elm architecture\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Reasoning about Performance in Distributed Systems\",\n \"description\": \"Ines will explore performance in distributed systems (more details to be announced).\",\n \"keywords\": \"performance distributed systems microservices\"\n },\n \"name\": \"Ines Sombra\",\n \"slug\": \"ines-sombra\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/randommood\",\n \"tagline\": \"Distributed systems & scalability.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"CSS4 Grid: True Layout Finally Arrives\",\n \"description\": \"For years, front-end developers fumbled with hacking floats for layouts, often as part of a grid system. Media queries made these float-based grids more responsive to different screen dimensions, but unfortunately, the behaviors weren't always granular enough to work well with complex layouts. Now with the new Grid specification in CSS4, we can easily define behaviors for each cell in our layouts in two dimensions. When combined with media queries, we can specify where each cell will be placed under a variety of conditions, in horizontal and vertical space. Similarities and differences with Flexbox will also be identified and discussed.\",\n \"keywords\": \"css4 css grids layout ux user experience\"\n },\n \"name\": \"Jen Kramer\",\n \"slug\": \"jen-kramer\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/jen4web\",\n \"tagline\": \"CSS4 grids, Harvard University Extension School\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Distributed Apps with IPFS\",\n \"description\": \"The InterPlanetary File System (IPFS) is a new hypermedia distribution protocol, addressed by content and identities. IPFS enables the creation of completely distributed applications. It aims to make the web faster, safer, and more open. IPFS enables completely decentralized and distributed apps. And it now supports fully dynamic apps, like real-time chat! This talk breaks down how to build a dynamic app on top of IPFS with CRDTs, pub/sub, and slick UIs. It also delves into new models for distributed computation, and the ethical importance of distributing the web.\",\n \"keywords\": \"ipfs crdts distributed computation hypermedia protocol\"\n },\n \"name\": \"Juan Benet\",\n \"slug\": \"juan-benet\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/juanbenet\",\n \"tagline\": \"InterPlanetary File System (IPFS) creator. Obsessed with Knowledge, Science, and Technology.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Lessons Learned from Scaling by an Order of Magnitude\",\n \"description\": \"More information to be announced soon.\",\n \"keywords\": \"\"\n },\n \"name\": \"Caitie McCaffrey\",\n \"slug\": \"caitie-mccaffrey\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/caitie\",\n \"tagline\": \"Distributed Systems Diva at Twitter. Orleans Platform.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"See the data flowing through your app\",\n \"description\": \"What if instead of building your own mental model of how data flows through your application, you could actually see the data flowing in real-time? In this talk we will explore functional and reactive streams as a building block in JavaScript applications, with tools like RxJS, Cycle.js, xstream, which enable DevTools from the future.\",\n \"keywords\": \"\"\n },\n \"name\": \"André Staltz\",\n \"slug\": \"andre-staltz\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/staltz\",\n \"tagline\": \"FRP guru. Author of the Cycle.js framwework. Introduction to Rx fame.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Unikernels\",\n \"description\": \"\",\n \"keywords\": \"docker unikernels containers\"\n },\n \"name\": \"Amir Chaudhry\",\n \"slug\": \"amir-chaudhry\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/amirmc\",\n \"tagline\": \"Unikernels at Docker. See unikernel.org and mirage.io\"\n }\n ,\n\n {\n \"website\": \"\",\n \"interview\": \"https://medium.com/@FullStackFest/interviewing-jf-bastien-29e0e6de6cd6\",\n \"talk\": {\n \"title\": \"WebAssembly: birth of a virtual ISA\",\n \"description\": \"WebAssembly is a new portable, size- and load-time-efficient format suitable for compilation to the web. It's efficient, fast, portable, and safe—as is the rest of the web—and the devil of how this is done is in the details. Let's walk through the diverse tech stacks which makes this possible, from high-level languages such as C++, to compilers, virtual machines, and hardware implementations.\",\n \"keywords\": \"web assembly browser\"\n },\n \"name\": \"JF Bastien\",\n \"slug\": \"jf-bastien\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/jfbastien\",\n \"tagline\": \"Just-in-time compiler on Google Chrome. Web Assembly. C++ standards committee punster.\"\n }\n ,\n\n {\n \"website\": \"http://sirupsen.com/\",\n \"interview\": \"https://medium.com/@FullStackFest/interviewing-simon-eskildsen-95080120a46d\",\n \"talk\": {\n \"title\": \"Shopify in Multiple Datacenters\",\n \"description\": \"How do you take the biggest commerce platform in the world, with hundreds of thousands of shops and make it run out of multiple locations? We'll go through how Shopify went from one datacenter, to a passive disaster recovery site all the way to to running shops out of multiple datacenters concurrently. The talk will cover how we move shops between data centers and perform failovers with the click of a button. We'll share the patterns that are widely useful, such as using load balancers to sort requests around the world to the right data centers.\",\n \"keywords\": \"scalability microservices scaling datacenters docker\"\n },\n \"name\": \"Simon Eskildsen\",\n \"slug\": \"simon-eskildsen\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/sirupsen\",\n \"tagline\": \"Leading Shopify's core architecture team.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Serverless architecture\",\n \"description\": \"\",\n \"keywords\": \"serverless\"\n },\n \"name\": \"Austen Collins\",\n \"slug\": \"austen-collins\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/austencollins\",\n \"tagline\": \"Creator of the Serverless Framework, founder of Serverless, Inc.\"\n }\n ,\n\n {\n \"website\": \"\",\n \"interview\": \"https://medium.com/@FullStackFest/interviewing-liv-erickson-557dfe685139#.955c28fqt\",\n \"talk\": {\n \"title\": \"Virtual Reality is Here, in your Browser\",\n \"description\": \"2016 has brought the launch of consumer-level desktop virtual reality technologies for the first time, and how we experience data and information is shifting into 3D. Today's web technologies are shaping the future of the VR web and bringing immersive experiences to support cross-platform, device-agnostic virtual reality experiences right in the browser. In this talk, we'll cover the experimental WebVR API and how new libraries and frameworks are developing to support immersive technologies that work on devices from Google Cardboard to the Oculus Rift, as well as how new virtual reality applications are using JavaScript and WebGL to enable user-generated content.\",\n \"keywords\": \"vr virtual reality javascript\"\n },\n \"name\": \"Liv Erickson\",\n \"slug\": \"liv-erickson\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/misslivirose\",\n \"tagline\": \"Virtual and Augmented Reality Developer Evangelist at Microsoft.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"From REST to GraphQL\",\n \"description\": \"As our applications grow larger, our REST architecture often becomes unmanageable. Custom endpoints and over/under fetching all become part of your life as a developer. Leave that behind and welcome GraphQL, a declarative and hierarchal way to fetch data, with just one endpoint to rule them all.\",\n \"keywords\": \"REST GraphQL\"\n },\n \"name\": \"Marc-Andre Giroux\",\n \"slug\": \"marc-andre-giroux\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/__xuorig__\",\n \"tagline\": \"GraphQL & Relay\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Beyond The Tab: Executing JavaScript Across Browser Contexts\",\n \"description\": \"Keeping JavaScript from interfering across tabs is great, but what about when a web application wants to share state without a server? You'll leave this talk with enough knowledge to get started with SharedWorkers, ServiceWorkers, and other techniques - and enough wisdom to know when to use them.\",\n \"keywords\": \"shared workers service workers browser javascript context\"\n },\n \"name\": \"Andrew Dunkman\",\n \"slug\": \"andrew-dunkman\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/adunkman\",\n \"tagline\": \"SharedWorkers, ServiceWorkers & other techniques.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"How to build a website that will (eventually) work on Mars?\",\n \"description\": \"At not so distant future human race will be able to make a colony on Mars, and we'll need a stable communication between the planets. Interplanetary internet works differently (ie. 3.5 to 22 mins delay between planets), so what from today's technologies can we use to build an interplanetary web app?\",\n \"keywords\": \"offline future communication latency\"\n },\n \"name\": \"Slobodan Stojanovic\",\n \"slug\": \"slobodan-stojanovic\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/slobodan_\",\n \"tagline\": \"Offline web apps, JavaScript & all things in between.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Taming the Asynchronous Beast with CSP Channels in JavaScript\",\n \"description\": \"Software deals with complex control flow mechanisms like callbacks, promises, events, and streams. Chosen solution has a deep impact on your code. Things can be simplified to a single abstraction since the underlying problem to all of this is the same, with CSP and the concept of channels.\",\n \"keywords\": \"csp nodejs streams channels\"\n },\n \"name\": \"Vincenzo Chianese\",\n \"slug\": \"vincenzo-chianese\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/D3DVincent\",\n \"tagline\": \"CSP Channels & NodeJS\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Make Reactive Programming on the Server Great Again\",\n \"description\": \"Reactive front ends are on the rise in the web platform, especially on the client side. But what about the server side? ABy using reactive techniques, we can rate limit, gracefully handle errors, handle timeouts, and even do real-time data analysis. Together we can make the server great again!\",\n \"keywords\": \"server reactive rxjs programming javascript\"\n },\n \"name\": \"Matthew Podwysocki\",\n \"slug\": \"matthew-podwysocki\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/mattpodwysocki\",\n \"tagline\": \"Reactive Extensions for JavaScript\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"High availability with Elixir and Erlang\",\n \"description\": \"This talk aims to present the most important ideas behind high availability support in Elixir/Erlang. It is a medium to high-level explanation of tools and approaches that can help developers drastically increase the uptime of their systems.\",\n \"keywords\": \"elixir erlang high availability resiliency uptime\"\n },\n \"name\": \"Saša Jurić\",\n \"slug\": \"sasa-juric\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/sasajuric\",\n \"tagline\": \"Elixir in Action author\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Best Practices on building a UI component library for your company\",\n \"description\": \"Learn how to building an internal component library & style guide can help your company ship and iterate faster. This talk will cover how we created a scalable & maintainable UI library (http://ux.mulesoft.com) with ES6, React, and PostCSS for consumption across multiple product teams.\",\n \"keywords\": \"javascript ui component library maintainability es6 react postcss\"\n },\n \"name\": \"David Wells\",\n \"slug\": \"david-wells\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/DavidWells\",\n \"tagline\": \"JavaScript, Node, HTML, CSS, Marketing, UI and UX\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Personalising the Web\",\n \"description\": \"Personalising your content is valuable: it keeps users around and encourages interaction. Traditionally this has been hard, but tools exist to make recommendation engines easy. We introduce these tools, and talk about how to interact with them from the web to customise users' experiences.\",\n \"keywords\": \"artificial intelligence ai machine learning\"\n },\n \"name\": \"David Simons\",\n \"slug\": \"david-simons\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/SwamWithTurtles\",\n \"tagline\": \"Machine Learning, UX customization.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Whirlwind tour through the HTTP2 spec\",\n \"description\": \"HTTP2 is already here, but apparently we are not using it. Learn why it's awesome. How we can boost website performance up to 50%. The missing pieces of the puzzle? The glue between the apps and frameworks to the webserver, for features like server push and getting the asset pipeline out of our way!\",\n \"keywords\": \"http2 server performance protocols\"\n },\n \"name\": \"Ole Michaelis\",\n \"slug\": \"ole-michaelis\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/Codestars\",\n \"tagline\": \"HTTP2, performance, real-time.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"NeuroJavaScript\",\n \"description\": \"Come see a demonstration on how to interact with an open-source brain-computer interface via JavaScript. Alex has been working with the OpenBCI team in order to visualize and interpret brainwaves in the browser with Angular. Find out how your thoughts are captured and how to get involved in the NeuroTech community.\",\n \"keywords\": \"iot brain waves angular\"\n },\n \"name\": \"Alex Castillo\",\n \"slug\": \"alex-castillo\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/castillo__io\",\n \"tagline\": \"Brainwaves on Angular\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"How secure are Docker containers?\",\n \"description\": \"During this talk, Ben will share his experiences and investigate Docker and it's security model. The aim is to answer the question - “How secure are Docker containers?”\",\n \"keywords\": \"docker security\"\n },\n \"name\": \"Ben Hall\",\n \"slug\": \"ben-hall\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/ben_hall\",\n \"tagline\": \"Docker, Linux, Monitoring & Security.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Low-res NLP for your everyday life\",\n \"description\": \"NLP can be a scary piece of jargon. In reality, you can get a far distance with loops, functions, and if statements, and still manage to create a delightful chat interface. This talk will cover low-res ways to incorporate NLP so you can get more bang for your buck without needing full AI.\",\n \"keywords\": \"nlp bots ai\"\n },\n \"name\": \"Duretti Hirpa\",\n \"slug\": \"duretti-hirpa\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/duretti\",\n \"tagline\": \"NLP, chat, human-computer interface.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"What did AlphaGo do to beat the strongest human Go player?\",\n \"description\": \"This year AlphaGo shocked the world by decisively beating the strongest human Go player, Lee Sedol. An accomplishment that wasn't expected for years to come. How did AlphaGo do this? What algorithms did it use? What advances in AI made it possible? This talk will answer these questions.\",\n \"keywords\": \"alphago ai artificial intelligence\"\n },\n \"name\": \"Tobias Pfeiffer\",\n \"slug\": \"tobias-pfeiffer\",\n \"track\": \"back\",\n \"twitter\": \"https://twitter.com/PragTob\",\n \"tagline\": \"Teacher and agile craftsman by passion. Shoes (Ruby library) maintainer.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Reactive Reality\",\n \"description\": \"Building User Interfaces is hard. Building them in 3D worlds (virtual reality) is harder. Building them mixing 2D and 3D elements in a web browser is literally a technology frontier project. Doing it maintaining the API and developer friendliness of ReactJS... is what I'll show!\",\n \"keywords\": \"virtual reality vr 3d javascript\"\n },\n \"name\": \"Massimiliano Mantione\",\n \"slug\": \"massimiliano-mantione\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/M_a_s_s_i\",\n \"tagline\": \"Languages & compilers. Worked at the V8 team - interested in all things JavaScript.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"The Frontend Is a Full Stack\",\n \"description\": \"Web pages are dead, very dead: long live apps! The web is the land of the paradigm shift and Frontend Developers are mutant ninjas. Today's frontends embody the UI, the View Logic, some Business Logic and some Storage. This is a Full Stack on its own, on the client. Modern, component-oriented UI frameworks, together with state-action paradigms (like Flux and its most popular implementation, Redux) have formalized a lot of new concepts. And this radically changes the way we think our architecture. How do we distribute our business logic? How do we keep things in sync? Do we actually need a backend? How do we keep components reusable? A lot of new practices have emerged and we feel that Relay will be the next big thing. We are shifting to a development environment that will give us more and more resources to focus on what really matters: the user, the experience, the product. Let's get ready for the next leap.\",\n \"keywords\": \"full stack frontend flux redux relay\"\n },\n \"name\": \"Luca Marchesini\",\n \"slug\": \"luca-marchesini\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/xbill82\",\n \"tagline\": \"Full stack engineer: Docker, ElasticSearch, Redis, Webpack, AngularJS, VueJS.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Clients in control: building demand-driven systems with Om Next\",\n \"description\": \"Traditional architectures are no longer suitable for the increasing needs of today's applications. The price is often paid in high bandwidth and reduced performance. Demand-driven design enables clients to request arbitrary data on demand. Companies like Facebook and Netflix have switched to demand-driven architectures to better embrace a great variety of continuously changing clients. Solutions like Relay and Falcor/JSONGraph distill such ideas. Om Next builds on, and extends these concepts further, to provide a Clojure(Script) based solution. In this talk, I present the motivation for a demand-driven approach and explore the benefits and tradeoffs that Om Next brings to the table.\",\n \"keywords\": \"om next falcor jsongraph demand driven clojurescript clojure\"\n },\n \"name\": \"António Nuno Monteiro\",\n \"slug\": \"antonio-nuno-monteiro\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/anmonteiro90\",\n \"tagline\": \"Clojure enthusiast. Om contributor.\"\n }\n ,\n\n {\n \"talk\": {\n \"title\": \"Immutable User Interfaces\",\n \"description\": \"One of the greatest challenges of building a rich UI is keeping track of all that is changing: incoming touch and mouse events, new data from your servers, animations, and more. Here we propose a new way to tackle this challenge that is as old as computing itself: don't let anything change in the first place. Come learn about how to build rich and highly performant UIs without losing your sanity by leveraging immutable data and the optimization techniques it enables.\",\n \"keywords\": \"immutable ui user interface graphql react\"\n },\n \"name\": \"Lee Byron\",\n \"slug\": \"lee-byron\",\n \"track\": \"front\",\n \"twitter\": \"https://twitter.com/leeb\",\n \"tagline\": \"React, GraphQL, Immutable.js, Mobile, JavaScript, Nonsense.\"\n }\n\n ]\n}\n",
13 | "headers": {
14 | "Content-Type": "application/json",
15 | "Content-Length": "25858",
16 | "Connection": "keep-alive",
17 | "Date": "Fri, 10 Jun 2016 09:22:07 GMT",
18 | "Cache-Control": "max-age=31536000",
19 | "x-amz-meta-content-md5": "293293a99d843a2a0ef5a6ed7130d997",
20 | "Last-Modified": "Fri, 10 Jun 2016 08:50:00 GMT",
21 | "ETag": "\"293293a99d843a2a0ef5a6ed7130d997\"",
22 | "Server": "AmazonS3",
23 | "Vary": "Accept-Encoding",
24 | "X-Cache": "Miss from cloudfront",
25 | "Via": "1.1 9284c66b76d02817071db4ac5242ee72.cloudfront.net (CloudFront)",
26 | "X-Amz-Cf-Id": "lMOCCqY1mUQWPgDpf7kZNQIcNn4-bhxT9hgrpp7hInvGM3V5bfAF-Q=="
27 | },
28 | "status_code": 200,
29 | "type": "ok"
30 | }
31 | }
32 | ]
33 |
--------------------------------------------------------------------------------