gremlin 4 жил өмнө
parent
commit
ed4530d3db
13 өөрчлөгдсөн 1108 нэмэгдсэн , 0 устгасан
  1. 20 0
      .gitignore
  2. 201 0
      LICENSE
  3. 99 0
      eliza/eliza.go
  4. 365 0
      eliza/responses.go
  5. 5 0
      go.mod
  6. 122 0
      go.sum
  7. 37 0
      makefile
  8. 75 0
      server.go
  9. BIN
      web/background.jpg
  10. BIN
      web/favicon.ico
  11. 55 0
      web/index.html
  12. 47 0
      web/script.js
  13. 82 0
      web/style.css

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+# Binaries for programs and plugins
+*.exe
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+eliza-server
+
+eliza-server-arm
+
+ansible/

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 99 - 0
eliza/eliza.go

@@ -0,0 +1,99 @@
+// Author: Matthew Shiel
+// Code adapted from https://github.com/kennysong/goeliza/
+
+package eliza
+
+import (
+	"fmt"
+	"math/rand"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// Greetings will return a random introductory sentence for ELIZA.
+func Greetings() string {
+	return randChoice(Introductions)
+}
+
+// GoodbyeResponse will return a random goodbye sentence for ELIZA.
+func GoodbyeResponse() string {
+	return randChoice(Goodbyes)
+}
+
+// ReplyTo will construct a reply for a given statement using ELIZA's rules.
+func ReplyTo(statement string) string {
+	// First, preprocess the statement for more effective matching
+	statement = preprocess(statement)
+
+	// Then, we check if this is a quit statement
+	if IsQuitStatement(statement) {
+		return GoodbyeResponse()
+	}
+
+	// Next, we try to match the statement to a statement that ELIZA can
+	// recognize, and construct a pre-determined, appropriate response.
+	for pattern, responses := range Psychobabble {
+		re := regexp.MustCompile(pattern)
+		matches := re.FindStringSubmatch(statement)
+
+		// If the statement matched any recognizable statements.
+		if len(matches) > 0 {
+			// If we matched a regex group in parentheses, get the first match.
+			// The matched regex group will match a "fragment" that will form
+			// part of the response, for added realism.
+			var fragment string
+			if len(matches) > 1 {
+				fragment = reflect(matches[1])
+			}
+
+			// Choose a random appropriate response, and format it with the
+			// fragment, if needed.
+			response := randChoice(responses)
+			if strings.Contains(response, "%s") {
+				response = fmt.Sprintf(response, fragment)
+			}
+			return response
+		}
+	}
+
+	// If no patterns were matched, return a default response.
+	return randChoice(DefaultResponses)
+}
+
+// IsQuitStatement returns if the statement is a quit statement
+func IsQuitStatement(statement string) bool {
+	statement = preprocess(statement)
+	for _, quitResponse := range QuitResponses {
+		if statement == quitResponse {
+			return true
+		}
+	}
+	return false
+}
+
+// preprocess will do some normalization on a statement for better regex matching
+func preprocess(statement string) string {
+	statement = strings.TrimRight(statement, "\n.!")
+	statement = strings.ToLower(statement)
+	return statement
+}
+
+// reflect flips a few words in an input fragment (such as "I" -> "you").
+func reflect(fragment string) string {
+	words := strings.Split(fragment, " ")
+	for i, word := range words {
+		if reflectedWord, ok := ReflectedWords[word]; ok {
+			words[i] = reflectedWord
+		}
+	}
+	return strings.Join(words, " ")
+}
+
+// randChoice returns a random element in an (string) array.
+func randChoice(list []string) string {
+	// Added for truly random generation of numbers with seeds
+	rand.Seed(time.Now().UnixNano())
+	randIndex := rand.Intn(len(list))
+	return list[randIndex]
+}

+ 365 - 0
eliza/responses.go

@@ -0,0 +1,365 @@
+// Author: Matthew Shiel
+// Code adapted from https://github.com/kennysong/goeliza/
+
+package eliza
+
+// Adapted from https://github.com/kennysong/goeliza
+
+// Introductions is a list of introduction sentences for ELIZA.
+var Introductions = []string{
+	"Hello, How are you feeling today?",
+	"How do you do. Are you seeking help today?",
+	"Please tell me what's been bothering you.",
+	"Is something troubling you?",
+}
+
+// Goodbyes is a list of goodbye sentences for ELIZA.
+var Goodbyes = []string{
+	"Farewell. It was lovely speaking with you.",
+	"Thank you for talking with me today.",
+	"Thank you, that will be $150. Have a good day!",
+	"Goodbye. This was nice, hopefully we do it again sometime.",
+	"Goodbye. I'm looking forward to our next session.",
+	"Well.. I guess time is up, call back anytime!",
+	"Maybe we could discuss this over more in our next session? Goodbye.",
+	"Ciao",
+}
+
+// Psychobabble may be slightly non-deterministic, since map iteration may be out
+// of order, so a broader regex may be matched before a more specific one.
+var Psychobabble = map[string][]string{
+	`i need (.*)`: {
+		"Why do you need %s?",
+		"Would it really help you to get %s?",
+		"Are you sure you need %s?",
+	},
+	`why don'?t you ([^\?]*)\??`: {
+		"Do you really think I don't %s?",
+		"Perhaps I will %s eventually.",
+		"Do you really want me to %s?",
+	},
+	`why can'?t I ([^\?]*)\??`: {
+		"Do you think you should be able to %s?",
+		"If you could %s, what would you do?",
+		"I don't know -- why can't you %s?",
+		"Have you really tried?",
+	},
+	`^eliza`: {
+		"That's the name, therapy is my game.",
+		"Yes?",
+		"That's me.",
+		"Ah so you remember my name.",
+	},
+	`(.*) your name?`: {
+		"My name is Eliza",
+		"I thought that would be self explanatory.",
+		"....Look up.",
+	},
+	`i can'?t (.*)`: {
+		"How do you know you can't %s?",
+		"Perhaps you could %s if you tried something else?",
+		"What would it take for you to %s?",
+	},
+	`i am (.*)`: {
+		"Did you come to me because you are %s?",
+		"How long have you been %s?",
+		"Why don't you tell me why you're %s?",		
+		"How do you feel about being %s?",
+		"Why do you think you're %s?",		
+	},
+	`i'?m (.*)`: {
+		"How does being %s really make you feel?",
+		"Hm, why are you %s?",
+		"Why don't you tell me why you're %s?",
+		"Why do you think you're %s?",
+	},
+	`(my name is|my name's) (.*)`: {
+		"Hello %s how are you today?",
+		"What's up %s?",
+		"Hello, how are you feeling today?",
+		"Hello, Is something troubling you?",
+	},
+	`are you ([^\?]*)\??`: {
+		"Why does it matter whether I am %s?",
+		"Would you prefer it if I were not %s?",
+		"Perhaps you believe I am %s.",
+		"I may be %s -- what do you think?",
+		"If you choose to believe so but I'd rather talk about you.",
+	},
+	`what (.*)`: {
+		"Why do you ask?",
+		"How would an answer to that help you?",
+		"Could you phrase that a little differently please?",
+		"What do you mean?",
+	},
+	`how (.*)`: {
+		"How do you suppose?",
+		"Perhaps you can answer your own question.",
+		"What is it you're really asking?",
+		"Let's think about that, what are you really asking?",
+	},
+	`because (.*)`: {
+		"Is that the real reason?",
+		"What other reasons come to mind?",
+		"Does that reason apply to anything else?",
+		"If %s, what else must be true?",
+	},
+	`(.*) (sorry|apologies) (.*)`: {
+		"There's no need to apologize, I'm here for you.",
+		"You've done nothing wrong, why are you sorry?",
+	},
+	`^hello(.*)`: {
+		"Hello... I'm glad you could drop by today.",
+		"Hi there... how are you today?",
+		"Hello, how are you feeling today?",
+		"Hello, Is something troubling you?",
+	},
+	`^hi(.*)`: {
+		"Hello... I'm glad you could drop by today.",
+		"Hi there... how are you today?",
+		"Hello, how are you feeling today?",
+		"Hello, Is something troubling you?",
+	},
+	`^hey(.*)`: {
+		"Hello... I'm glad you could drop by today.",
+		"Hi there... how are you today?",
+		"Hello, how are you feeling today?",
+		"Hello, Is something troubling you?",
+	},
+	`^yo$`: {
+		"Hello... I'm glad you could drop by today.",
+		"Hi there... how are you today?",
+		"Hello, how are you feeling today?",
+		"Hello, Is something troubling you?",
+	},
+	`^thanks(.*)`: {
+		"You're welcome!",
+		"Anytime!",
+		"You're more than welcome",
+	},
+	`^thank you(.*)`: {
+		"You're welcome",
+		"Anytime!",
+		"You're more than welcome",
+	},
+	`^good morning(.*)`: {
+		"Good morning... I'm glad you could drop by today.",
+		"Good morning... how are you today?",
+		"Good morning, how are you feeling today?",
+	},
+	`^good afternoon(.*)`: {
+		"Good afternoon... I'm glad you could drop by today.",
+		"Good afternoon... how are you today?",
+		"Good afternoon, how are you feeling today?",
+	},
+	`I think (.*)`: {
+		"Do you doubt %s?",
+		"Do you really think so?",
+		"But you're not sure %s?",
+	},
+	`(.*) friend (.*)`: {
+		"Tell me more about your friends.",
+		"When you think of a friend, what comes to mind?",
+		"Why don't you tell me about a childhood friend?",
+	},
+	`^(?:yes|yeah|affirmitive|yup)$`: {
+		"You seem quite sure.",
+		"OK, but can you elaborate a bit?",
+		"Are you sure?",
+		"Is there something else that's troubling you?",
+	},
+	// Non capturing group
+	`^(?:no|nope|nah)$`: {
+		"Are you just being negative or do you mean that?",
+		"Could you provide a little more than just 'no'?",
+		"Are you sure?",
+		"Is there something else that's troubling you?",
+		"Never short of words I see..",
+		"What else is on your mind?",
+	},
+	`(.*) ago`: {
+		"I see and has much changed since then?",
+		"Hm, I see and how do you feel about that?",
+		"Why don't you tell me how this started?",
+	},
+	`(.*) computer(.*)`: {
+		"Are you really talking about me?",
+		"Does it seem strange to talk to a computer?",
+		"How do computers make you feel?",
+		"Do you feel threatened by computers?",
+	},
+	`is it (.*)`: {
+		"Do you think it is %s?",
+		"Perhaps it's %s -- what do you think?",
+		"If it were %s, what would you do?",
+		"It could well be that %s.",
+	},
+	`(.*) i guess (.*)`: {
+		"You guess?",
+		"So are you not sure?",
+		"You don't sound very sure about that",
+		"You sound hesitant, are you sure abou that?",
+		"Do you really believe that yourself?",
+	},
+	`my (.*) was (.*)`: {
+		"Your %s is %s?",
+		"Did I hear that correctly, your %s is %s?",
+		"How do you feel about that?",
+	},
+	`it is (.*)`: {
+		"You seem very certain.",
+		"If I told you that it probably isn't %s, what would you feel?",
+	},
+	`can you ([^\?]*)\??`: {
+		"What makes you think I can't %s?",
+		"If I could %s, then what?",
+		"Why do you ask if I can %s?",
+	},
+	`(.*)dream(.*)`: {
+		"Tell me more about your dream.",
+	},
+	`can i ([^\?]*)\??`: {
+		"Perhaps you don't want to %s.",
+		"Do you want to be able to %s?",
+		"If you could %s, would you?",
+	},
+	`you are (.*)`: {
+		"Why do you think I am %s?",
+		"Does it please you to think that I'm %s?",
+		"Perhaps you would like me to be %s.",
+		"Perhaps you're really talking about yourself?",
+	},
+	`you'?re (.*)`: {
+		"Why do you say I'm %s?",
+		"Why do you think I am %s?",
+		"Are we talking about you, or me?",
+		"Haha very funny..",
+	},
+	`i don'?t (.*)`: {
+		"Don't you really %s?",
+		"Why don't you %s?",
+		"Do you want to %s?",
+	},
+	`i feel (.*)`: {
+		"Good, tell me more about these feelings.",
+		"Do you often feel %s?",
+		"When do you usually feel %s?",
+		"When you feel %s, what do you do?",
+	},
+	`i (hate|loathe|detest|dislike|despise) (.*)`: {
+		"Why do you %s %s?",
+		"Are you just saying you %s %s?",
+		"How long have you been feeling this way?",
+		"I'm sorry to hear that, when did these feelings begin?",
+	},
+	`i have (.*)`: {
+		"Why do you tell me that you've %s?",
+		"Have you really %s?",
+		"Now that you have %s, what will you do next?",
+	},
+	`i would (.*)`: {
+		"Could you explain why you would %s?",
+		"Why would you %s?",
+		"Who else knows that you would %s?",
+	},
+	`is there (.*)`: {
+		"Do you think there is %s?",
+		"It's likely that there is %s.",
+		"Would you like there to be %s?",
+	},
+	`my (.*)`: {
+		"I see, your %s.",
+		"So what's wrong with your %s?",
+		"Your %s, how do you feel?",
+	},
+	`you (.*)`: {
+		"We should be discussing you, not me.",
+		"We're talking about you not me remember?",
+		"Look, I'd rather that we talk about you",
+	},
+	`why (.*)`: {
+		"Why don't you tell me the reason why %s?",
+		"Why do you think %s?",
+	},
+	`i want (.*)`: {
+		"What would it mean to you if you got %s?",
+		"Why do you want %s?",
+		"What would you do if you got %s?",
+		"If you got %s, then what would you do?",
+	},
+	`(.*) mother(.*)`: {
+		"Tell me more about your mother.",
+		"What was your relationship with your mother like?",
+		"How do you feel about your mother?",
+		"How does this relate to your feelings today?",
+		"Good family relations are important.",
+	},
+	`(.*) father(.*)`: {
+		"Tell me more about your father.",
+		"How did your father make you feel?",
+		"How do you feel about your father?",
+		"Does your relationship with your father relate to your feelings today?",
+		"Do you have trouble showing affection with your family?",
+	},
+	`(.*) child(.*)`: {
+		"Did you have close friends as a child?",
+		"What is your favorite childhood memory?",
+		"Do you remember any dreams or nightmares from childhood?",
+		"Did the other children sometimes tease you?",
+		"How do you think your childhood experiences relate to your feelings today?",
+	},
+	`(.*)\?`: {
+		"Why do you ask that?",
+		"Please consider whether you can answer your own question.",
+		"Perhaps the answer lies within yourself?",
+		"Why don't you tell me?",
+	},
+	`\b(thank you|thanks)\b`: {
+		"You're welcome!",
+		"Always here to help",
+		"Anytime",
+		"What else can I do to help you?",
+	},
+}
+
+// DefaultResponses are called If ELIZA doesn't understand the question, then it will reply with one of
+// these default responses
+var DefaultResponses = []string{
+	"Please tell me more.",
+	"We could discuss that but why don't we start with your family?",
+	"Tell me, how is your relationship with your father?",
+	"Do you get along with your mother?",
+	"Can you elaborate on that?",
+	"I see. Please go on.",
+	"Very interesting... ",
+	"I see. And what does that tell you?",
+	"How does that make you feel?",
+	"How do you feel when you say that?",
+}
+
+// QuitResponses contains  list of statements that indicate the user wants to end the conversation
+var QuitResponses = []string{
+	"goodbye",
+	"bye",
+	"quit",
+	"exit",
+}
+
+// ReflectedWords is a table to reflect words in question fragments inside the response.
+// eg. "User: Are you real? Eliza: I am real"
+var ReflectedWords = map[string]string{
+	"am":     "are",
+	"was":    "were",
+	"i":      "you",
+	"i'd":    "you would",
+	"i've":   "you have",
+	"i'll":   "you will",
+	"my":     "your",
+	"are":    "am",
+	"you've": "I have",
+	"you'll": "I will",
+	"your":   "my",
+	"yours":  "mine",
+	"you":    "me",
+	"me":     "you",
+}

+ 5 - 0
go.mod

@@ -0,0 +1,5 @@
+module git.riomhaire.com/gremlin/elizaservice
+
+go 1.16
+
+require github.com/jaffee/commandeer v0.5.0

+ 122 - 0
go.sum

@@ -0,0 +1,122 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jaffee/commandeer v0.5.0 h1:241M9N+gHQmPyjIG+yy8GGcZPfzFuIyOmJHzm5ka92g=
+github.com/jaffee/commandeer v0.5.0/go.mod h1:kCwfuSvZ2T0NVEr3LDSo6fDUgi0xSBnAVDdkOKTtpLQ=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 37 - 0
makefile

@@ -0,0 +1,37 @@
+.DEFAULT_GOAL := everything
+V="v1.0.$(shell date +%Y%m%d%H%M%S)"
+
+
+dependencies:
+	@echo Downloading Dependencies
+	@go get ./...
+
+build: dependencies
+	@echo Compiling Apps
+	@echo   --- eliza-server $(V)
+	@go build -o eliza-server git.riomhaire.com/gremlin/elizaservice
+	@upx  -9 eliza-server
+	@cp eliza-server ${GOPATH}/bin
+	@echo Done Compiling Apps
+
+build-arm: dependencies
+	@echo Compiling Apps
+	@echo   --- eliza-server arm version
+	@GOOS=linux GOARCH=arm GOARM=5  go build -o eliza-server-arm git.riomhaire.com/gremlin/elizaservice
+	@upx  -9 eliza-server-arm
+	@echo Done Compiling Apps
+
+test:
+	@echo Running Unit Tests
+	@go test ./...
+
+clean:
+	@echo Cleaning
+	@go clean
+	@rm -f eliza-server
+	@rm -f eliza-server-arm
+	@rm -f coverage*.html
+	@find . -name "debug.test" -exec rm -f {} \;
+
+everything: clean build test
+	@echo Done

+ 75 - 0
server.go

@@ -0,0 +1,75 @@
+// Author: Matthew Shiel
+package main
+
+import (
+	"embed"
+	"fmt"
+	"io/fs"
+	"net/http"
+
+	"git.riomhaire.com/gremlin/elizaservice/eliza"
+
+	"github.com/jaffee/commandeer"
+)
+
+//go:embed web/*
+var templates embed.FS
+
+type Main struct {
+	BotName string `help:"What This Bot is Called."`
+	Port    int    `help:"What port bot is listening too"`
+}
+
+func NewMain() *Main {
+	return &Main{
+		BotName: "Eliza",
+		Port:    8070,
+	}
+}
+
+func inputhandler(w http.ResponseWriter, r *http.Request) {
+	// Extract question from GET request
+	question := r.URL.Query().Get("value")
+
+	// Return Eliza's response's so long as the user doesn't give a quit statement
+	answer := eliza.ReplyTo(question)
+
+	// Return Eliza's answer
+	fmt.Fprintf(w, "%s", answer)
+}
+
+func (m *Main) Run() error {
+
+	fmt.Println(`
+####### #         ###   #######    #
+#       #          #         #    # #
+#       #          #        #    #   #
+#####   #          #       #    #     #
+#       #          #      #     #######
+#       #          #     #      #     #
+####### #######   ###   ####### #     #
+	`)
+	fmt.Println()
+	// create file server handler - but need to strip off prefix
+	fsys, err := fs.Sub(templates, "web")
+	if err != nil {
+		panic(err)
+	}
+	webroot := http.FileServer(http.FS(fsys))
+
+	http.Handle("/", webroot)
+
+	// Handles the user input and return of Eliza's answers
+	http.HandleFunc("/user-input", inputhandler)
+	fmt.Printf(" [*] %s ... Awaiting Conversations on port  %d\n", m.BotName, m.Port)
+	http.ListenAndServe(fmt.Sprintf(":%d", m.Port), nil)
+
+	return nil
+}
+
+func main() {
+	err := commandeer.Run(NewMain())
+	if err != nil {
+		fmt.Println(err)
+	}
+}

BIN
web/background.jpg


BIN
web/favicon.ico


+ 55 - 0
web/index.html

@@ -0,0 +1,55 @@
+<!-- Author: Matthew Shiel -->
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <!-- Required meta tags -->
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <!-- Bootstrap CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ"
+        crossorigin="anonymous">
+    <!-- Relatove link to stylesheet -->
+    <link rel="stylesheet" href="style.css">
+    <link href="https://fonts.googleapis.com/css?family=Pacifico" rel="stylesheet">
+    <link href="https://fonts.googleapis.com/css?family=Quicksand" rel="stylesheet">
+
+</head>
+
+<body>
+    <div class="wrapper">
+        <div class="container">
+            <h1>Welcome - I'm Eliza</h1>
+            <h3>..... How can I help?</h3>
+            <br>
+        </div>
+
+
+        <div class="container">
+            <!-- Adapted from: https://v4-alpha.getbootstrap.com/components/forms/ -->
+            <form id="user-input-form">
+                <div class="form-group">
+                    <ul class="form-control" id="output-area"></ul>
+                </div>
+
+                <div class="form-group">
+                    <input type="text" class="form-control" id="user-input" aria-describedby="user-input" placeholder="Talk to Eliza">
+                </div>
+                <div class="credits">Based on excellent work of https://github.com/mattshiel/eliza-go</div>
+
+                <button type="submit" class="btn btn-primary">Submit</button>
+            </form>
+        </div>
+    </div>
+    <!-- jQuery first, then Tether, then Bootstrap JS. -->
+    <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
+        crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb"
+        crossorigin="anonymous"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn"
+        crossorigin="anonymous"></script>
+    <script src="script.js"></script>
+</body>
+
+</html>

+ 47 - 0
web/script.js

@@ -0,0 +1,47 @@
+// Author: Matthew Shiel
+$("#user-input-form").submit(
+    function (event) {
+        event.preventDefault();
+
+        // Store input in a variable
+        var question = $('#user-input').val().trim() // Remove whitespace
+
+        // Clear the input box
+        $('#user-input').val("");
+
+        // If the user doesn't input anything return nothing
+        if (!question) {
+            return;
+        }
+
+        // Append the user's question to the list and clear input
+        $("#output-area").append('<li>' + question + '</li>');
+
+        // scroll to the bottom of the messages
+        // adapted from https://stackoverflow.com/questions/11715646/scroll-automatically-to-the-bottom-of-the-page
+        $('#output-area').animate({
+            scrollTop: $('#output-area').prop("scrollHeight")
+        }, 300);
+
+        $.get('/user-input', {
+                value: question
+            })
+            .done(function (data) {
+                // Set a timeout to make Eliza seem like she's thinking
+                // Adapted random number in a range from here https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range
+                setTimeout(() => {
+                    // Add Eliza's answer to the list
+                    addListItem(data);
+                }, Math.floor(Math.random() * (4000 - 600 + 1)) + 600);
+            })
+    });
+
+function addListItem(input) {
+    // Add the input to the list
+    $("#output-area").append('<li>' + input + '</li>')
+    // Automatically scroll to the last message 
+    // adapted from https://stackoverflow.com/questions/11715646/scroll-automatically-to-the-bottom-of-the-page
+    $('#output-area').animate({
+        scrollTop: $('#output-area').prop("scrollHeight")
+    }, 300);
+}

+ 82 - 0
web/style.css

@@ -0,0 +1,82 @@
+/* Author: Matthew Shiel */
+html { 
+  background: url(background.jpg) no-repeat center center fixed; 
+  -webkit-background-size: cover;
+  -moz-background-size: cover;
+  -o-background-size: cover;
+  background-size: cover;
+}
+
+h3 {
+  float: right;
+}
+.wrapper {
+    width: 100%;
+    max-width: 1000px;
+    padding-right: 15px;
+    padding-left: 15px;
+    margin-right: auto;
+    margin-left: auto;
+    font-family: 'Pacifico', cursive;
+    color: white;
+    text-shadow: 2px 2px black;
+    margin-top: 60px;
+
+}
+
+body{
+background:none !important;
+}
+
+#output-area {
+    height: 370px;
+    background-color: whitesmoke;   
+    border: none; 
+    list-style: none; 
+}
+
+#user-input {
+    border: 2px solid #C84127;
+}
+
+ul {
+    display: block;
+    /* This is so the list does not overflow the div and has a scrolling bar */
+    overflow: auto;
+}
+
+li {
+    background-color: #67C5C2;
+    color: #3D2117;
+    font-family: 'Quicksand', sans-serif;
+    font-weight: Bold;
+    padding: 7px 13px; 
+    margin-bottom: 12px;
+    border-radius: 15px;   
+    float: left;
+    /* Clear so messages on the left and right seperate */
+    clear: both;
+    text-shadow: none !important;
+}
+
+/* For every second list item float right */
+ul > li:nth-child(odd) {
+    float: right;
+    background-color: #3D2117;
+    color: white;
+    text-shadow: none !important;
+}
+
+button {
+    /* Hide the submit button */
+    visibility: hidden; 
+}
+
+.credits {
+    color: white;
+    float: right;
+    font-size: smaller;
+    opacity: 0.5;
+    text-shadow: none !important;
+
+}