From 19843f416fafa6a7938062fd9b348eb6e291c864 Mon Sep 17 00:00:00 2001 From: Ethan Marshall Date: Mon, 22 Jan 2024 23:55:15 +0000 Subject: [PATCH] Initial commit --- go.mod | 3 ++ index.css | 46 +++++++++++++++++++ index.gohtml | 25 +++++++++++ index.js | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 79 +++++++++++++++++++++++++++++++++ pajen.go | 72 ++++++++++++++++++++++++++++++ pajen.pl | 70 +++++++++++++++++++++++++++++ 7 files changed, 418 insertions(+) create mode 100644 go.mod create mode 100644 index.css create mode 100644 index.gohtml create mode 100644 index.js create mode 100644 main.go create mode 100644 pajen.go create mode 100755 pajen.pl diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..375990b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/ejv2/pajen-site + +go 1.21.5 diff --git a/index.css b/index.css new file mode 100644 index 0000000..4d3b81b --- /dev/null +++ b/index.css @@ -0,0 +1,46 @@ +/* + * index.css - Pajenerator stylesheet + */ + +body { + font: 1.2em/1.62 sans-serif; + margin: 1em auto; + padding:0 .62em 3.24em; +} + +header { + padding-bottom: 15px; +} + +input[type="text"] { + padding: 4px; + min-width: 75%; + text-align: center; + margin: 10px; + + font-weight: bold; + font-size: larger; +} + +button { + font-size: large; + font-weight: bold; + color: white; + background-color: rgb(25, 135, 84); + padding: 6px; + margin-top: 4px; + cursor: pointer; +} + +button[disabled] { + cursor: not-allowed; + background-color: gray; +} + +input.highlight { + border: solid medium rgb(13, 202, 240); +} + +.loading { + cursor: wait !important; +} diff --git a/index.gohtml b/index.gohtml new file mode 100644 index 0000000..95265a5 --- /dev/null +++ b/index.gohtml @@ -0,0 +1,25 @@ + + + + + Pajenerator - The Indian Name Generator! + + + + + + +
+
+

Pajenerator - The Indian Name Generator

+
+
+ + +
+ +
+ +
+ + diff --git a/index.js b/index.js new file mode 100644 index 0000000..26a29c7 --- /dev/null +++ b/index.js @@ -0,0 +1,123 @@ +/* + * index.js - Pajenerator client code + */ + +// State +var generating = false; +var names = []; +var flashcount = 0; +var flashinterval; + +// Needed elements +let root; +let btn; +let out; + +function onready() +{ + root = document.querySelector("html") + btn = document.querySelector("#btn"); + out = document.querySelector("#name-box"); +} + +// Returns the names from the API - as many as are configured. +function get_names(onget) +{ + const error_response = ["Errar", "Prablem"]; + const uri = "/api"; + let req = new XMLHttpRequest(); + + req.onreadystatechange = function() { + if (this.readyState == 4) { + if (this.status != 200) { + onget(error_response); + } + + try { + let arr = JSON.parse(this.responseText) + onget(arr); + } catch { + console.log("bad json: "+this.responseText) + onget(error_response); + } + } + } + + req.open("GET", uri, true); + req.send(); +} + +/* + * 0.2x + 0.2e^(x-27) + * where: + * x - nth iteration + * returns - time in seconds + */ +function timeout(i) +{ + return (0.2*i) + (0.2*Math.exp(i - 27)); +} + +function cleanup_generate() +{ + // Reset button text (partially) + btn.textContent = "Do the needful"; + btn.disabled = true; + // Reset cursor + root.classList.remove("loading"); + btn.classList.remove("loading"); + out.classList.remove("loading"); + + flashinterval = setInterval(() => { + out.classList.toggle("highlight"); + if (flashcount++ >= 6) { + flashcount = 0; + generating = false; + names = []; + + clearInterval(flashinterval); + + out.classList.remove("highlight"); + btn.disabled = false; + } + }, 500) + +} + +function run_generate() +{ + for (let i = 0; i < names.length; i++) { + setTimeout(() => { + out.value = names[i]; + if (i == names.length - 1) { + cleanup_generate(); + } + }, 1000*timeout(i)); + } +} + +function pull_names(arr) +{ + names = names.concat(arr); + if (names.length >= 30) { + run_generate(); + } else { + get_names(pull_names) + } +} + +function generate() +{ + if (generating) + return; + generating = true; + + // Button text + btn.textContent = "Ok sar...!"; + // Cursors + root.classList.add("loading"); + btn.classList.add("loading"); + out.classList.add("loading"); + + get_names(pull_names); +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..515e273 --- /dev/null +++ b/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "encoding/json" + "flag" + "html/template" + "log" + "net/http" +) + +// Server configs. +var ( + listenAddr = flag.String("listen", "0.0.0.0:80", "Listen address for server, in the form addr:port") + scriptPath = flag.String("script", "./pajen.pl", "The relative path to pajen.pl. Must be executable!") + apiQty = flag.Int("quantity", 5, "The number of names returned at a time from the API") +) + +var ( + pajen *Pajen + indexTmpl = template.Must(template.ParseFiles("index.gohtml")) +) + +func handleRoot(w http.ResponseWriter, r *http.Request) { + if r.RequestURI != "/" { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 not found")) + return + } + + indexTmpl.Execute(w, nil) +} + +func handleAPI(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" || r.RequestURI != "/api" { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 not found")) + return + } + + a := make([]string, 0, *apiQty) + for i := 0; i < *apiQty; i++ { + a = append(a, <-pajen.Chan) + } + + b, err := json.Marshal(a) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + b, _ = json.Marshal(map[string]any{ + "status": "Failed", + "error": err, + }) + } + + w.Write(b) +} + +func main() { + log.Println("Starting pajen.pl frontend - the famous indian name generator") + flag.Parse() + + p, err := NewPajen(*scriptPath) + if err != nil { + log.Fatal(err) + } + pajen = p + + http.HandleFunc("/", handleRoot) + http.HandleFunc("/api", handleAPI) + http.HandleFunc("/index.css", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "index.css") + }) + http.HandleFunc("/index.js", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "index.js") + }) + + if err := http.ListenAndServe(*listenAddr, nil); err != nil { + log.Fatalln(err) + } +} diff --git a/pajen.go b/pajen.go new file mode 100644 index 0000000..8776de7 --- /dev/null +++ b/pajen.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "io" + "log" + "os/exec" + "strings" +) + +// Pajen represents an instance of the pajen.pl script running in infinite +// generation mode. +type Pajen struct { + Chan chan string + + proc *exec.Cmd + out io.ReadCloser +} + +// NewPajen starts the pajen script at the path given. +func NewPajen(path string) (*Pajen, error) { + p := new(Pajen) + + p.proc = exec.Command("/usr/bin/perl", path, "-i") + p.proc.Stdin = p + + pi, err := p.proc.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("pajen: pipe pajen.pl: %w", err) + } + p.out = pi + p.proc.Stderr = p.proc.Stdout + + p.Chan = make(chan string) + + if err := p.proc.Start(); err != nil { + return nil, fmt.Errorf("pajen: start pajen.pl: %w", err) + } + + go p.run() + return p, nil +} + +// Read feeds constant newlines to the program. +func (r *Pajen) Read(b []byte) (int, error) { + if len(b) < 1 { + return 0, nil + } + + return copy(b, []byte("\n")), nil +} + +func (r *Pajen) run() { + buf := make([]byte, 255) + + for { + _, err := r.out.Read(buf) + if err != nil { + log.Println("pajen read error:", err) + return + } + + wk := string(buf) + lines := strings.Split(wk, "\n") + + for _, l := range lines { + if l != "" && l != "\n" { + r.Chan <- l + } + } + } +} diff --git a/pajen.pl b/pajen.pl new file mode 100755 index 0000000..3c57669 --- /dev/null +++ b/pajen.pl @@ -0,0 +1,70 @@ +#!/usr/bin/perl +# Pajeet name generator (pajen.pl) +# Written by Ethan Marshall - 2024 +# Material from https://desu-usergeneratedcontent.xyz/g/image/1705/80/1705808467048.png + +use strict; +use diagnostics; +use warnings; + +use Getopt::Long; + +# Generated names consist of a prefix followed by a suffix +our @prefixes = ( + "Pa", + "Ra", + "Ab", + "A", + "Na", + "Vi", + "Pra", + "Ku", + "Gi", + "Ha", + "San", + "Bel" +); + +our @suffixes = ( + "jeet", + "kesh", + "hul", + "jeesh", + "jesh", + "mit", + "mit", + "hesh", + "raj", + "nil", + "jith", + "tik" +); + +sub fatal { + printf("%s\n", $_[0]); + exit(1); +} + +sub usage { + fatal("Usage: pajen [-n count] [-ihu]") +} + +# Command line options +my $count = 1; +my $infinite = ''; +GetOptions( + 'number=i' => \$count, + "infinite" => \$infinite, + "help|usage" => sub { usage() } +) or usage(); + +for (my $i = 0; $i < $count || $infinite; $i++) { + my $prefix = int(rand(@prefixes)); + my $suffix = int(rand(@suffixes)); + + printf("%s%s\n", $prefixes[$prefix], $suffixes[$suffix]); + + if ($infinite) { + ( eq "q\n") && exit(0); + } +}