diff options
author | Paul Duncan <pabs@pablotron.org> | 2025-03-07 19:13:17 -0500 |
---|---|---|
committer | Paul Duncan <pabs@pablotron.org> | 2025-03-07 19:13:17 -0500 |
commit | 75539a6e72a58a3ec2600a1aa902790660e09791 (patch) | |
tree | e15f4db3f8ce3ba5c356793887328273de829b9b | |
download | jim-bot-75539a6e72a58a3ec2600a1aa902790660e09791.tar.xz jim-bot-75539a6e72a58a3ec2600a1aa902790660e09791.zip |
initial commitr20250307
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Dockerfile | 16 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | bot/bot.go | 546 | ||||
-rw-r--r-- | bot/bot_test.go | 104 | ||||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | main.go | 26 |
7 files changed, 704 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d01e8f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +./jim-bot diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a9abbab --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +# build stage +FROM docker.io/golang:1.24.1-alpine AS build +COPY . /src +WORKDIR /src + +# cache /go between builds to cache packages and improve build speed +RUN --mount=type=cache,target=/go ["go", "build", "-trimpath", "-ldflags=-s -w"] + +# run stage +# +# this stage used to use "FROM scratch", but we need ca-certificates in +# order to connect to the NWS API. +FROM gcr.io/distroless/static +ENV TZ "America/New_York" +COPY --from=build /src/jim-bot /jim-bot +CMD ["/jim-bot"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af99727 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ + +@PHONY=all app + +all: + go build -trimpath -ldflags='-s -w' + +clean: + go clean diff --git a/bot/bot.go b/bot/bot.go new file mode 100644 index 0000000..233a865 --- /dev/null +++ b/bot/bot.go @@ -0,0 +1,546 @@ +// Port of jim-bot from C to go. +package bot + +import ( + "math/rand/v2" + "strings" +) + +// adverb word list +var adverbs = []string { + "appropriately", + "assertively", + "authoritatively", + "collaboratively", + "compellingly", + "competently", + "completely", + "continually", + "conveniently", + "credibly", + "distinctively", + "dramatically", + "dynamically", + "efficiently", + "energistically", + "enthusiastically", + "fungibly", + "globally", + "holisticly", + "interactively", + "intrinsicly", + "monotonectally", + "objectively", + "phosfluorescently", + "proactively", + "professionally", + "progressively", + "quickly", + "rapidiously", + "seamlessly", + "synergistically", + "uniquely", +} + +// verb word list +var verbs = []string { + "actualize", + "administrate", + "aggregate", + "architect", + "benchmark", + "brand", + "build", + "cloudify", + "communicate", + "conceptualize", + "coordinate", + "create", + "cultivate", + "customize", + "deliver", + "deploy", + "develop", + "dinintermediate", + "disseminate", + "drive", + "embrace", + "e-enable", + "empower", + "enable", + "engage", + "engineer", + "enhance", + "envisioneer", + "evisculate", + "evolve", + "expedite", + "exploit", + "extend", + "fabricate", + "facilitate", + "fashion", + "formulate", + "foster", + "generate", + "grow", + "harness", + "ideate", + "impact", + "implement", + "incentivize", + "incubate", + "instantiate", + "initiate", + "innovate", + "integrate", + "iterate", + "leverage existing", + "leverage other's", + "maintain", + "matrix", + "maximize", + "mesh", + "monetize", + "morph", + "myocardinate", + "negotiate", + "network", + "optimize", + "orchestrate", + "parallel task", + "plagiarize", + "pontificate", + "predominate", + "productivate", + "productize", + "promote", + "provide access to", + "pursue", + "recaptiualize", + "reconceptualize", + "redefine", + "re-engineer", + "reintermediate", + "reinvent", + "repurpose", + "restore", + "revolutionize", + "scale", + "seize", + "simplify", + "strategize", + "streamline", + "supply", + "syndicate", + "synergize", + "synthesize", + "target", + "transform", + "transition", + "underwhelm", + "unleash", + "utilize", + "visualize", + "whiteboard", +} + +// adjective word list +var adjectives = []string { + "24/7", + "24/365", + "accurate", + "adaptive", + "alternative", + "an expanded array of", + "B2B", + "B2C", + "backend", + "backward-compatible", + "best-of-breed", + "bleeding-edge", + "bricks-and-clicks", + "business", + "clicks-and-mortar", + "client-based", + "client-centered", + "client-centric", + "client-focused", + "collaborative", + "compelling", + "competitive", + "cooperative", + "corporate", + "cost effective", + "covalent", + "cross functional", + "cross-media", + "cross-platform", + "cross-unit", + "customer directed", + "customized", + "cutting-edge", + "distinctive", + "distributed", + "diverse", + "dynamic", + "e-business", + "economically sound", + "effective", + "efficient", + "emerging", + "empowered", + "enabled", + "end-to-end", + "enterprise", + "enterprise-wide", + "equity invested", + "error-free", + "ethical", + "excellent", + "exceptional", + "extensible", + "extensive", + "flexible", + "focused", + "frictionless", + "front-end", + "fully researched", + "fully tested", + "functional", + "functionalized", + "fungible", + "future-proof", + "global", + "go forward", + "goal-oriented", + "granular", + "high standards in", + "high-payoff", + "high-quality", + "highly efficient", + "holistic", + "impactful", + "inexpensive", + "innovative", + "installed base", + "integrated", + "interactive", + "interdependent", + "intermandated", + "interoperable", + "intuitive", + "just in time", + "leading-edge", + "leveraged", + "long-term high-impact", + "low-risk high-yield", + "magnetic", + "maintainable", + "market positioning", + "market-driven", + "mission-critical", + "multidisciplinary", + "multifunctional", + "multimedia based", + "next-generation", + "one-to-one", + "open-source", + "optimal", + "orthogonal", + "out-of-the-box", + "pandemic", + "parallel", + "performance based", + "plug-and-play", + "premier", + "premium", + "principle-centered", + "proactive", + "process-centric", + "professional", + "progressive", + "prospective", + "quality", + "real-time", + "reliable", + "resource sucking", + "resource maximizing", + "resource-leveling", + "revolutionary", + "robust", + "scalable", + "seamless", + "stand-alone", + "standardized", + "standards compliant", + "state of the art", + "sticky", + "strategic", + "superior", + "sustainable", + "synergistic", + "tactical", + "team building", + "team driven", + "technically sound", + "timely", + "top-line", + "transparent", + "turnkey", + "ubiquitous", + "unique", + "user-centric", + "user friendly", + "value-added", + "vertical", + "viral", + "virtual", + "visionary", + "web-enabled", + "wireless", + "world-class", + "worldwide", +} + +// noun word list +// (note: added "A.I." for 2025) +var nouns = []string { + "action items", + "alignments", + "applications", + "architectures", + "bandwidth", + "benefits", + "best practices", + "catalysts for change", + "channels", + "clouds", + "collaboration and idea-sharing", + "communities", + "content", + "convergence", + "core competencies", + "customer service", + "data", + "deliverables", + "e-business", + "e-commerce", + "e-markets", + "e-tailers", + "e-services", + "experiences", + "expertise", + "functionalities", + "fungibility", + "growth strategies", + "human capital", + "ideas", + "imperatives", + "infomediaries", + "information", + "infrastructures", + "initiatives", + "innovation", + "intellectual capital", + "interfaces", + "internal or \"organic\" sources", + "leadership", + "leadership skills", + "manufactured products", + "markets", + "materials", + "meta-services", + "methodologies", + "methods of empowerment", + "metrics", + "mindshare", + "models", + "networks", + "niches", + "niche markets", + "nosql", + "opportunities", + "\"outside the box\" thinking", + "outsourcing", + "paradigms", + "partnerships", + "platforms", + "portals", + "potentialities", + "process improvements", + "processes", + "products", + "quality vectors", + "relationships", + "resources", + "results", + "ROI", + "scenarios", + "schemas", + "services", + "solutions", + "sources", + "strategic theme areas", + "storage", + "supply chains", + "synergy", + "systems", + "technologies", + "technology", + "testing procedures", + "total linkage", + "users", + "value", + "vortals", + "web-readiness", + "web services", + "A.I.", + "blockchain", +} + +var joins = []string { + "for", + "and", + "while", + "by", + "to", +} + +var delims = []string { + " ", + ", ", + " and ", + ", and ", +} + +type Phrase struct { + HasAdverb bool // does this phrase have an adverb? + Adverb int // word position in adverbs + Verb int // word position in verbs + Adjectives []int // word positions in adjectives + Noun int // word position in nouns +} + +// convert phrase to string +func (p Phrase) String() string { + var b strings.Builder + + // append adverb + if p.HasAdverb { + b.WriteString(adverbs[p.Adverb]) + b.WriteString(" ") + } + + // append verb + b.WriteString(verbs[p.Verb]) + b.WriteString(" ") + + // append adjectives + for i, adjective := range(p.Adjectives) { + b.WriteString(adjectives[adjective]) // append adjective + + if len(p.Adjectives) == 1 { + b.WriteString(" ") // append tail " " + } else if len(p.Adjectives) == 2 { + if i == 0 { + b.WriteString(" and ") // append delimiting " and " + } else { + b.WriteString(" ") // append tail " " + } + } else if len(p.Adjectives) > 2 { + if i == len(p.Adjectives) - 2 { + b.WriteString(", and ") // append ", and " + } else if i < len(p.Adjectives) - 2 { + b.WriteString(", ") // append delimiting ", " + } else { + b.WriteString(" ") // append tail " " + } + } + } + + // append noun + b.WriteString(nouns[p.Noun]) + + // return string + return b.String() +} + +type Sentence struct { + Phrases []Phrase // phrases + Joins []int // phrase joins +} + +// get 0-5 unique adjectives +func uniqueAdjectives() []int { + num := rand.IntN(5) // number of adjectives + + lut := make(map[int]bool) + for len(lut) < num { + n := rand.IntN(len(adjectives)) + if !lut[n] { + lut[n] = true + } + } + + // build result + r := make([]int, 0, num) + for n := range(lut) { + r = append(r, n) + } + + // return result + return r +} + +// Create sentence +func NewSentence() Sentence { + phrases := make([]Phrase, 1 + rand.IntN(3)) + for i := range(len(phrases)) { + // adverb + phrases[i].HasAdverb = (rand.IntN(2) == 1) + phrases[i].Adverb = rand.IntN(len(adverbs)) + + phrases[i].Verb = rand.IntN(len(verbs)) // verb + phrases[i].Adjectives = uniqueAdjectives() // adjectives + phrases[i].Noun = rand.IntN(len(nouns)) // noun + } + + // return sentence + return Sentence { + Phrases: phrases, + Joins: rand.Perm(len(joins))[:len(phrases)], + } +} + +// Convert sentence to string. +func (s Sentence) String() string { + var b strings.Builder + for i, phrase := range(s.Phrases) { + b.WriteString(phrase.String()) // append phrase + if len(s.Phrases) > 1 && i < len(s.Phrases) - 1 { + // append space, join, space + b.WriteString(" ") + b.WriteString(joins[s.Joins[i]]) + b.WriteString(" ") + } + } + + // return string + return b.String() +} + +// Return one randomly generated sentence. +func String() string { + return NewSentence().String() +} + +// Return a slice of `num` randomly generated sentences. +func N(num int) []string { + r := make([]string, num) + for i := range(num) { + r[i] = String() + } + + return r +} diff --git a/bot/bot_test.go b/bot/bot_test.go new file mode 100644 index 0000000..122614f --- /dev/null +++ b/bot/bot_test.go @@ -0,0 +1,104 @@ +package bot + +import "testing" + +func TestPhraseString(t *testing.T) { + passTests := []struct { + val Phrase // test value + exp string // expected string + } {{ + val: Phrase { + Verb: 0, + Noun: 0, + }, + exp: "actualize action items", + }, { + val: Phrase { + HasAdverb: true, + Adverb: 1, + Verb: 2, + Noun: 3, + }, + exp: "assertively aggregate architectures", + }, { + val: Phrase { + HasAdverb: true, + Adverb: 2, + Verb: 3, + Adjectives: []int { 4 }, + Noun: 5, + }, + exp: "authoritatively architect alternative benefits", + }, { + val: Phrase { + HasAdverb: true, + Adverb: 3, + Verb: 4, + Adjectives: []int { 5, 6 }, + Noun: 7, + }, + exp: "collaboratively benchmark an expanded array of and B2B catalysts for change", + }, { + val: Phrase { + HasAdverb: true, + Adverb: 4, + Verb: 5, + Adjectives: []int { 6, 7, 8 }, + Noun: 9, + }, + exp: "compellingly brand B2B, B2C, and backend clouds", + }} + + for _, test := range(passTests) { + t.Run(test.exp, func(t *testing.T) { + got := test.val.String() + if got != test.exp { + t.Fatalf("got \"%s\", exp \"%s\"", got, test.exp) + } + }) + } +} + +func TestSentenceString(t *testing.T) { + passTests := []struct { + val Sentence // test value + exp string // expected string + } {{ + val: Sentence{ + Phrases: []Phrase{ + Phrase { + Verb: 0, + Noun: 0, + }, + }, + }, + exp: "actualize action items", + }, { + val: Sentence { + Phrases: []Phrase { + Phrase { + Verb: 0, + Noun: 0, + }, + + Phrase { + HasAdverb: true, + Adverb: 1, + Verb: 2, + Noun: 3, + }, + }, + Joins: []int { 0 }, + }, + exp: "actualize action items for assertively aggregate architectures", + }} + + for _, test := range(passTests) { + t.Run(test.exp, func(t *testing.T) { + got := test.val.String() + if got != test.exp { + t.Fatalf("got \"%s\", exp \"%s\"", got, test.exp) + } + }) + } +} @@ -0,0 +1,3 @@ +module pablotron.org/jim-bot + +go 1.23.0 @@ -0,0 +1,26 @@ +package main + +import ( + "log" + "net/http" + "strings" + + "pablotron.org/jim-bot/bot" +) + +// serve home page +func doHome(w http.ResponseWriter, _ *http.Request) { + // generate respones body + body := []byte(strings.Join(bot.N(10), "\n") + "\n") + + // write header and response + w.Header().Add("Content-Type", "text/plain") + if _, err := w.Write(body); err != nil { + log.Print(err) + } +} + +func main() { + http.HandleFunc("/", doHome) + http.ListenAndServe(":8080", nil) +} |