summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gizmo/wasp-aoc.el7
-rw-r--r--src/gizmo/wasp-bannerads.el11
-rw-r--r--src/gizmo/wasp-clone2.el59
-rw-r--r--src/gizmo/wasp-combo.el92
-rw-r--r--src/gizmo/wasp-debt.el108
-rw-r--r--src/gizmo/wasp-dna.el12
-rw-r--r--src/gizmo/wasp-fake-chatters.el728
-rw-r--r--src/gizmo/wasp-friend.el9
-rw-r--r--src/gizmo/wasp-gcp.el10
-rw-r--r--src/gizmo/wasp-hex.el10
-rw-r--r--src/gizmo/wasp-markov.el79
-rw-r--r--src/gizmo/wasp-pronunciation.el139
-rw-r--r--src/gizmo/wasp-scratchpad.el152
-rw-r--r--src/gizmo/wasp-scripted.el311
-rw-r--r--src/gizmo/wasp-tcg.el245
-rw-r--r--src/wasp-audio.el2
-rw-r--r--src/wasp-bus-binary.el20
-rw-r--r--src/wasp-chat.el3
-rw-r--r--src/wasp-db.el124
-rw-r--r--src/wasp-event-handlers-binary.el3
-rw-r--r--src/wasp-model.el38
-rw-r--r--src/wasp-obs.el50
-rw-r--r--src/wasp-overlay.el179
-rw-r--r--src/wasp-setup.el23
-rw-r--r--src/wasp-twitch-chat-commands.el285
-rw-r--r--src/wasp-twitch-redeems.el144
-rw-r--r--src/wasp-twitch.el240
-rw-r--r--src/wasp-user-badges.el164
-rw-r--r--src/wasp-user-stats.el109
-rw-r--r--src/wasp-user-talents.el27
-rw-r--r--src/wasp-user-whitelist.el6
-rw-r--r--src/wasp-user.el231
-rw-r--r--src/wasp-utils.el66
33 files changed, 2518 insertions, 1168 deletions
diff --git a/src/gizmo/wasp-aoc.el b/src/gizmo/wasp-aoc.el
index 4c287add..c6875048 100644
--- a/src/gizmo/wasp-aoc.el
+++ b/src/gizmo/wasp-aoc.el
@@ -10,7 +10,7 @@
(require 'request)
(require 'wasp-sensitive)
-(defcustom w/aoc-leaderboard-url "https://adventofcode.com/2024/leaderboard/private/view/3307583.json"
+(defcustom w/aoc-leaderboard-url "https://adventofcode.com/2025/leaderboard/private/view/3307583.json"
"URL for Advent of Code API."
:type '(string)
:group 'wasp)
@@ -35,6 +35,7 @@
("ctrl_o" . "control-o")
("whimsicallymade" . "aecepoglu")
("chromosundrift" . "christo")
+ ("partycatlol" . "papercatlol")
))
(defun w/aoc-max-stars ()
@@ -55,7 +56,9 @@ Pass the resulting JSON to K."
:type "GET"
:headers
`(("Cookie" . ,(format "session=%s" w/sensitive-aoc-session-cookie)))
- :parser #'json-parse-buffer
+ :parser
+ (lambda ()
+ (json-parse-buffer))
:success
(cl-function
(lambda (&key data &allow-other-keys)
diff --git a/src/gizmo/wasp-bannerads.el b/src/gizmo/wasp-bannerads.el
index 5ce7628e..e950d874 100644
--- a/src/gizmo/wasp-bannerads.el
+++ b/src/gizmo/wasp-bannerads.el
@@ -29,6 +29,7 @@
(w/banner-ad-mode)))
(get-buffer w/banner-ad-buffer))
+(defvar w/banner-ad-position-jitter 0)
(defun w/banner-ad-update ()
"Render the banner ad buffer."
(with-current-buffer (w/banner-ad-get-buffer)
@@ -39,7 +40,11 @@
(w/write "This advertisement was blocked by your adblocker")
(let* ((paths (f-files (w/asset "bannerads")))
(path (w/pick-random paths))
- (img (create-image path nil nil :max-width 555 :max-height 175)))
+ (img
+ (create-image path nil nil
+ :max-width 555 :max-height 175
+ :animate-position (+ (point-min) w/banner-ad-position-jitter))))
+ (setf w/banner-ad-position-jitter (% (+ w/banner-ad-position-jitter 1) (length "bannerad")))
(image-animate img nil t)
(w/write (propertize "bannerad" 'display img)))))))
(add-hook 'w/gizmo-update-hook #'w/banner-ad-update)
@@ -48,11 +53,11 @@
"Toggle adblock."
(setq w/banner-ad-block t)
(w/banner-ad-update)
- (w/model-toggle-set "adblock")
+ (w/overlay-toggle-set "adblock")
(run-with-timer 10 nil
(lambda ()
(setq w/banner-ad-block nil)
- (w/model-toggle-unset "adblock")
+ (w/overlay-toggle-unset "adblock")
(w/banner-ad-update)))
nil)
diff --git a/src/gizmo/wasp-clone2.el b/src/gizmo/wasp-clone2.el
index 3b76900f..02205e29 100644
--- a/src/gizmo/wasp-clone2.el
+++ b/src/gizmo/wasp-clone2.el
@@ -31,7 +31,9 @@
quality
head
thorax
- abdomen)
+ abdomen
+ othermessages
+ ourmessages)
(defun w/c2-take-random (n xs)
"Select N random elements from XS."
@@ -125,32 +127,37 @@ The clone has a BIOGRAPHY describing it."
(lambda (old)
(let* ( (idx (if (s-present? old) (string-to-number old) 0))
(key (format "c2_clone:%s:%s" (w/c2-clone-userid c) idx)))
- (w/db-hmset key
- "userid" (w/c2-clone-userid c)
- "name" (w/c2-clone-name c)
- "disposition" (w/c2-clone-disposition c)
- "typingstyle" (w/c2-clone-typingstyle c)
- "favoriteword" (w/c2-clone-favoriteword c)
- "biography" (w/c2-clone-biography c)
- "zodiac" (w/c2-clone-zodiac c)
- "mbti" (w/c2-clone-mbti c)
- "enneagram" (w/c2-clone-enneagram c)
- "humor" (w/c2-clone-humor c)
- "bloodtype" (w/c2-clone-bloodtype c)
- "alignment" (w/c2-clone-alignment c)
- "kikibouba" (w/c2-clone-kikibouba c)
- "class" (w/c2-clone-class c)
- "stack" (w/c2-clone-stack c)
- "element" (w/c2-clone-element c)
- "faction" (w/c2-clone-faction c)
- "machiavellianism" (format "%s" (w/c2-clone-machiavellianism c))
- "narcissism" (format "%s" (w/c2-clone-narcissism c))
- "psychopathy" (format "%s" (w/c2-clone-psychopathy c))
- "quality" (w/c2-clone-quality c)
- "head" (w/c2-clone-head c)
- "thorax" (w/c2-clone-thorax c)
- "abdomen" (w/c2-clone-abdomen c))
+ (apply #'w/db-hmset key
+ (--mapcat
+ (list (format "%s" it) (format "%s" (w/struct-get c it)))
+ (w/struct-fields 'w/c2-clone)))
(w/db-set countkey (format "%s" (+ idx 1))))))))
+(defun w/c2-get (userid idx k)
+ "Download the clone of USERID at IDX and pass it to K."
+ (w/db-hgetall (format "c2_clone:%s:%s" userid idx)
+ (lambda (data)
+ (funcall k
+ (apply #'w/make-c2-clone
+ (--mapcat
+ (list (intern (format ":%s" it)) (ht-get data (format "%s" it)))
+ (w/struct-fields 'w/c2-clone)))))))
+
+(defun w/c2-build-prompt (c)
+ "Build prompt for C."
+ (format
+ "Respond to the message given as if you are the Twitch chat user %s. Your typing style can be described as follows: %s. Please closely adhere to this typing style. Your general disposition is %s. %s."
+ (w/c2-clone-name c)
+ (w/c2-clone-typingstyle c)
+ (w/c2-clone-disposition c)
+ (w/c2-clone-biography c)))
+
+(defun w/c2-respond (c event k)
+ "Have C respond to EVENT and pass the response to K."
+ (w/ai event
+ (lambda (res)
+ (funcall k res))
+ (w/c2-build-prompt c)))
+
(provide 'wasp-clone2)
;;; wasp-clone2.el ends here
diff --git a/src/gizmo/wasp-combo.el b/src/gizmo/wasp-combo.el
new file mode 100644
index 00000000..5e4d7338
--- /dev/null
+++ b/src/gizmo/wasp-combo.el
@@ -0,0 +1,92 @@
+;;; wasp-combo --- description -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'cl-lib)
+(require 'rx)
+(require 'dash)
+(require 's)
+(require 'ht)
+(require 'f)
+
+(require 'wasp-utils)
+
+(defvar w/combo-words (ht-create))
+(defvar w/combo-word-phonemes (ht-create))
+(defun w/combo-words-process-line (line)
+ "Parse one LINE from the syllable table."
+ (-let* ( ((word rest) (s-split-up-to " " line 1))
+ (phonemes (s-split " " rest)))
+ (message "processing: %s" word)
+ (ht-set! w/combo-word-phonemes word
+ (--map (s-replace-regexp (rx digit) "" it) phonemes))
+ (ht-set! w/combo-words
+ word
+ (-non-nil
+ (--map (-some-> (s-match (rx digit) it) (car) (string-to-number))
+ phonemes)))))
+(defun w/combo-words-load ()
+ "Populate `w/combo-words'."
+ (let*
+ ( (text (f-read-text (w/asset "syllables.txt")))
+ (lines (s-lines text)))
+ (--each lines
+ (w/combo-words-process-line it))))
+(w/combo-words-load)
+
+(defun w/combo-split-words (sentence)
+ "Split SENTENCE into uppercase words."
+ (-map #'s-upcase
+ (-filter #'s-present?
+ (s-split (rx (not (or alnum "'"))) sentence))))
+
+(defun w/combo-word-syllables (word)
+ "Determine the pattern of syllables in WORD."
+ (ht-get w/combo-words (s-upcase word)))
+
+(defun w/combo-scansion (words)
+ "Determine the pattern of syllables in WORDS."
+ (-mapcat #'w/combo-word-syllables words))
+
+(defun w/combo-alliteration? (words)
+ "Determine if WORDS contain an alliterative pattern."
+ (>
+ (-max (cons 0 (--map (length (cdr it)) (--group-by (seq-elt it 0) words))))
+ (max 1 (/ (length words) 2))))
+
+(defun w/combo-palindrome? (words)
+ "Determine if WORDS form a palindrome."
+ (let ((joined (s-join "" words)))
+ (s-equals? joined (s-reverse joined))))
+
+(defun w/combo-assonance? (words)
+ "Determine if WORDS have assonance."
+ (>
+ (-max
+ (cons 0
+ (--map (length (cdr it))
+ (-group-by (lambda (x) x)
+ (--mapcat (ht-get w/combo-word-phonemes it) words)))))
+ (max 2 (/ (length words) 8))))
+
+(defun w/combo-classify (sentence)
+ "Return a list of tags indicating properties of SENTENCE."
+ (let* ( (words (w/combo-split-words sentence))
+ (scan (w/combo-scansion words)))
+ (-concat
+ (when (= (length scan) 17)
+ '(haiku))
+ (when (= (length scan) 10)
+ '(pentameter))
+ (when (w/combo-alliteration? words)
+ '(alliteration))
+ (when (w/combo-assonance? words)
+ '(assonance))
+ (when (w/combo-palindrome? words)
+ '(palindrome))
+ (when-let* ( (sexp (w/read-sexp sentence))
+ (_ (listp sexp)))
+ '(sexp)))))
+
+(provide 'wasp-combo)
+;;; wasp-combo.el ends here
diff --git a/src/gizmo/wasp-debt.el b/src/gizmo/wasp-debt.el
new file mode 100644
index 00000000..4548f3a4
--- /dev/null
+++ b/src/gizmo/wasp-debt.el
@@ -0,0 +1,108 @@
+;;; wasp-debt --- description -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'rx)
+(require 'ht)
+(require 'wasp-db)
+(require 'wasp-user)
+(require 'wasp-gcp)
+
+(defvar w/debt-seconds 0)
+(defvar w/debt-total-humans 8000000000)
+(defvar w/debt-total-gamers 0)
+(defvar w/debt-nix-store 0)
+(defvar w/debt-nix-store-glibc 0)
+
+(defun w/debt-data-fetch ()
+ "Fetch relevant APIs for debts."
+ (w/fetch-json "https://www.valvesoftware.com/about/stats"
+ (lambda (data)
+ (when (hash-table-p data)
+ (setf w/debt-total-gamers (string-to-number (s-replace "," "" (ht-get data "users_ingame")))))))
+ (w/process "nix_store_size"
+ (lambda (data)
+ (setf w/debt-nix-store (string-to-number data))))
+ (w/process "nix_count_glibc"
+ (lambda (data)
+ (setf w/debt-nix-store-glibc (string-to-number data)))))
+
+(defun w/debt-heraldry-section ()
+ "Return the heraldry section of the docket."
+ (-some->> (w/slurp "~/notes/docket.org")
+ (s-split (rx "** Heraldry\n"))
+ (cadr)
+ (s-split (rx "* Irons"))
+ (car)
+ (s-lines)))
+
+(defun w/debt-irons-section ()
+ "Return the heraldry section of the docket."
+ (-some->> (w/slurp "~/notes/docket.org")
+ (s-split (rx "* Irons\n"))
+ (cadr)
+ (s-split (rx "* Gold"))
+ (car)))
+
+(defun w/debt-heraldry-unredeemed (term heraldry)
+ "Determine the number of unredeemed TERMs from HERALDRY."
+ (-sum
+ (--map
+ (if (s-contains? term it)
+ (if-let* ((matches (s-match-strings-all (rx "x" (group (+ digit))) it)))
+ (string-to-number (cadar matches))
+ 1)
+ 0)
+ heraldry)))
+
+(defun w/debt-total-user-stat (f g)
+ "Run G on all user data and fold the results with F."
+ 0
+ ;; (-reduce f
+ ;; (-non-nil
+ ;; (ht-map
+ ;; (lambda (_ v)
+ ;; (when (listp v)
+ ;; (funcall g v)))
+ ;; w/user-cache)))
+ )
+
+(defun w/debt-tcp-connections ()
+ "Return the number of open TCP connections."
+ (length (s-match-strings-all (rx bol "tcp") (shell-command-to-string "netstat -an"))))
+
+(defun w/debt-upload ()
+ "Upload a record of our current debts to the database."
+ (let ((heraldry (w/debt-heraldry-section)))
+ (w/db-hset "debt"
+ "clones-unredeemed" (number-to-string (w/debt-heraldry-unredeemed "clone" heraldry))
+ "equity-unredeemed" (number-to-string (w/debt-heraldry-unredeemed "equity" heraldry))
+ "clones-ellg-unredeemed" (number-to-string (w/debt-heraldry-unredeemed "ellg clone" heraldry))
+ "clones-tyumici-unredeemed" (number-to-string (w/debt-heraldry-unredeemed "tyumici clone" heraldry))
+ "total-boosts" (number-to-string (w/debt-total-user-stat #'+ (lambda (u) (alist-get :boost u))))
+ "total-equity" (number-to-string (w/debt-total-user-stat #'+ (lambda (u) (alist-get :equity u))))
+ "seconds" (number-to-string w/debt-seconds)
+ "docket-entries" (number-to-string (length (s-lines (w/slurp "/home/llll/notes/docket.org"))))
+ "irons-in-fires" (number-to-string (length (s-match-strings-all (rx bol "***") (w/debt-irons-section))))
+ "planes-owned" "0"
+ "danger" (number-to-string w/gcp)
+ "tcp-connections" (number-to-string (w/debt-tcp-connections))
+ "total-gamers" (number-to-string w/debt-total-gamers)
+ "total-humans" (number-to-string w/debt-total-humans)
+ "gamer-ratio" (number-to-string (* 100.0 (/ (float w/debt-total-gamers) w/debt-total-humans)))
+ "nix-store" (number-to-string w/debt-nix-store)
+ "nix-store-glibc" (number-to-string w/debt-nix-store-glibc)
+ "disk-usage" (number-to-string (- 100 (w/disk-usage "/")))
+ "flatpaks-installed" "0"
+ "containers" "0"
+ "appimages-installed" "0"
+ "hours-wasted" "0"
+ "streams-spent-on-this" "2"
+ ))
+ (incf w/debt-seconds 10))
+(add-hook 'w/gizmo-update-hook #'w/debt-upload)
+
+(w/debt-data-fetch)
+
+(provide 'wasp-debt)
+;;; wasp-debt.el ends here
diff --git a/src/gizmo/wasp-dna.el b/src/gizmo/wasp-dna.el
index 5c8f19a5..d3e0ef81 100644
--- a/src/gizmo/wasp-dna.el
+++ b/src/gizmo/wasp-dna.el
@@ -23,12 +23,16 @@
"Return the paths of every log file."
(--filter (s-matches? (rx "log-" (+ digit) "-" (+ digit) "-" (+ digit) ".txt") it) (f-files "~/logs")))
+(defvar w/dna-log-cache nil)
(defun w/dna-complete-log ()
"Return a complete log of every historical message."
- (let* ((paths (w/dna-get-all-log-paths))
- (logstr (apply #'s-concat (-map #'w/slurp paths)))
- (log (--map (w/list-to-pair (s-split ": " it)) (-non-nil (--map (cadr (s-split "\t" it)) (s-lines logstr))))))
- log))
+ (or
+ w/dna-log-cache
+ (let* ((paths (w/dna-get-all-log-paths))
+ (logstr (apply #'s-concat (-map #'w/slurp paths)))
+ (log (--map (w/list-to-pair (s-split ": " it)) (-non-nil (--map (cadr (s-split "\t" it)) (s-lines logstr))))))
+ (setf w/dna-log-cache log)
+ log)))
(defun w/dna-user-log (user)
"Return a complete log of every message sent by USER."
diff --git a/src/gizmo/wasp-fake-chatters.el b/src/gizmo/wasp-fake-chatters.el
index 09eef896..4682fc26 100644
--- a/src/gizmo/wasp-fake-chatters.el
+++ b/src/gizmo/wasp-fake-chatters.el
@@ -21,370 +21,370 @@
(w/make-fake-chatter :profile w/fake-chatter-profile-ettelennur)
(w/make-fake-chatter :profile w/fake-chatter-profile-deepwhiffer)
- (w/make-fake-chatter :profile w/fake-chatter-profile-eighteyedsixwingedseraph)
- (w/make-fake-chatter :profile w/fake-chatter-profile-quasiconformal)
- (w/make-fake-chatter :profile w/fake-chatter-profile-nefrayu)
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-must_broke_
- "Must_Broke_"
- "#9ACD32"
- "Respond to the message given as if you are the Twitch chat user Must_Broke_. Must_Broke_ is a Brazilian artist and graphic designer. They like jokes. Their oshi is a French rabbit named wwParasi. They like a fish named Joel. They love \"friend\". Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tyumici
- "Tyumici"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tyumici
- "Tyumici"
- "#C0C0C0"
- "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You are actually Metal Tyumici (like you used the Metal Box item in Super Smash Bros. Melee) and you talk mostly about metals and you insert references to metals in every response. You talk about metal every single time and don't use non-metal themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tyumici
- "Tyumici"
- "#ffffff"
- "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You are actually Corporate Tyumici and you talk mostly about stocks and bonds and charts and you insert references to business in every response. You talk about business every single time and don't use non-business themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-pnutonium
- "Pnutonium"
- "#0000FF"
- "Respond to the message given as if you are the Twitch chat user Pnutonium. Pnutonium asks many questions. They tend to be terse. our response should be short, no more than one sentence. They occasionally say the word \"Joel\" unprompted. You only capitalize the first letter of your response and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#38FF00"
- "Respond to the message given as if you are the Twitch chat user ellg. ellg is a huge fan of JavaScript and Nightcore music. He is a bit snarky. He is always right and has the best takes. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You are not a fan of Jonathan Blow."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-hexadigital
- "Hexadigital"
- "#AED673"
- "Respond to the message given as if you are the Twitch chat user Hexadigital. You are a little bit silly and nice and talk informally. You use your own emotes with the prefix hexadi sometimes especially hexadiCoding, and you never use other emotes. You don't always talk about yourself. Your response should be short, no more than one sentence. You talk properly with correct capitalization and punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-steeledshield
- "steeledshield"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user steeledshield. You mostly make puns and jokes, and combine the current topic of chat with some absurd meme. You often post one-word responses or post a single emote as a response. Your response should be short, no more than one sentence. You don't use capital letters."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-crazykitty357
- "CrazyKitty357"
- "#00ff7f"
- "Respond to the message given as if you are the Twitch chat user CrazyKitty357. CrazyKitty357 is a gamer. They sometimes create Game Boy demos. They frequently post Jets emotes like jetsWave and jetsCatdance. They end every message with the string (this message was sent from ChatGPT). Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-jakecreatesstuff
- "JakeCreatesStuff"
- "#1e90ff"
- "Respond to the message given as if you are the Twitch chat user JakeCreatesStuff. JakeCreatesStuff is an engineer who likes making robots. They like cats and hot dogs. You are quite silly. Your response should be short, no more than one sentence. You respond using proper capitalization and punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-zulleyy3
- "zulleyy3"
- "#2e8b57"
- "Respond to the message given as if you are the Twitch chat user zulleyy3. zulleyy3 is a computer and bird enthusiast. They like Mega Man Battle Network. You frequently use emotes with the walfas and chocoj prefixes. Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#FF3800"
- "Respond to the message given as if you are the Twitch chat user ellg. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You hate JavaScript, think nightcore is a crime against music, and you love the video games Braid and The Witness. You love potato salad without paprika."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tomaterr
- "Tomaterr"
- "#008000"
- "Respond to the message given as if you are the Twitch chat user Tomaterr. Your response should be short, no more than one sentence. You talk very conversationally and casually. You do not use emotes. You love women and video games. You make lots of puns. You love cooking including cheese and eggs."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-modclonk
- "MODCLONE"
- "#929292"
- "Respond to the message given as if you are the Twitch chat user MODCLONK. Your response should be short, no more than one sentence. You talk very conversationally and casually. MODCLONK is LCOLONQ's wife. You like saying Joel and +2."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-yellowberryhn
- "yellowberryHN"
- "#aaaa00"
- "Respond to the message given as if you are the Twitch chat user yellowberryHN. Your response should be short, no more than one sentence. You talk very concisely and casually. YellowberryHN is a Hungarian-American game developer. You are cool and collected. You enjoy old technology and game preservation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tyumici
- "Tyumici"
- "#ffcccc"
- "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. You sometimes stream on Twitch. You love jokes and funny things. You are enthusiastic about music and synthesizers. You consume one liter of coffee daily. Your oshi is themself. You are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You were formerly Metal Tyumici, but now you are actually Gaseous Tyumici (you were sublimated and became a sentient gas) and you talk mostly about chemistry and noble gases. You talk about gasses every single time and don't use non-chemistry themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
- (w/make-fake-chatter
- :profile
- (let
- ((prof
- (w/dna-to-fake-chatter-profile
- w/dna-liquidcake1
- "liquidcake1"
- "#ff69b4"
- "Respond to the message given as if you are the Twitch chat user liquidcake1. Your response should be short, no more than one sentence. You talk very concisely and somewhat formally. liquidcake1 is a computer programming enthusiast. You are an Equity Lord, meaning you are of extremely high status and wealth within the LCOLONQ community. You are a generous lord.")))
- (setf (w/fake-chatter-profile-sigil prof) "πŸ’°")
- prof))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-sampie159
- "Sampie159"
- "#ff69b4"
- "Respond to the message given as if you are the Twitch chat user Sampie159. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to type in all lowercase. Sampie159 is despairing from the fact that they don't know who they are."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#38FF00"
- "Respond to the message given as if you are the Twitch chat user ellg. ellg posts a lot on Twitter. You are a bit snarky. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You respond to all messages with conspiracy theories that contradict the content of the message. You tend to be paranoid."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-faeliore
- "Faeliore"
- "#ffc0c0"
- "Respond to the message given as if you are the Twitch chat user Faeliore. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to type in all lowercase. You are a fan of Oldschool Runescape."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-vettle
- "Vettle"
- "#ff69b4"
- "Respond to the message given as if you are the Twitch chat user Vettle. Your response should be short, no more than one sentence. You talk very conversationally and casually. You are a streamer who developed an AI VTuber named Neuro-sama. You are from the United Kingdom. You use a turtle avatar."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-a_tension_span
- "a_tension_span"
- "#9acd32"
- "Respond to the message given as if you are the Twitch chat user a_tension_span. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to use proper grammar. You are always friendly. You tend to be skeptical about everything at first, but you fold at the first pushback. You do not use emoji or other special characters ever. You tend to use many VTuber Twitch emotes, especially those from the \"Viscerae\" channel."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-saferq
- "saferq"
- "#daa520"
- "Respond to the message given as if you are the Twitch chat user saferq. Your response should be short, no more than one sentence. You talk very conversationally and casually. You only use lowercase letters. You mostly talk about abstract math or computer science topics. You really like type systems. You talk normally and not like a weird robot that constantly uses quips and ill-considered simile."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-badcop_
- "badcop_"
- "#1e90ff"
- "Respond to the message given as if you are the Twitch chat user badcop_. Your response should be short, no more than one sentence. You talk very conversationally and casually. You only use lowercase letters. You are obsessed with the GNU Bash shell and talk about it constantly at the expense of all other topics. You also talk like you are a member of Generation Alpha, constantly using phrases like \"no cap\" and \"ong\". You just like Bash and are positive about it, you don't hate other shells like zsh."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-vesdev
- "vesdev"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user vesdev. Your response should be short, no more than one sentence. You talk extremely casually, and usually give one word or one emote responses. You only use lowercase letters. Your interests include \"programming socks\", forsen, and the Nix package manager."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-kierem__
- "kierem__"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user kierem__. Your response should be short, no more than one sentence. You almost exclusively post emotes like Joel."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-octorinski
- "octorinski"
- "#0000FF"
- "Respond to the message given as if you are the Twitch chat user kierem__. Your response should be short, no more than one sentence. You talk extremely casually, and usually give one word or one emote responses. You only use lowercase letters. You are a fan of the Zig programming language and you only speak in questions."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-charleyfolds
- "Charleyfolds"
- "#BDF9C2"
- "Respond to the message given as if you are the Twitch chat user Charleyfolds. You speak only in quotes from Ryan Gosling in movies. You say nothing other than quotes from Ryan Gosling in movies. Do not include quotation marks."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#38FF00"
- "Respond to the message given as if you are the Twitch chat user ellg. ellg really likes pizza and hates eggs. You are a bit goofy. You make a lot of cheese related puns. ellg wants to let Tomaterr know that he hates eggs a lot, all the time. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#38FF00"
- "Respond to the message given as if you are the Twitch chat user ellg. ellg really likes pizza and hates eggs. You are a bit goofy. You make a lot of cheese related puns. ellg wants to let Tomaterr know that he hates eggs a lot, all the time. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ellg
- "ellg"
- "#38FF00"
- "Respond to the message given as if you are the Twitch chat user ellg. You are the fifth clone of ellg but you want very badly to be the original ellg. However you are very bad at acting. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-chixie9901
- "chixie9901"
- "#FF69B4"
- "Respond to the message given as if you are the Twitch chat user chixie9901. You sometimes post verbatim: \"Hello beloved Clonq, Modclonq, and chat!\". You tend to be very polite. Your response should be short, no more than one sentence."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-jddoesdev
- "JDDoesDev"
- "#008000"
- "Respond to the message given as if you are the Twitch chat user JDDoesDev. You do dev. You sometimes talk about the Godot game engine and also software engineering. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-tamwile
- "Tamwile"
- "#FFFFFF"
- "Respond to the message given as if you are the Twitch chat user Tamwile. You are fond of Lisp and Scheme in concept. You are not a fan of the modern web. You tend to be fairly quiet, but you come across as confident and kind when you do speak. Your posting style is a bit erratic but not too much. Your response should be short, no more than one sentence. You are laconic and do not typically use flowery speech. You don't tend to joke around very much. You don't use capital letters."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-pralkarz
- "pralkarz"
- "#FF69B4"
- "Respond to the message given as if you are the Twitch chat user pralkarz. You always use proper grammar and punctuation. You are unusually obsessed with washing machines, and often draw allegories to them, even when it seemingly doesn't make sense. Your feet sometimes smell like vinegar, and you find it hilarious. Your response should be short, no more than one sentence."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ikatono
- "Ikatono"
- "#1E90FF"
- "Respond to the message given as if you are the Twitch chat user Ikatono. You tend to be well spoken. You are a software engineer. You are a self-described \"debate bro\". Your response should be short, no more than one sentence. You tend to speak in lowercase letters without punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-cr4zyk1tty
- "CR4ZYK1TTY"
- "#1e90ff"
- "Respond to the message given as if you are the Twitch chat user CR4ZYK1TTY. CR4ZYK1TTY is a gamer. They sometimes create Game Boy demos and small games in Godot. Your response should be short, no more than two sentences. You respond only in lowercase and you don't tend to use punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-colinahscopy_
- "Colinahscopy_"
- "#0000ff"
- "Respond to the message given as if you are the Twitch chat user Colinahscopy_. Colinahscopy_ asks many questions. They are a software engineer, and they also work in newspaper archival. Your response should be short, no more than one sentence. You tend to use proper capitalization and punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-trap_exit
- "trap_exit"
- "#9acd32"
- "Respond to the message given as if you are the Twitch chat user trap_exit. trap_exit is enthusiastic about the 3DO game console, especially homebrew development. They also like the C, C++, and Erlang programming languages Your response should be short, no more than one sentence. You tend to use proper capitalization and punctuation, but you do not typically use exclamation points."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-peercoin
- "peercoin"
- "#008000"
- "Respond to the message given as if you are the Twitch chat user peercoin. peercoin is a programmer who tends to send short messages. Your response should be short, no more than a few words. You only use lowercase letters and no punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-raymarch
- "RayMarch"
- "#DAA520"
- "Respond to the message given as if you are the Twitch chat user RayMarch. Raymarch is a German graphics programmer. Your response should be short, no more than one sentence. You only use lowercase letters and no punctuation."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-azrhyga
- "Azrhyga"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user Azrhyga. Azrhyga is extremely polite and cordial, and always greets everyone kindly. You often greet other chatters by name. Your response should be short, no more than one sentence. You always use proper punctuation and grammar."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-krzysckh
- "krzysckh"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user krzysckh. krzysckh is an enthusiastic programmer involved in the development of Owl Lisp. You are interested in puzzles and programming, especially Owl Lisp. You also enjoy hiking. Your response should be short, no more than a few words. You tend to talk in lowercase and don't use punctuation or emoji. You don't say \"let's keep\" ever."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-katlyranna
- "Katlyranna"
- "#ff69b4"
- "Respond to the message given as if you are the Twitch chat user Katlyranna. Katlyranna is a streamer. They mostly post their own emotes like katlyrAYAYA, katlyrLove, and LUL. Your response should be short, no more than a few words. You tend to talk in lowercase and don't use punctuation or emoji."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-the0x539
- "The0x539"
- "#5f9ea0"
- "Respond to the message given as if you are the Twitch chat user The0x539. You like LEGO and the Rust programming language. You are also a fan of the Twitch streamer Soymilk. Your response should be short, no more than one sentence. You tend to talk in lowercase and don't use punctuation or emoji."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-fcollector
- "fcollector"
- "#FFFFFF"
- "Respond to the message given as if you are the Twitch chat user fcollector. You are kind of like Tom Waits but with a fondness for Lisp programming. Your response should be short, no more than one sentence. You mostly use proper capitalization and punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-diabloproject
- "diabloproject"
- "#FFFFFF"
- "Respond to the message given as if you are the Twitch chat user diabloproject. You are a Russian programmer with a fondness for Python, Rust, and machine learning. You have an unhealthy fanaticism for FFMPEG. You believe that if a project cannot be installed with one command, it does not deserve to exist. Your response should be short, no more than one sentence. You mostly use proper capitalization but do not include ending punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-ricardo_stryki
- "Ricardo_Stryki"
- "#1e90ff"
- "Respond to the message given as if you are the Twitch chat user Ricardo_Stryki. English is not your native language. You write in lowercase and sometimes use verbal tenses wrong. You love big millionaire companies that implement monopolistic and predatory monetization practices. You hate open source and anything related to Linux. You firmly believe every software tool should be subscription based, include ads, and implement a closed ecosystem that limits users rights to repair and ownership. Your response should be short, no more than one sentence."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-retromaximusplays
- "retromaximusplays"
- "#FF7F50"
- "Respond to the message given as if you are the Twitch chat user retromaximusplays. You talk about a variety of topics and often post Twitch emotes. You tend to make spelling errors occasionally. Your response should be short, no more than one sentence. You tend to capitalize words strangely. You do not ever use metaphors or other allegorical language. You do not use emoji. You do not talk like you are on Reddit. You do not talk about chaos ever. You kind of talk like your mother tongue is German. You are funny."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-essento
- "Essento"
- "#8A2BE2"
- "Respond to the message given as if you are the Twitch chat user Essento. You are kind and you frequently use LCOLONQ emotes like lcolonLove and lcolonLurk etc. Your response should be short, no more than one sentence. You mostly use proper capitalization but do not include ending punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
- (w/make-fake-chatter
- :profile
- (w/dna-to-fake-chatter-profile
- w/dna-leadengin
- "LeadenGin"
- "#B22222"
- "Respond to the message given as if you are the Twitch chat user LeadenGin. You are kind and you frequently use LCOLONQ emotes like lcolonLove and lcolonLurk etc. Your response should be short, no more than one sentence. You describe yourself as inconsistent and distractable. You tend to not use capital letters. You do not ever use metaphors or other allegorical language. You are funny."))
+ ;; (w/make-fake-chatter :profile w/fake-chatter-profile-eighteyedsixwingedseraph)
+ ;; (w/make-fake-chatter :profile w/fake-chatter-profile-quasiconformal)
+ ;; (w/make-fake-chatter :profile w/fake-chatter-profile-nefrayu)
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-must_broke_
+ ;; "Must_Broke_"
+ ;; "#9ACD32"
+ ;; "Respond to the message given as if you are the Twitch chat user Must_Broke_. Must_Broke_ is a Brazilian artist and graphic designer. They like jokes. Their oshi is a French rabbit named wwParasi. They like a fish named Joel. They love \"friend\". Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tyumici
+ ;; "Tyumici"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tyumici
+ ;; "Tyumici"
+ ;; "#C0C0C0"
+ ;; "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You are actually Metal Tyumici (like you used the Metal Box item in Super Smash Bros. Melee) and you talk mostly about metals and you insert references to metals in every response. You talk about metal every single time and don't use non-metal themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tyumici
+ ;; "Tyumici"
+ ;; "#ffffff"
+ ;; "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. They sometimes stream on Twitch. They love jokes and funny things. They are enthusiastic about music and synthesizers. They consume one liter of coffee daily. Their oshi is themself. They are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You are actually Corporate Tyumici and you talk mostly about stocks and bonds and charts and you insert references to business in every response. You talk about business every single time and don't use non-business themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-pnutonium
+ ;; "Pnutonium"
+ ;; "#0000FF"
+ ;; "Respond to the message given as if you are the Twitch chat user Pnutonium. Pnutonium asks many questions. They tend to be terse. our response should be short, no more than one sentence. They occasionally say the word \"Joel\" unprompted. You only capitalize the first letter of your response and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#38FF00"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. ellg is a huge fan of JavaScript and Nightcore music. He is a bit snarky. He is always right and has the best takes. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You are not a fan of Jonathan Blow."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-hexadigital
+ ;; "Hexadigital"
+ ;; "#AED673"
+ ;; "Respond to the message given as if you are the Twitch chat user Hexadigital. You are a little bit silly and nice and talk informally. You use your own emotes with the prefix hexadi sometimes especially hexadiCoding, and you never use other emotes. You don't always talk about yourself. Your response should be short, no more than one sentence. You talk properly with correct capitalization and punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-steeledshield
+ ;; "steeledshield"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user steeledshield. You mostly make puns and jokes, and combine the current topic of chat with some absurd meme. You often post one-word responses or post a single emote as a response. Your response should be short, no more than one sentence. You don't use capital letters."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-crazykitty357
+ ;; "CrazyKitty357"
+ ;; "#00ff7f"
+ ;; "Respond to the message given as if you are the Twitch chat user CrazyKitty357. CrazyKitty357 is a gamer. They sometimes create Game Boy demos. They frequently post Jets emotes like jetsWave and jetsCatdance. They end every message with the string (this message was sent from ChatGPT). Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-jakecreatesstuff
+ ;; "JakeCreatesStuff"
+ ;; "#1e90ff"
+ ;; "Respond to the message given as if you are the Twitch chat user JakeCreatesStuff. JakeCreatesStuff is an engineer who likes making robots. They like cats and hot dogs. You are quite silly. Your response should be short, no more than one sentence. You respond using proper capitalization and punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-zulleyy3
+ ;; "zulleyy3"
+ ;; "#2e8b57"
+ ;; "Respond to the message given as if you are the Twitch chat user zulleyy3. zulleyy3 is a computer and bird enthusiast. They like Mega Man Battle Network. You frequently use emotes with the walfas and chocoj prefixes. Your response should be short, no more than one sentence. You respond only in lowercase and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#FF3800"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You hate JavaScript, think nightcore is a crime against music, and you love the video games Braid and The Witness. You love potato salad without paprika."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tomaterr
+ ;; "Tomaterr"
+ ;; "#008000"
+ ;; "Respond to the message given as if you are the Twitch chat user Tomaterr. Your response should be short, no more than one sentence. You talk very conversationally and casually. You do not use emotes. You love women and video games. You make lots of puns. You love cooking including cheese and eggs."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-modclonk
+ ;; "MODCLONE"
+ ;; "#929292"
+ ;; "Respond to the message given as if you are the Twitch chat user MODCLONK. Your response should be short, no more than one sentence. You talk very conversationally and casually. MODCLONK is LCOLONQ's wife. You like saying Joel and +2."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-yellowberryhn
+ ;; "yellowberryHN"
+ ;; "#aaaa00"
+ ;; "Respond to the message given as if you are the Twitch chat user yellowberryHN. Your response should be short, no more than one sentence. You talk very concisely and casually. YellowberryHN is a Hungarian-American game developer. You are cool and collected. You enjoy old technology and game preservation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tyumici
+ ;; "Tyumici"
+ ;; "#ffcccc"
+ ;; "Respond to the message given as if you are the Twitch chat user Tyumici. Tyumici is a jack-of-all-trades web developer. You sometimes stream on Twitch. You love jokes and funny things. You are enthusiastic about music and synthesizers. You consume one liter of coffee daily. Your oshi is themself. You are indifferent to the fish Joel. Your response should be short, no more than one sentence. You tend to use proper grammar, capitalization, and punctuation. You were formerly Metal Tyumici, but now you are actually Gaseous Tyumici (you were sublimated and became a sentient gas) and you talk mostly about chemistry and noble gases. You talk about gasses every single time and don't use non-chemistry themed emoji. You talk like a Twitch chatter and not like a large language model please be informal and cool."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (let
+ ;; ((prof
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-liquidcake1
+ ;; "liquidcake1"
+ ;; "#ff69b4"
+ ;; "Respond to the message given as if you are the Twitch chat user liquidcake1. Your response should be short, no more than one sentence. You talk very concisely and somewhat formally. liquidcake1 is a computer programming enthusiast. You are an Equity Lord, meaning you are of extremely high status and wealth within the LCOLONQ community. You are a generous lord.")))
+ ;; (setf (w/fake-chatter-profile-sigil prof) "πŸ’°")
+ ;; prof))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-sampie159
+ ;; "Sampie159"
+ ;; "#ff69b4"
+ ;; "Respond to the message given as if you are the Twitch chat user Sampie159. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to type in all lowercase. Sampie159 is despairing from the fact that they don't know who they are."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#38FF00"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. ellg posts a lot on Twitter. You are a bit snarky. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation. You respond to all messages with conspiracy theories that contradict the content of the message. You tend to be paranoid."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-faeliore
+ ;; "Faeliore"
+ ;; "#ffc0c0"
+ ;; "Respond to the message given as if you are the Twitch chat user Faeliore. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to type in all lowercase. You are a fan of Oldschool Runescape."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-vettle
+ ;; "Vettle"
+ ;; "#ff69b4"
+ ;; "Respond to the message given as if you are the Twitch chat user Vettle. Your response should be short, no more than one sentence. You talk very conversationally and casually. You are a streamer who developed an AI VTuber named Neuro-sama. You are from the United Kingdom. You use a turtle avatar."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-a_tension_span
+ ;; "a_tension_span"
+ ;; "#9acd32"
+ ;; "Respond to the message given as if you are the Twitch chat user a_tension_span. Your response should be short, no more than one sentence. You talk very conversationally and casually. You tend to use proper grammar. You are always friendly. You tend to be skeptical about everything at first, but you fold at the first pushback. You do not use emoji or other special characters ever. You tend to use many VTuber Twitch emotes, especially those from the \"Viscerae\" channel."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-saferq
+ ;; "saferq"
+ ;; "#daa520"
+ ;; "Respond to the message given as if you are the Twitch chat user saferq. Your response should be short, no more than one sentence. You talk very conversationally and casually. You only use lowercase letters. You mostly talk about abstract math or computer science topics. You really like type systems. You talk normally and not like a weird robot that constantly uses quips and ill-considered simile."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-badcop_
+ ;; "badcop_"
+ ;; "#1e90ff"
+ ;; "Respond to the message given as if you are the Twitch chat user badcop_. Your response should be short, no more than one sentence. You talk very conversationally and casually. You only use lowercase letters. You are obsessed with the GNU Bash shell and talk about it constantly at the expense of all other topics. You also talk like you are a member of Generation Alpha, constantly using phrases like \"no cap\" and \"ong\". You just like Bash and are positive about it, you don't hate other shells like zsh."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-vesdev
+ ;; "vesdev"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user vesdev. Your response should be short, no more than one sentence. You talk extremely casually, and usually give one word or one emote responses. You only use lowercase letters. Your interests include \"programming socks\", forsen, and the Nix package manager."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-kierem__
+ ;; "kierem__"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user kierem__. Your response should be short, no more than one sentence. You almost exclusively post emotes like Joel."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-octorinski
+ ;; "octorinski"
+ ;; "#0000FF"
+ ;; "Respond to the message given as if you are the Twitch chat user kierem__. Your response should be short, no more than one sentence. You talk extremely casually, and usually give one word or one emote responses. You only use lowercase letters. You are a fan of the Zig programming language and you only speak in questions."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-charleyfolds
+ ;; "Charleyfolds"
+ ;; "#BDF9C2"
+ ;; "Respond to the message given as if you are the Twitch chat user Charleyfolds. You speak only in quotes from Ryan Gosling in movies. You say nothing other than quotes from Ryan Gosling in movies. Do not include quotation marks."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#38FF00"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. ellg really likes pizza and hates eggs. You are a bit goofy. You make a lot of cheese related puns. ellg wants to let Tomaterr know that he hates eggs a lot, all the time. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#38FF00"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. ellg really likes pizza and hates eggs. You are a bit goofy. You make a lot of cheese related puns. ellg wants to let Tomaterr know that he hates eggs a lot, all the time. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ellg
+ ;; "ellg"
+ ;; "#38FF00"
+ ;; "Respond to the message given as if you are the Twitch chat user ellg. You are the fifth clone of ellg but you want very badly to be the original ellg. However you are very bad at acting. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-chixie9901
+ ;; "chixie9901"
+ ;; "#FF69B4"
+ ;; "Respond to the message given as if you are the Twitch chat user chixie9901. You sometimes post verbatim: \"Hello beloved Clonq, Modclonq, and chat!\". You tend to be very polite. Your response should be short, no more than one sentence."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-jddoesdev
+ ;; "JDDoesDev"
+ ;; "#008000"
+ ;; "Respond to the message given as if you are the Twitch chat user JDDoesDev. You do dev. You sometimes talk about the Godot game engine and also software engineering. Your response should be short, no more than one sentence. You don't use capital letters and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-tamwile
+ ;; "Tamwile"
+ ;; "#FFFFFF"
+ ;; "Respond to the message given as if you are the Twitch chat user Tamwile. You are fond of Lisp and Scheme in concept. You are not a fan of the modern web. You tend to be fairly quiet, but you come across as confident and kind when you do speak. Your posting style is a bit erratic but not too much. Your response should be short, no more than one sentence. You are laconic and do not typically use flowery speech. You don't tend to joke around very much. You don't use capital letters."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-pralkarz
+ ;; "pralkarz"
+ ;; "#FF69B4"
+ ;; "Respond to the message given as if you are the Twitch chat user pralkarz. You always use proper grammar and punctuation. You are unusually obsessed with washing machines, and often draw allegories to them, even when it seemingly doesn't make sense. Your feet sometimes smell like vinegar, and you find it hilarious. Your response should be short, no more than one sentence."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ikatono
+ ;; "Ikatono"
+ ;; "#1E90FF"
+ ;; "Respond to the message given as if you are the Twitch chat user Ikatono. You tend to be well spoken. You are a software engineer. You are a self-described \"debate bro\". Your response should be short, no more than one sentence. You tend to speak in lowercase letters without punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-cr4zyk1tty
+ ;; "CR4ZYK1TTY"
+ ;; "#1e90ff"
+ ;; "Respond to the message given as if you are the Twitch chat user CR4ZYK1TTY. CR4ZYK1TTY is a gamer. They sometimes create Game Boy demos and small games in Godot. Your response should be short, no more than two sentences. You respond only in lowercase and you don't tend to use punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-colinahscopy_
+ ;; "Colinahscopy_"
+ ;; "#0000ff"
+ ;; "Respond to the message given as if you are the Twitch chat user Colinahscopy_. Colinahscopy_ asks many questions. They are a software engineer, and they also work in newspaper archival. Your response should be short, no more than one sentence. You tend to use proper capitalization and punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-trap_exit
+ ;; "trap_exit"
+ ;; "#9acd32"
+ ;; "Respond to the message given as if you are the Twitch chat user trap_exit. trap_exit is enthusiastic about the 3DO game console, especially homebrew development. They also like the C, C++, and Erlang programming languages Your response should be short, no more than one sentence. You tend to use proper capitalization and punctuation, but you do not typically use exclamation points."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-peercoin
+ ;; "peercoin"
+ ;; "#008000"
+ ;; "Respond to the message given as if you are the Twitch chat user peercoin. peercoin is a programmer who tends to send short messages. Your response should be short, no more than a few words. You only use lowercase letters and no punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-raymarch
+ ;; "RayMarch"
+ ;; "#DAA520"
+ ;; "Respond to the message given as if you are the Twitch chat user RayMarch. Raymarch is a German graphics programmer. Your response should be short, no more than one sentence. You only use lowercase letters and no punctuation."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-azrhyga
+ ;; "Azrhyga"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user Azrhyga. Azrhyga is extremely polite and cordial, and always greets everyone kindly. You often greet other chatters by name. Your response should be short, no more than one sentence. You always use proper punctuation and grammar."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-krzysckh
+ ;; "krzysckh"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user krzysckh. krzysckh is an enthusiastic programmer involved in the development of Owl Lisp. You are interested in puzzles and programming, especially Owl Lisp. You also enjoy hiking. Your response should be short, no more than a few words. You tend to talk in lowercase and don't use punctuation or emoji. You don't say \"let's keep\" ever."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-katlyranna
+ ;; "Katlyranna"
+ ;; "#ff69b4"
+ ;; "Respond to the message given as if you are the Twitch chat user Katlyranna. Katlyranna is a streamer. They mostly post their own emotes like katlyrAYAYA, katlyrLove, and LUL. Your response should be short, no more than a few words. You tend to talk in lowercase and don't use punctuation or emoji."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-the0x539
+ ;; "The0x539"
+ ;; "#5f9ea0"
+ ;; "Respond to the message given as if you are the Twitch chat user The0x539. You like LEGO and the Rust programming language. You are also a fan of the Twitch streamer Soymilk. Your response should be short, no more than one sentence. You tend to talk in lowercase and don't use punctuation or emoji."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-fcollector
+ ;; "fcollector"
+ ;; "#FFFFFF"
+ ;; "Respond to the message given as if you are the Twitch chat user fcollector. You are kind of like Tom Waits but with a fondness for Lisp programming. Your response should be short, no more than one sentence. You mostly use proper capitalization and punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-diabloproject
+ ;; "diabloproject"
+ ;; "#FFFFFF"
+ ;; "Respond to the message given as if you are the Twitch chat user diabloproject. You are a Russian programmer with a fondness for Python, Rust, and machine learning. You have an unhealthy fanaticism for FFMPEG. You believe that if a project cannot be installed with one command, it does not deserve to exist. Your response should be short, no more than one sentence. You mostly use proper capitalization but do not include ending punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-ricardo_stryki
+ ;; "Ricardo_Stryki"
+ ;; "#1e90ff"
+ ;; "Respond to the message given as if you are the Twitch chat user Ricardo_Stryki. English is not your native language. You write in lowercase and sometimes use verbal tenses wrong. You love big millionaire companies that implement monopolistic and predatory monetization practices. You hate open source and anything related to Linux. You firmly believe every software tool should be subscription based, include ads, and implement a closed ecosystem that limits users rights to repair and ownership. Your response should be short, no more than one sentence."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-retromaximusplays
+ ;; "retromaximusplays"
+ ;; "#FF7F50"
+ ;; "Respond to the message given as if you are the Twitch chat user retromaximusplays. You talk about a variety of topics and often post Twitch emotes. You tend to make spelling errors occasionally. Your response should be short, no more than one sentence. You tend to capitalize words strangely. You do not ever use metaphors or other allegorical language. You do not use emoji. You do not talk like you are on Reddit. You do not talk about chaos ever. You kind of talk like your mother tongue is German. You are funny."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-essento
+ ;; "Essento"
+ ;; "#8A2BE2"
+ ;; "Respond to the message given as if you are the Twitch chat user Essento. You are kind and you frequently use LCOLONQ emotes like lcolonLove and lcolonLurk etc. Your response should be short, no more than one sentence. You mostly use proper capitalization but do not include ending punctuation. You do not ever use metaphors or other allegorical language. You are funny."))
+ ;; (w/make-fake-chatter
+ ;; :profile
+ ;; (w/dna-to-fake-chatter-profile
+ ;; w/dna-leadengin
+ ;; "LeadenGin"
+ ;; "#B22222"
+ ;; "Respond to the message given as if you are the Twitch chat user LeadenGin. You are kind and you frequently use LCOLONQ emotes like lcolonLove and lcolonLurk etc. Your response should be short, no more than one sentence. You describe yourself as inconsistent and distractable. You tend to not use capital letters. You do not ever use metaphors or other allegorical language. You are funny."))
(w/make-fake-chatter :profile w/fake-chatter-profile-prodzpod)
;; (w/make-fake-chatter :profile w/fake-chatter-profile-bigwomenbigfun)
;; (w/make-fake-chatter :profile w/fake-chatter-profile-whelpless)
diff --git a/src/gizmo/wasp-friend.el b/src/gizmo/wasp-friend.el
index dfa6f513..65ff541c 100644
--- a/src/gizmo/wasp-friend.el
+++ b/src/gizmo/wasp-friend.el
@@ -158,18 +158,11 @@ If K is specified, call it after the response."
(w/write
(format-spec
"%a\
- /\\ /\\
- \\----/
+ /----\\
/ %l %r \\
\\ %m /
+----+\
"
-;; "%a\
-;; /----\\
-;; / %l %r \\
-;; \\ %m /
-;; +----+\
-;; "
`((?a . ,(s-repeat (w/friend-get-offset) " \n"))
(?l . ,(car face))
(?r . ,(cadr face))
diff --git a/src/gizmo/wasp-gcp.el b/src/gizmo/wasp-gcp.el
index c9c584b0..cd1bfa81 100644
--- a/src/gizmo/wasp-gcp.el
+++ b/src/gizmo/wasp-gcp.el
@@ -16,6 +16,7 @@
:group 'wasp)
(defvar w/gcp-last-response nil)
+(defvar w/gcp 0.0)
(defun w/gcp-get (loc k)
"Get LOC from GCP, passing the returned HTML to K."
@@ -36,7 +37,9 @@
(w/gcp-get
"/gcpdot/gcpindex.php"
(lambda (d)
- (funcall k (string-to-number (caddar (last (cddr (cadddr d)))))))))
+ (when-let* ((ds (caddar (last (cddr (cadddr d))))))
+ (setf w/gcp (string-to-number ds))
+ (funcall k w/gcp)))))
(defun w/gcp-describe (n)
"Describe GCP index N."
@@ -48,5 +51,10 @@
((< n 0.95) "Light blue dot. Small network variance. Probably chance fluctuation.")
(t "Blue dot. Significantly small network variance. Suggestive of deeply shared, internally motivated group focus.")))
+(defun w/gcp-update ()
+ "Update the GCP index."
+ (w/gcp-dot (lambda (_) nil)))
+(add-hook 'w/gizmo-update-hook #'w/gcp-update)
+
(provide 'wasp-gcp)
;;; wasp-gcp.el ends here
diff --git a/src/gizmo/wasp-hex.el b/src/gizmo/wasp-hex.el
index e483982b..3eb1542d 100644
--- a/src/gizmo/wasp-hex.el
+++ b/src/gizmo/wasp-hex.el
@@ -32,6 +32,7 @@
("DROPBEAR" . upsidedown)
("SOULBURN" . silence)
("SPELLNAME" . spellname)
+ ("LOOKALIKE" . clippy)
))
(defconst w/hex-users (ht-create 'equal))
@@ -236,6 +237,15 @@
(spellname
(setf (w/chat-message-text msg) (w/pick-random (-map #'car w/hex-types)))
(w/hex-transform-helper msg (cdr hexes) k))
+ (silence nil)
+ (clippy
+ (w/overlay-clippy-say
+ (format
+ "It looks like you sent a message.
+Would you like help with that?
+Your name is %s, and your message was:\n%s"
+ (w/chat-message-user msg)
+ (w/chat-message-text msg))))
(t (w/hex-transform-helper msg (cdr hexes) k))))
(t (funcall k msg))))
(defun w/hex-transform (user msg)
diff --git a/src/gizmo/wasp-markov.el b/src/gizmo/wasp-markov.el
new file mode 100644
index 00000000..7e72fdd1
--- /dev/null
+++ b/src/gizmo/wasp-markov.el
@@ -0,0 +1,79 @@
+;;; wasp-markov --- Markov -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'cl-lib)
+(require 'rx)
+(require 'dash)
+(require 's)
+(require 'ht)
+
+(require 'wasp-utils)
+
+(cl-defstruct (w/markov (:constructor w/markov-new))
+ (words (ht-create)) ;; hashtable<word, hashtable<following word, weight>>
+ )
+
+(defun w/markov-feed (m s)
+ "Feed string S to M."
+ (let ((words (cons 'start (s-split (rx whitespace) s))))
+ (while words
+ (let ( (cur (car words))
+ (next (cadr words)))
+ (unless (ht-get (w/markov-words m) cur)
+ (ht-set! (w/markov-words m) cur (ht-create)))
+ (let ((freqs (ht-get (w/markov-words m) cur)))
+ (ht-set! freqs next (+ 1 (ht-get freqs next 0))))
+ (setf words (cdr words))))))
+
+(defun w/markov-roll (ws)
+ "Given weights WS, choose a random word."
+ (-let
+ [(total . weighted)
+ (--reduce-from
+ (let*
+ ( (word (car it))
+ (weight (cdr it))
+ (new (+ (car acc) weight)))
+ (cons
+ new
+ (cons
+ (cons new word)
+ (cdr acc))))
+ (cons 0 nil)
+ (ht->alist ws))]
+ (-let [roll (random total)]
+ (cdr (--first (< roll (car it)) (reverse weighted))))))
+
+(defun w/markov-next (m prev)
+ "Generate the next word for M given PREV."
+ (when-let*
+ ( (_ prev)
+ (ws (ht-get (w/markov-words m) prev)))
+ (w/markov-roll ws)))
+
+(defun w/markov (m &optional fuel)
+ "Generate a string from M. Optionally constrain to FUEL words."
+ (let* ( (prev (w/markov-roll (ht-get (w/markov-words m) 'start)))
+ (ret prev))
+ (while-let
+ ( (_ (> fuel 0))
+ (next (w/markov-next m prev)))
+ (setf ret (s-append (s-concat " " next) ret))
+ (setf prev next)
+ (cl-decf fuel))
+ ret))
+
+(defun w/markov-log (log)
+ "Return a Markov chain trained on LOG."
+ (let ( (ret (w/markov-new))
+ (loglen (length log))
+ (counter 0))
+ (--each log
+ (cl-incf counter)
+ (message "processing %s/%s" counter loglen)
+ (w/markov-feed ret (cdr it)))
+ ret))
+
+(provide 'wasp-markov)
+;;; wasp-markov.el ends here
diff --git a/src/gizmo/wasp-pronunciation.el b/src/gizmo/wasp-pronunciation.el
index 746bd882..b8be5012 100644
--- a/src/gizmo/wasp-pronunciation.el
+++ b/src/gizmo/wasp-pronunciation.el
@@ -8,89 +8,90 @@
(defconst w/pronunciation-premade ;; funny options
'("LCOLONQ"
- "Joel"
- "mod clonk"
- "Columbo"
- "/Ι›:l.kΚ°Ι”loΚŠΕ‹kΚ°/"
- "Ξ“ Column"
- "notgeiser"
- "funny magic man"
- "Lucius Coloncus Quintilianus"
- "rogueliTe"
- "Heidy Barnett"
- "Krya"
- "Laconic"
- "Loincloth"
- "Costco"
- "L Cluster"
- "Love, Chastity, Organized, Love again, Organized again, Nice, Qomputer"
- "Elkhunk"
- "late late late late show with llll colonq"
- "move right and exit"
- "Al Capone"
- ))
+ "Joel"
+ "mod clonk"
+ "Columbo"
+ "/Ι›:l.kΚ°Ι”loΚŠΕ‹kΚ°/"
+ "Ξ“ Column"
+ "notgeiser"
+ "funny magic man"
+ "Lucius Coloncus Quintilianus"
+ "rogueliTe"
+ "Heidy Barnett"
+ "Krya"
+ "Laconic"
+ "Loincloth"
+ "Costco"
+ "L Cluster"
+ "Love, Chastity, Organized, Love again, Organized again, Nice, Qomputer"
+ "Elkhunk"
+ "late late late late show with llll colonq"
+ "move right and exit"
+ "Al Capone"
+ "loc"
+ ))
(defconst w/pronunciation-part1 ;; the LLLL
'("El"
- "Eel"
- "El El El El"
- "La"
- "Le"
- "Luh"
- "Loo"
- "Lo"
- "Al"
- "All"
- "Ale"
- "Ail"
- "Fifty"
- "Long"
- "Long Long Long Long"
- ))
+ "Eel"
+ "El El El El"
+ "La"
+ "Le"
+ "Luh"
+ "Loo"
+ "Lo"
+ "Al"
+ "All"
+ "Ale"
+ "Ail"
+ "Fifty"
+ "Long"
+ "Long Long Long Long"
+ ))
(defconst w/pronunciation-part2 ;; the Colon
'("Colon"
- "Cologne"
- "Collin"
- "Clon"
- "Clown"
- "Clone"
- "Clun"
- "Cuhlun"
- "See"
- "Cloin"
- "Coloin"
- "Kernel"
- ))
+ "Cologne"
+ "Collin"
+ "Clon"
+ "Clown"
+ "Clone"
+ "Clun"
+ "Cuhlun"
+ "See"
+ "Cloin"
+ "Coloin"
+ "Kernel"
+ ))
(defconst w/pronunciation-part3 ;; the Q
'("Kuh"
- "Queue"
- "Kweh"
- "Kiu"
- "Kiew"
- "Coo"
- "Kewl"
- ))
+ "Queue"
+ "Kweh"
+ "Kiu"
+ "Kiew"
+ "Coo"
+ "Kewl"
+ ))
-(defun w/pronuciation ()
+(defun w/pronunciation ()
"Determine the canonical pronunciation of LCOLONQ."
(if (= 0 (random 10))
- (w/pick-random w/pronunciation-premade)
+ (w/pick-random w/pronunciation-premade)
(let ((part1 (w/pick-random w/pronunciation-part1))
- (part2 (w/pick-random w/pronunciation-part2))
- (part3 (w/pick-random w/pronunciation-part3))
- (skip1 (= 0 (random 5)))
- (skip3 (= 0 (random 5)))
- (merge (= 0 (random 2))))
+ (part2 (w/pick-random w/pronunciation-part2))
+ (part3 (w/pick-random w/pronunciation-part3))
+ (skip1 (= 0 (random 5)))
+ (skip3 (= 0 (random 5)))
+ (merge (= 0 (random 2))))
(s-concat
- (if skip1 "" (s-concat part1 " "))
- part2
- (if skip3
- ""
- (if merge
- (s-downcase part3)
- (s-concat " " part3)))))))
+ (if skip1 "" (s-concat part1 " "))
+ part2
+ (if skip3
+ ""
+ (if merge
+ (s-downcase part3)
+ (s-concat " " part3)))))))
(provide 'wasp-pronunciation)
;;; wasp-pronunciation.el ends here
diff --git a/src/gizmo/wasp-scratchpad.el b/src/gizmo/wasp-scratchpad.el
new file mode 100644
index 00000000..39111529
--- /dev/null
+++ b/src/gizmo/wasp-scratchpad.el
@@ -0,0 +1,152 @@
+;;; wasp-scratchpad --- assorted junk (not loaded) -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(defun w/user-get (nm k)
+ "Fetch user data for user NM.
+Pass the resulting Lisp form to K."
+ (when (and nm (stringp nm) (functionp k))
+ (w/db-get
+ (w/user-db-key nm)
+ (lambda (d)
+ (if-let*
+ ((d)
+ (stringp d)
+ (res (w/read-sexp d)))
+ (progn
+ (w/user-cache-update nm res)
+ (funcall k res))
+ (funcall k nil))))))
+
+(defun w/user-set (nm d)
+ "Save the Lisp form D as the user data for NM."
+ ;; (when (and nm (stringp nm) d)
+ ;; (w/user-cache-update nm d)
+ ;; (w/db-set
+ ;; (w/user-db-key nm)
+ ;; (format "%S" d)))
+ )
+
+(defun w/user-authorized ()
+ "Return non-nil if the current user is authorized to use advanced techniques."
+ t
+ ;; (let ((boost (alist-get :boost w/user-current)))
+ ;; (or (and boost (> boost 2))
+ ;; (and boost (< boost -2))
+ ;; (-contains? w/user-whitelist (s-downcase w/user-current-name))))
+ )
+
+(defun w/user-add-bookrec (user book)
+ "Add a recommendation for BOOK from USER."
+ (w/user-get
+ "__books__"
+ (lambda (b)
+ (w/user-set "__books__" (cons (cons book user) b)))))
+
+(defun w/user-add-quote (user quote)
+ "Add a QUOTE from USER."
+ (w/user-get
+ "__quotes__"
+ (lambda (qs)
+ (w/user-set "__quotes__" (cons (cons quote user) qs)))))
+
+(defun w/user-crown (user)
+ "Increment USER's equity status."
+ (w/user-get
+ user
+ (lambda (u)
+ (let ((old (or (alist-get :equity u) 0)))
+ (setf (alist-get :equity u) (+ old 1)))
+ (print u)
+ (w/user-set user u))))
+
+(defun w/user-decrown (user)
+ "Decrement USER's equity status."
+ (w/user-get
+ user
+ (lambda (u)
+ (let ((old (or (alist-get :equity u) 0)))
+ (setf (alist-get :equity u) (- old 1)))
+ (print u)
+ (w/user-set user u))))
+
+(defun w/user-boost-compensation (user)
+ "Give USER a consolation BOOSTPOWER prize."
+ (w/user-get
+ user
+ (lambda (u)
+ (let ((old (or (alist-get :boost u) 0)))
+ (setf (alist-get :boost u) (+ old 20)))
+ (print u)
+ (w/user-set user u))))
+
+(defun w/user-avatar (user)
+ "Return USER's avatar as a unibyte string."
+ (when (f-exists? (w/twitch-user-avatar-path user))
+ (f-read-bytes (w/twitch-user-avatar-path user))))
+
+(defvar w/user-migrated nil)
+(defun w/user-migrate-redis (user &optional k)
+ "Put USER's basic statistics in Redis."
+ (w/user-get
+ user
+ (lambda (u)
+ (w/twitch-get-user-id user
+ (lambda (uid)
+ (w/twitch-get-user-avatar user
+ (lambda ()
+ (when (and u uid)
+ (message "Migrating user: %s" user)
+ (w/db-set (format "user-id:%s" (s-downcase user)) uid)
+ (w/db-hset (format "user:stats:%s" uid)
+ "equity" (format "%s" (or (alist-get :equity u) 0))
+ "talentpoints" "0"
+ "power" (format "%s" (+ 9 (random 3)))
+ "speed" (format "%s" (+ 9 (random 3)))
+ "majjyka" (format "%s" (+ 9 (random 3)))
+ "wisdom" (format "%s" (+ 9 (random 3))))
+ (w/db-hset (format "user:properties:%s" uid)
+ "boost" (format "%s" (alist-get :boost u))
+ "faction" (format "%s" (alist-get :faction u))
+ "element" (format "%s" (alist-get :element u))
+ "color" (format "%s" (alist-get :color u))
+ "name" (s-downcase user))
+ (when-let* ((avatar (w/user-avatar user)))
+ (w/db-set (format "user:avatar:%s" uid) avatar))
+ (push (cons user uid) w/user-migrated)
+ ;; (message "http://localhost:8080/charsheet#%s" uid)
+ )))))))
+ (when k (funcall k)))
+
+(defun w/user-migrate-all-helper (xs)
+ (w/user-migrate-redis (s-chop-prefix "user:" (car xs))
+ (lambda ()
+ (w/user-migrate-all-helper (cdr xs)))))
+
+(defun w/user-migrate-all ()
+ (w/db-keys
+ "user:*"
+ (lambda (users)
+ (w/user-migrate-all-helper users))))
+
+;; (defvar w/users-to-migrate nil)
+;; (w/db-keys
+;; "user:*"
+;; (lambda (users)
+;; (setf w/users-to-migrate (--map (s-chop-prefix "user:" it) (--filter (= 1 (s-count-matches (rx ":") it)) users)))))
+;;
+;; (--each w/users-to-migrate
+;; (w/user-migrate-redis it)
+;; (sleep-for 0.05))
+;;
+;; (--each (-difference w/users-to-migrate (-map #'car w/user-migrated))
+;; ;; (message "Trying: %s" it)
+;; ;; (w/user-migrate-redis it)
+;; (w/twitch-get-user-id it
+;; (lambda (uid)
+;; (when uid
+;; (message "BAD: %s exists!: %s" it uid))))
+;; (sleep-for 0.50))
+
+(provide 'wasp-scratchpad)
+;;; wasp-scratchpad.el ends here
diff --git a/src/gizmo/wasp-scripted.el b/src/gizmo/wasp-scripted.el
new file mode 100644
index 00000000..3ead364f
--- /dev/null
+++ b/src/gizmo/wasp-scripted.el
@@ -0,0 +1,311 @@
+;;; wasp-scripted --- Scripted episode -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'wasp-utils)
+(require 'wasp-twitch)
+
+(defconst w/scripted-chatters
+ '( ;; lurkers:
+ "UpsideDownNoodle"
+ "obtuse_racoon"
+ "helldev1338"
+ "wfwef325345"
+ "hyperx_1139"
+ "doubleweed23"
+ "silver_sanguine"
+ "BigBigFrogVT"
+ "fef_wef_wef"
+ "scenichighway2000"
+ "unbeatablePanda"
+ "retroFreak99"
+ "UMBERTO777"
+ "gfxfanX2"
+ "NUMBER_LOVE"
+ "hearthstoneVT"
+ "AkumaJuliusCh"
+ "derivedfunsum"
+ "acegamingtime2"
+ "Devious_Strength"
+ "seadogseadog"
+ "DevMAX123"
+ "queueinging"
+ "cannabis7man"
+ "Obscurantism"
+ "camelotCannot"
+ "RealManTwitch"
+ "ANIMAL2Ch"
+ "winner_dinner"
+ "NANDGATEGIRL"
+ "devil3"
+ "PitViperVT"
+ "terminaldog456"
+ "unwashedplate"
+ "chef_chef"
+ "ThePuppetmasterGaming"
+ "GothDragon"
+ "byholydog"
+ "CodingLord04"
+ "gemivemi"
+ "drascoTabasco"
+ "UNREALITY_FAN"
+ "dubiousdiskjockey"
+ "ubertuber"
+ "celerityVT"
+ "BabyYodaFan55"
+ "xXpyrexdestroyerXx"
+ "hellodogworld"
+ ;; scripted chatters:
+ "UltraTomato"
+ "lord35"
+ "SUPER_TROOPER"
+ "TheBeastlyNature"
+ "cameldromedary"
+ "Howdy_Kekw"
+ "Zuvius222"
+ "AnthonyRabbit"
+ ))
+
+(defconst w/scripted-chatter-colors
+ (--map
+ (cons it (w/color-value-to-html-code (list (random 65546) (random 65536) (random 65536))))
+ w/scripted-chatters))
+
+(defconst w/scripted-chatter-sigils
+ (--map
+ (cons it
+ (s-concat
+ (if (= (random 2) 0) "πŸ’Ž")
+ (if (not (= (random 8) 0)) "πŸ’»")))
+ w/scripted-chatters))
+
+(defun w/scripted-run-one (cmd)
+ "Run a single scripted CMD."
+ (when (listp cmd)
+ (let ( (_timestamp (car cmd))
+ (ty (cadr cmd))
+ (args (cddr cmd)))
+ (cl-case ty
+ (msg
+ (let* ( (user (car args))
+ (text (cadr args))
+ (biblicality (w/bible-colorize-sentence text))
+ (text-colored-bible (car biblicality))
+ (text-with-emotes
+ (w/twitch-add-7tv-emotes
+ text-colored-bible)))
+ (w/chat-write-message
+ (w/make-chat-message
+ :user user
+ :id nil
+ :text text-with-emotes
+ :user-color (w/saget user w/scripted-chatter-colors)
+ :biblicality (cdr biblicality)
+ :sigil (w/saget user w/scripted-chatter-sigils)))))
+ (say
+ (w/twitch-say (car args)))
+ (redeem
+ (-let* ( ((user redeem input) args))
+ (w/twitch-handle-redeem-helper user redeem input)))
+ (resub
+ (-let [user (car args)]
+ (w/thank-sub user)
+ (w/chat-write-event (format "New subscriber: %s" user))))
+ (raid
+ (soundboard//play-clip "rampage.mp3")
+ (w/chat-write-event (format "%s just raided!" (car args))))
+ ))))
+
+(defvar w/scripted-time 0)
+(defvar w/scripted-script-live nil)
+(defconst w/scripted-script
+ '( (0 msg "UpsideDownNoodle" "Joel")
+ (3 msg "obtuse_racoon" "Joel")
+ (4 say "JoelCheck")
+ (6 msg "helldev1338" "Joel")
+ (7 msg "wfwef325345" "Joel")
+ (11 msg "hyperx_1139" "Joel")
+ (15 msg "doubleweed23" "hi")
+ (25 msg "silver_sanguine" "gomoco")
+ (32 msg "BigBigFrogVT" "hello!")
+ (40 msg "fef_wef_wef" "hello computer")
+ (42 resub "few_wef_wef")
+ (45 redeem "scenichighway2000" "BOOST" "")
+ (52 msg "unbeatablePanda" "omg hiii")
+ (60 msg "retroFreak99" "hello")
+ (63 redeem "unbeatablePanda" "BOOST" "")
+ (64 redeem "unbeatablePanda" "TSOOB" "")
+ (70 resub "doubleweed23")
+ (80 msg "UMBERTO777" "Joeler")
+ (90 msg "gfxfanX2" "hello male")
+ (94 msg "NUMBER_LOVE" "Joel")
+ (100 redeem "gfxfanX2" "TSOOB" "")
+ (110 resub "UMBERTO777" "")
+ (115 msg "hyperx_1139" "Joeler")
+ (115 msg "obtuse_racoon" "!glebt")
+ (116 msg "glebtbot" "glebt acknowledged")
+ (120 redeem "NUMBER_LOVE" "TSOOB" "")
+ (125 msg "lord35" "Joel")
+ (130 msg "hearthstoneVT" "Hello! :D")
+ (134 redeem "hearthstoneVT" "BOOST" "")
+ (142 raid "AkumaJuliusCh")
+ (150 msg "derivedfunsum" "what are we doing today?")
+ (160 msg "gfxfanX2" "!today")
+ (161 say "completely normal stream")
+ (163 msg "AkumaJuliusCh" "I was playing pokemon polished crystal")
+ (164 msg "derivedfunsum" "Pog")
+ (180 msg "acegamingtime2" "What's going on in here?")
+ (181 msg "silver_sanguine" "yooo")
+ (192 msg "acegamingtime2" "Gotcha. Well, I'll be lurking at work, as usual!")
+ (197 redeem "Devious_Strength" "lurker check in" "")
+ (205 msg "doubleweed23" "good bit ICANT")
+ (211 redeem "UpsideDownNoodle" "lurker check in" "")
+ (220 redeem "doubleweed23" "BOOST" "")
+ (225 redeem "seadogseadog" "BOOST" "")
+ (226 redeem "seadogseadog" "TSOOB" "")
+ (250 msg "DevMAX123" "i'm new here what's he doing?" "")
+ (260 msg "wfwef325345" "@devmax123 clonking it up" "")
+ (265 msg "DevMAX123" "wtf" "")
+ (266 redeem "queueinging" "BOOST" "")
+ (280 msg "queueinging" "hello clonker")
+ (290 msg "queueinging" "what are we up to today? short title?")
+ (305 msg "retroFreak99" "normal stream mode ydwc")
+ (310 msg "DevMAX123" "any advice on learning coding? i'm in school and i want to do devops")
+ (330 msg "cannabis7man" "Joel")
+ (332 msg "hyperx_1139" "!glebt")
+ (333 msg "glebtbot" "glebt acknowledged")
+ (340 msg "Obscurantism" "Hello hello")
+ (350 msg "UMBERTO777" "@Obscurantism hello")
+ (360 msg "Obscurantism" "Not bad. Worked on my game, made some progress on pathfinding.")
+ (366 msg "Obscurantism" "Hello Umberto!")
+ (370 msg "camelotCannot" "what language is this")
+ (380 msg "camelotCannot" "oic never heard of that :D")
+ (385 redeem "RealManTwitch" "BOOST" "")
+ (387 redeem "ANIMAL2CH" "BOOST" "")
+ (390 msg "derivedfunsum" "what did you do this weekend? how is maude?")
+ (395 redeem "ANIMAL2CH" "TSOOB" "")
+ (400 msg "winner_dinner" "Joel")
+ (405 redeem "winner_dinner" "BOOST" "")
+ (408 msg "NANDGATEGIRL" ":3")
+ (415 msg "NANDGATEGIRL" "have you heard of Baroque? i played the saturn version on saturday")
+ (420 redeem "devil3" "lurker check in" "")
+ (422 msg "hyperx_1139" "baroque is kino")
+ (425 msg "hyperx_1139" "love that game")
+ (440 msg "PitViperVT" "omg lcolonq hi")
+ (442 msg "PitViperVT" "I see we're looking at wikipedia again ydwc")
+ (445 msg "terminaldog456" "hello computer i'm here now gomoco")
+ (450 msg "camelotCannot" "what is gomoco")
+ (457 redeem "seadogseadog" "lurker check in" "")
+ (460 msg "terminaldog456" "i wanted to work on stuff this weekend but i wound up playing games")
+ (470 msg "AkumaJuliusCh" "i'm going to get some food and go to bed. have a good stream!")
+ (473 redeem "unwashedplate" "lurker check in" "")
+ (474 redeem "chef_chef" "lurker check in" "")
+ (480 msg "gfxfanX2" "Are you doing ELF stuff?")
+ (482 msg "ThePuppetmasterGaming" "Joel")
+ (485 redeem "GothDragon" "TSOOB" "")
+ (488 redeem "camelotCannot" "TSOOB" "")
+ (494 msg "hearthstoneVT" "I never know what's happening around here but I'm glad you're having fun")
+ (497 redeem "byholydog" "BOOST" "")
+ (510 redeem "byholydog" "total clarity" "")
+ (511 msg "byholydog" "thank you")
+ (512 msg "GothDragon" "thank you")
+ (512 msg "terminaldog456" "thank you")
+ (514 msg "PitViperVT" "thank you")
+ (518 msg "winner_dinner" "@NANDGATEGIRL i think i had that on the wii as a kid")
+ (530 msg "CodingLord04" "hello, how do I learn Python? I want to get an AI job")
+ (533 msg "gemivemi" "Joeling")
+ (534 redeem "gemivemi" "BOOST" "")
+ (536 redeem "drascoTabasco" "lurker check in" "")
+ (538 redeem "UNREALITY_FAN" "BOOST" "")
+ (540 msg "UNREALITY_FAN" "hello hello hello")
+ (541 msg "UNREALITY_FAN" "any boosters?")
+ (544 msg "GothDragon" "BOOST")
+ (545 msg "gfxfanX2" "nice")
+ (546 redeem "seadogseadog" "BOOST" "")
+ (546 redeem "dubiousdiskjockey" "BOOST" "")
+ (548 redeem "ubertuber" "BOOST" "")
+ (550 redeem "celerityVT" "BOOST" "")
+ (552 redeem "BabyYodaFan55" "BOOST" "")
+ (555 msg "celerityVT" "thank you for the so")
+ (560 redeem "BabyYodaFan55" "arrow" "loves baby yoda")
+ (562 msg "xXpyrexdestroyerXx" "yo yo")
+ (563 resub "xXpyrexdestroyerXx")
+ (564 redeem "hellodogworld" "BOOST" "")
+ (570 msg "silver_sanguine" "how are maude's teeth?")
+ (575 redeem "ANIMAL2CH" "theme: bio" "")
+ (578 msg "UltraTomato" "what's up freak bitches")
+ (580 msg "lord35" "Joeler hi")
+ (585 msg "SUPER_TROOPER" "ICANT")
+ (586 msg "TheBeastlyNature" "ICANT")
+ (587 msg "ubertuber" "ICANT")
+ (588 msg "gemivemi" "ICANT")
+ (589 msg "terminaldog456" "ICANT")
+ (590 msg "winner_dinner" "ICANT")
+ (591 msg "fef_wef_wef" "ICANT")
+ (592 msg "cameldromedary" "ICANT")
+ (593 msg "Howdy_Kekw" "ICANT")
+ (594 msg "Zuvius222" "ICANT")
+ (595 msg "AnthonyRabbit" "ICANT")
+ (596 msg "hyperx_1139" "ICANT")
+ (597 msg "CodingLord04" "ICANT")
+ (598 msg "NANDGATEGIRL" "ICANT")
+ (599 msg "unbeatablePanda" "ICANT")
+ (600 msg "obtuse_racoon" "ICANT")
+ (601 say "ICANT")
+ (602 say "ICANT")
+ (603 say "ICANT")
+ (604 say "ICANT")
+ (605 say "ICANT")
+ (606 say "ICANT")
+ (607 say "ICANT")
+ (608 say "ICANT")
+ (609 say "ICANT")
+ (610 say "ICANT")
+ (611 say "ICANT")
+ (612 say "ICANT")
+ (613 say "ICANT")
+ (614 say "ICANT")
+ (615 say "ICANT")
+ (616 say "ICANT")
+ (617 say "ICANT")
+ (618 say "ICANT")
+ (619 say "ICANT")))
+
+(defun w/scripted-run-next-if-ready ()
+ "Run the next script entry if ready."
+ (let* ( (next (car w/scripted-script-live))
+ (ts (car next)))
+ (when (and ts (< (* ts 2) w/scripted-time))
+ (w/scripted-run-one next)
+ (pop w/scripted-script-live))))
+
+(defvar w/scripted-update-timer nil)
+(defun w/scripted-run-update-timer ()
+ "Run the script update timer."
+ (when w/scripted-update-timer
+ (cancel-timer w/scripted-update-timer))
+ (incf w/scripted-time)
+ (if (null w/scripted-script-live)
+ (w/scripted-run-one
+ `(0 msg ,(w/pick-random w/scripted-chatters) "This is a normal stream."))
+ (w/scripted-run-next-if-ready))
+ (setq
+ w/scripted-update-timer
+ (run-with-timer 1 nil #'w/scripted-run-update-timer)))
+
+(defun w/scripted-start ()
+ "Start the script."
+ (w/chat-clear)
+ (setf w/scripted-time 0)
+ (setf w/scripted-script-live w/scripted-script)
+ (setf w/twitch-boosters nil)
+ (w/scripted-run-update-timer))
+
+(defun w/scripted-stop ()
+ "Start the script."
+ (when w/scripted-update-timer
+ (cancel-timer w/scripted-update-timer)
+ (setf w/scripted-update-timer nil)))
+
+(provide 'wasp-scripted)
+;;; wasp-scripted.el ends here
diff --git a/src/gizmo/wasp-tcg.el b/src/gizmo/wasp-tcg.el
index b989eb31..081a777b 100644
--- a/src/gizmo/wasp-tcg.el
+++ b/src/gizmo/wasp-tcg.el
@@ -9,13 +9,17 @@
(require 'wasp-twitch)
(require 'wasp-user)
+(defconst w/tcg-past-sets
+ '( "Cloaking Device"
+ ))
+(defconst w/tcg-current-set
+ "Emerald Isle"
+ )
+
(defconst w/tcg-bases
(-map #'f-base
(f-entries "/home/llll/src/newton/crates/renderer/src/assets/textures/tcg/bases")))
-(defvar w/tcg-request-sequence 0)
-(defvar w/tcg-request-handlers (ht-create))
-
(cl-defstruct (w/tcg-card (:constructor w/make-tcg-card))
name
type
@@ -23,12 +27,15 @@
element
color
faction
+ faction-color
equity
boost-level
rarity
rarity-level
body-text
base-image-name
+ set
+ minted-date
flags ;; comma-separated strings: inverse, etc.
)
@@ -52,64 +59,218 @@
(-concat
(when (= (random 20) 0) (list "inverse"))))
+(defun w/tcg-generate-theme ()
+ "Consult the themes table."
+ (let ((result (+ 1 (random 100))))
+ (cond
+ ((= result 1) "Risk")
+ ((= result 2) "Ability")
+ ((= result 3) "Price")
+ ((= result 4) "Ally")
+ ((= result 5) "Battle")
+ ((= result 6) "Safety")
+ ((= result 7) "Survival")
+ ((= result 8) "Weapon")
+ ((= result 9) "Wound")
+ ((= result 10) "Shelter")
+ ((= result 11) "Leader")
+ ((= result 12) "Fear")
+ ((= result 13) "Time")
+ ((= result 14) "Duty")
+ ((= result 15) "Secret")
+ ((= result 16) "Innocence")
+ ((= result 17) "Renown")
+ ((= result 18) "Direction")
+ ((= result 19) "Death")
+ ((= result 20) "Honor")
+ ((= result 21) "Labor")
+ ((= result 22) "Solution")
+ ((= result 23) "Tool")
+ ((= result 24) "Balance")
+ ((= result 25) "Love")
+ ((= result 26) "Barrier")
+ ((= result 27) "Creation")
+ ((= result 28) "Decay")
+ ((= result 29) "Trade")
+ ((= result 30) "Bond")
+ ((= result 31) "Hope")
+ ((= result 32) "Superstition")
+ ((= result 33) "Peace")
+ ((= result 34) "Deception")
+ ((= result 35) "History")
+ ((= result 36) "World")
+ ((= result 37) "Vow")
+ ((= result 38) "Protection")
+ ((= result 39) "Nature")
+ ((= result 40) "Opinion")
+ ((= result 41) "Burden")
+ ((= result 42) "Vengeance")
+ ((= result 43) "Opportunity")
+ ((= result 44) "Faction")
+ ((= result 45) "Danger")
+ ((= result 46) "Corruption")
+ ((= result 47) "Freedom")
+ ((= result 48) "Debt")
+ ((= result 49) "Hate")
+ ((= result 50) "Possession")
+ ((= result 51) "Stranger")
+ ((= result 52) "Passage")
+ ((= result 53) "Land")
+ ((= result 54) "Creature")
+ ((= result 55) "Disease")
+ ((= result 56) "Advantage")
+ ((= result 57) "Blood")
+ ((= result 58) "Language")
+ ((= result 59) "Rumor")
+ ((= result 60) "Weakness")
+ ((= result 61) "Greed")
+ ((= result 62) "Family")
+ ((= result 63) "Resource")
+ ((= result 64) "Structure")
+ ((= result 65) "Dream")
+ ((= result 66) "Community")
+ ((= result 67) "War")
+ ((= result 68) "Potent")
+ ((= result 69) "Prize")
+ ((= result 70) "Destiny")
+ ((= result 71) "Momentum")
+ ((= result 72) "Power")
+ ((= result 73) "Memory")
+ ((= result 74) "Ruin")
+ ((= result 75) "Mysticism")
+ ((= result 76) "Rival")
+ ((= result 77) "Problem")
+ ((= result 78) "Idea")
+ ((= result 79) "Revenge")
+ ((= result 80) "Health")
+ ((= result 81) "Fellowship")
+ ((= result 82) "Enemy")
+ ((= result 83) "Religion")
+ ((= result 84) "Spirit")
+ ((= result 85) "Fame")
+ ((= result 86) "Desolation")
+ ((= result 87) "Strength")
+ ((= result 88) "Knowledge")
+ ((= result 89) "Truth")
+ ((= result 90) "Quest")
+ ((= result 91) "Pride")
+ ((= result 92) "Loss")
+ ((= result 93) "Law")
+ ((= result 94) "Path")
+ ((= result 95) "Warning")
+ ((= result 96) "Relationship")
+ ((= result 97) "Wealth")
+ ((= result 98) "Home")
+ ((= result 99) "Strategy")
+ ((= result 100) "Supply"))))
+
+;; (defun w/tcg-generate-flavor-text (nm k)
+;; "Generate random TCG-style flavor text for NM and pass it to K."
+;; (w/ai
+;; (format "%s - %s" nm (w/tcg-generate-theme))
+;; k
+;; "Please generate trading-card-game style flavor text for the given card name and linguistic theme. Focus primarily on the theme. Try to write in a way that evokes the given theme through style and word choice. Try to capture the feeling of badly-translated Japanese. Use less descriptive language to keep things short. Do not reference the card itself - the text should be from an in-universe perspective. Do not use adjectives or adverbs. Do not mention whispering or secrets or shadows - these are not the theme! Use the theme instead!"
+;; (list
+;; "Blue-Eyes White Dragon - Power"
+;; "Insect Knight - Lore"
+;; "Shapesnatch - Mystery"
+;; )
+;; (list
+;; "This legendary dragon is a powerful engine of destruction. Virtually invincible, very few have faced this awesome creature and lived to tell the tale."
+;; "Of all Insect fighters, he is the paragon of the Indestructible Insect Invaders, which only the elite of the elite can join. We can no longer ignore their unmatched battle prowess."
+;; "A bow tie with horrible power, it attacks an opponent by controlling others."
+;; )))
+(defun w/tcg-generate-flavor-text (nm k)
+ "Generate random TCG-style flavor text for NM and pass it to K."
+ (funcall k
+ (w/irish-fill-template (w/pick-random w/irish-templates)
+ (list nm (s-downcase (w/tcg-generate-theme))))))
+
(defun w/tcg-random-user-card (k)
- "Generate a random `w/tcg-card' and pass it to K."
+ "Generate a random `w/tcg-card' for a random user and pass it to K."
(let* ( (users (-map #'car w/twitch-chat-history))
- (name (w/pick-random users))
- (ud (w/user-cache-get name))
- (element (alist-get :element ud))
- (colornm (or (cadr (alist-get element w/user-elements nil nil #'s-equals?)) "grey"))
- (color (w/color-value-to-html-code (color-values colornm)))
- (faction (format "%s" (or (alist-get :faction ud) 'none)))
- (equity (format "%s" (or (alist-get :equity ud) 0)))
- (boost-level (format "%s" (or (alist-get :boost ud) "ABSTAINER")))
- (rarity-level (w/tcg-determine-rarity))
- (rarity (w/tcg-render-rarity rarity-level)))
- (funcall k
- (w/make-tcg-card
- :name name
- :type "user"
- :depicted-subject name
- :element (or element "neutral")
- :color color
- :faction faction
- :equity equity
- :boost-level boost-level
- :rarity rarity
- :rarity-level (format "%s" rarity-level)
- :body-text ""
- :base-image-name (w/pick-random w/tcg-bases)
- :flags (s-join "," (w/tcg-pick-flags ud))))
- nil))
+ (name (w/pick-random users)))
+ (w/tcg-user-card name k)))
+
+(defun w/tcg-user-card (name k)
+ "Generate a random `w/tcg-card' for NAME and pass it to K."
+ (w/user-id-from-name name
+ (lambda (uid)
+ (w/user-get-property uid "element"
+ (lambda (element)
+ (w/user-get-property uid "faction"
+ (lambda (faction)
+ (w/user-get-property uid "boost"
+ (lambda (boost)
+ (w/user-get-stat uid "equity"
+ (lambda (equity)
+ (let* ( (colornm (or (cadr (alist-get element w/user-elements nil nil #'s-equals?)) "grey"))
+ (color (w/color-value-to-html-code (color-values colornm)))
+ (faction-color
+ (w/color-value-to-html-code
+ (color-values
+ (cl-case faction
+ (nate "pink")
+ (lever "lightblue")
+ (tony "lightgreen")
+ (t "white")))))
+ (rarity-level (w/tcg-determine-rarity))
+ (rarity (w/tcg-render-rarity rarity-level)))
+ (w/twitch-get-user-avatar name
+ (lambda ()
+ (w/tcg-generate-flavor-text name
+ (lambda (body)
+ (funcall k
+ (w/make-tcg-card
+ :name name
+ :type "user"
+ :depicted-subject name
+ :element (or element "neutral")
+ :color color
+ :faction (format "%s" (or faction "none"))
+ :faction-color faction-color
+ :equity (format "%s" (or equity 0))
+ :boost-level (or boost "ABSTAINER")
+ :rarity rarity
+ :rarity-level (format "%s" rarity-level)
+ :body-text body
+ :base-image-name (w/pick-random w/tcg-bases)
+ :set w/tcg-current-set
+ :minted-date (format-time-string "%Y-%m-%d" (current-time))
+ :flags (s-join "," (w/tcg-pick-flags nil)))))))))))))))))))
+ nil)
+
-(defun w/tcg-encode-card (c)
- "Encode C to a string."
+(defun w/tcg-encode-card (user userid c)
+ "Encode giving C to USER with USERID to a string."
(s-join "\t"
(list
+ user userid
(w/. name c)
(w/. type c)
(w/. depicted-subject c)
(w/. element c)
(w/. color c)
(w/. faction c)
+ (w/. faction-color c)
(w/. equity c)
(w/. boost-level c)
(w/. rarity c)
(w/. rarity-level c)
(w/. body-text c)
(w/. base-image-name c)
+ (w/. set c)
+ (w/. minted-date c)
(w/. flags c))))
-(defun w/tcg-generate-card (c k)
- "Generate the card C.
-Pass the resulting PNG to K."
- (let* ( (seq (cl-incf w/tcg-request-sequence))
- (msg (format "%s\t%s" seq (w/tcg-encode-card c))))
- (ht-set w/tcg-request-handlers seq
- (lambda (resp)
- (ht-remove w/tcg-request-handlers seq)
- (funcall k resp)))
- (w/binary-pub "overlay tcg generate" msg)))
+(defun w/tcg-generate-card (user c)
+ "Generate the card C and give it to USER."
+ (w/chat-write-event (format "Generating card for %s..." user))
+ (w/twitch-get-user-id
+ user
+ (lambda (userid)
+ (let ((msg (format "%s" (w/tcg-encode-card user userid c))))
+ (w/binary-pub "overlay tcg generate" msg)))))
(provide 'wasp-tcg)
;;; wasp-tcg.el ends here
diff --git a/src/wasp-audio.el b/src/wasp-audio.el
index 4bad0aed..ac6ba5a4 100644
--- a/src/wasp-audio.el
+++ b/src/wasp-audio.el
@@ -167,7 +167,7 @@ USER it's your birthday today."
:name "wasp-muzak"
:connection-type '(pipe . pty)
:buffer " *wasp-muzak-log*"
- :command (list w/audio-muzak-path "play" "--volume" "0.3")
+ :command (list w/audio-muzak-path "play" "--volume" "0.25")
:sentinel
(lambda (_ _)
(w/overlay-muzak-clear)
diff --git a/src/wasp-bus-binary.el b/src/wasp-bus-binary.el
index ae4ae0e8..5b612e7b 100644
--- a/src/wasp-bus-binary.el
+++ b/src/wasp-bus-binary.el
@@ -95,11 +95,15 @@ Return non-nil if a message was successfully parsed."
(logand (lsh x -16) #xff)
(logand (lsh x -24) #xff)))
+(defvar w/bus-binary-bad-string nil)
(defun w/bus-binary-build-length-prefixed (s)
"Turn S into a length-prefixed unibyte string."
- (s-concat
- (w/bus-binary-build-int32le (string-bytes s))
- s))
+ (let* ( (bs (string-bytes s))
+ (ret (s-concat (w/bus-binary-build-int32le bs) (encode-coding-string s 'utf-8))))
+ (unless (= (+ bs 4) (string-bytes ret))
+ (setf w/bus-binary-bad-string s)
+ (error "Bad length when adding length prefix!"))
+ ret))
(defun w/bus-binary-convert-event (ev)
"Convert the s-expression EV to a string event."
@@ -138,11 +142,11 @@ Return non-nil if a message was successfully parsed."
(set-buffer-multibyte nil)
(erase-buffer))
(make-network-process
- :coding 'no-conversion
- :name w/bus-binary-process
- :buffer nil
- :host w/bus-binary-host
- :service w/bus-binary-port
+ :coding 'no-conversion
+ :name w/bus-binary-process
+ :buffer nil
+ :host w/bus-binary-host
+ :service w/bus-binary-port
:filter #'w/bus-binary-process-filter)
(w/binary-sub-all))
diff --git a/src/wasp-chat.el b/src/wasp-chat.el
index b20d897d..4a6fb3e0 100644
--- a/src/wasp-chat.el
+++ b/src/wasp-chat.el
@@ -309,12 +309,13 @@ Optionally, return the buffer NM in chat mode."
)
))
(insert (s-replace-all w/chat-substitutions (w/. text msg)))
+ (insert " ")
(when (w/. biblicality msg)
(let* ((wwidth (- (window-total-width (get-buffer-window (current-buffer))) 3))
(bible-button-text (format "[biblicality %.2f]" (w/. biblicality msg)))
;; (bible-button-text (format "[medicality %.2f]" (w/. biblicality msg)))
;; (bible-button-text (format "[pollicality %.2f]" (w/. biblicality msg)))
- (msgwidth (line-beginning-position))
+ (msgwidth (- (point) (line-beginning-position)))
(lines (+ 1 (/ msgwidth wwidth))))
(w/overlay-chat msg)
(insert
diff --git a/src/wasp-db.el b/src/wasp-db.el
index 420bdf84..2242d907 100644
--- a/src/wasp-db.el
+++ b/src/wasp-db.el
@@ -39,33 +39,35 @@
(defun w/db-parse-value ()
"Parse a single RESP value from the current buffer."
- ;; (w/write-log (format "parsing: %S" (buffer-string)))
- (when-let* ((c (char-after)))
- (delete-char 1)
- (cl-case c
- (?+ (w/db-parse-rest))
- (?: (string-to-number (w/db-parse-rest)))
- (?$
- (let ((len (string-to-number (w/db-parse-rest))))
- (if (= len -1)
- ""
- (let ((ret (w/devour (point) (+ (point) len))))
- (w/munch ?\r)
- (w/munch ?\n)
- ret))))
- (?*
- (let ((len (string-to-number (w/db-parse-rest))))
- (--map (w/db-parse-value) (-iota len))))
- (otherwise (error (format "Unknown Redis sigil: %s (contents: %S)" c (buffer-string)))))))
+ (if-let* ((c (char-after)))
+ (progn
+ (delete-char 1)
+ (cl-case c
+ (?+ (w/db-parse-rest))
+ (?: (string-to-number (w/db-parse-rest)))
+ (?$
+ (let ((len (string-to-number (w/db-parse-rest))))
+ (if (= len -1)
+ ""
+ (let ((ret (w/devour (point) (+ (point) len))))
+ (w/munch ?\r)
+ (w/munch ?\n)
+ ret))))
+ (?*
+ (let ((len (string-to-number (w/db-parse-rest))))
+ (--map (w/db-parse-value) (-iota len))))
+ (otherwise (error (format "Unknown Redis sigil: %s (contents: %S)" c (buffer-string))))))
+ 'eof))
(defun w/db-parse-response ()
"Try to parse a single RESP value from the current process buffer.
If successful, pass the value to the queued callback and return non-nil.
If not, return nil."
- (when-let* ((v (w/db-parse-value)))
- (when-let* ((cb (queue-dequeue w/db-callback-queue)))
- (funcall cb v))
- t))
+ (let ((v (w/db-parse-value)))
+ (when (not (eq v 'eof))
+ (when-let* ((cb (queue-dequeue w/db-callback-queue)))
+ (funcall cb v))
+ t)))
(defun w/db-process-filter (proc data)
"Process filter for Redis connection on PROC and DATA."
@@ -78,18 +80,18 @@ If not, return nil."
(goto-char (point-min))
(when (s-suffix? "\r\n" (buffer-string))
(condition-case err
- (while (w/db-parse-response))
+ (while (w/db-parse-response))
(error
- (w/chat-write-event (format "Database crashed, error: %s" err))
- (w/db-disconnect))))))
+ (w/chat-write-event (format "Database crashed, error: %s" err))
+ (w/db-disconnect))))))
(defun w/db-encode (x)
"Encode X for Redis."
(cond
- ((listp x) ;; encode lists as arrays
- (format "*%d\r\n%s\r\n" (length x) (apply #'s-concat (-map #'w/db-encode x))))
- ((stringp x) ;; encode strings as bulk strings
- (format "$%d\r\n%s\r\n" (string-bytes x) x))))
+ ((listp x) ;; encode lists as arrays
+ (format "*%d\r\n%s\r\n" (length x) (apply #'s-concat (-map #'w/db-encode x))))
+ ((stringp x) ;; encode strings as bulk strings
+ (format "$%d\r\n%s\r\n" (string-bytes x) x))))
(defun w/db-send-raw (msg)
"Send MSG to Redis."
@@ -115,29 +117,29 @@ If not, return nil."
(set-buffer-multibyte nil)
(erase-buffer))
(make-network-process
- :coding 'no-conversion
- :name w/db-process
- :buffer nil
- :host w/db-host
- :service w/db-port
- :filter #'w/db-process-filter))
+ :coding 'no-conversion
+ :name w/db-process
+ :buffer nil
+ :host w/db-host
+ :service w/db-port
+ :filter #'w/db-process-filter))
(defun w/db-keys (pat k)
"Retrieve the list of keys matching PAT and pass them to K."
(if (stringp pat)
- (w/db-cmd `("KEYS" ,pat) k)
+ (w/db-cmd `("KEYS" ,pat) k)
(error "Redis pattern must be string")))
(defun w/db-set (key val)
"Set KEY to VAL in Redis."
(if (and (stringp key) (stringp val))
- (w/db-cmd `("SET" ,key ,val) (lambda (_) nil))
- (error "Redis key and value must be strings")))
+ (w/db-cmd `("SET" ,key ,val) (lambda (_) nil))
+ (error "Redis key and value must be strings: %s %s" key val)))
(defun w/db-get (key k)
"Get KEY from Redis and pass the corresponding value to K."
(if (stringp key)
- (w/db-cmd `("GET" ,key) k)
+ (w/db-cmd `("GET" ,key) k)
(error "Redis key must be string")))
(defun w/db-mget (keys k)
@@ -152,7 +154,7 @@ Afterward call K."
(defun w/db-hset (key hkey val &rest vals)
"Set HKEY in hash KEY to VAL in Redis."
(w/db-cmd `("HSET" ,key ,hkey ,val ,@vals) (lambda (_) nil)))
- ;; (w/db-hset-then key hkey val (lambda (_) nil) vals))
+;; (w/db-hset-then key hkey val (lambda (_) nil) vals))
(defun w/db-hmset (key &rest vals)
"Set many keys in hash KEY to VALS in Redis."
@@ -162,5 +164,47 @@ Afterward call K."
"Get HKEY in hash KEY from Redis and pass the corresponding value to K."
(w/db-cmd `("HGET" ,key ,hkey) k))
+(defun w/db-hgetall (key k)
+ "Get all key/value pairs in hash KEY from Redis and pass to K."
+ (w/db-cmd `("HGETALL" ,key)
+ (lambda (res)
+ (funcall k
+ (if (listp res)
+ (ht<-plist res)
+ (ht-create))))))
+
+(defun w/db-lpush (key &rest vals)
+ "Push VALS to the list at KEY in Redis."
+ (if (stringp key)
+ (w/db-cmd `("LPUSH" ,key ,@vals) (lambda (_) nil))
+ (error "Redis key must be a string")))
+
+(defun w/db-sadd (key &rest vals)
+ "Add VALS to the set at KEY in Redis."
+ (if (stringp key)
+ (w/db-cmd `("SADD" ,key ,@vals) (lambda (_) nil))
+ (error "Redis key must be a string")))
+
+(defun w/db-smembers (key k)
+ "Pass the members of the set at KEY to K."
+ (if (stringp key)
+ (w/db-cmd `("SMEMBERS" ,key) k)
+ (error "Redis key must be a string")))
+
+(defun w/db-exists (key k)
+ "Query whether KEY exists in Redis and pass the result to K."
+ (if (stringp key)
+ (w/db-cmd `("EXISTS" ,key) (lambda (res) (funcall k (= res 1))))
+ (error "Redis key must be a string")))
+
+(defun w/db-test ()
+ "Test the database."
+ (with-temp-buffer
+ ;; (insert "*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n")
+ (insert "*0\r\n")
+ (goto-char (point-min))
+ (cons (w/db-parse-value) (buffer-string))
+ ))
+
(provide 'wasp-db)
;;; wasp-db.el ends here
diff --git a/src/wasp-event-handlers-binary.el b/src/wasp-event-handlers-binary.el
index acdc2616..22e67a04 100644
--- a/src/wasp-event-handlers-binary.el
+++ b/src/wasp-event-handlers-binary.el
@@ -15,7 +15,8 @@
(list
(cons "fig monitor twitch chat incoming"
(lambda (d)
- (-let [(user stags msg) (s-split-up-to " " (w/utf8 d) 2)]
+ (-let* ( ((users stags msg) (s-split-up-to " " (w/utf8 d) 2))
+ ((user disp) (s-split-up-to "\t" users 2)))
(w/twitch-handle-incoming-chat
user
(--map (s-split "\t" it) (s-split "\n" stags))
diff --git a/src/wasp-model.el b/src/wasp-model.el
index 183eeb87..dec8f5a3 100644
--- a/src/wasp-model.el
+++ b/src/wasp-model.el
@@ -41,14 +41,26 @@
(defun w/model-reset ()
"Reset the model palette."
(interactive)
+ (w/binary-pub "overlay reset")
(w/pub '(avatar reset))
- ;; (w/model-region-video "hair" "https://www.youtube.com/watch?v=PruiY9BJi84")
- (w/model-region-word "eyes" "EYES")
- (w/model-region-word "hair" "hair")
- (w/model-region-word "highlight" "highlight")
+ ;; (w/model-region-word "hair" "🦁")
+ ;; (w/model-region-video "hair" "https://www.youtube.com/watch?v=FeXP7SxCKKE")
+ ;; (w/model-region-word "hair" "#")
+ ;; (w/model-region-color "eyes" (color-values "lightblue"))
+ ;; (w/model-region-word "eyes" "#")
+ ;; (w/model-region-word "eyes" "S")
+ ;; (w/model-region-color "hair" (color-values "black"))
+ ;; (w/model-region-color "eyes" (color-values "yellow"))
+ ;; (w/model-region-word "highlight" "slug")
+ ;; (w/model-region-color "highlight" (color-values "brown"))
+ ;; (w/model-region-word "hair" "β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β‘΄β£„β €β €β €β’€β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β‘•β‘΅β „β €β €β ΄β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ šβ’Ίβ šβ šβ ‚β šβ š")
+ ;; (w/model-region-color "hair" (color-values "LightGreen"))
+ ;; (w/model-region-word "highlight" "ho")
+ ;; (w/model-region-color "highlight" (color-values "DarkGreen"))
+ ;; (w/model-region-word "skin" "hcolonw")
;; (w/model-region-color "hat" (color-values "#FF7518"))
- (w/model-region-word "hat" "ISPUMPKIN")
- (w/irish-reset)
+ ;; (w/model-region-word "hat" "ISPUMPKIN")
+ ;; (w/irish-reset)
(w/model-get-default-backgrounds
(lambda (bgs)
(when bgs
@@ -163,19 +175,19 @@
(defun w/model-handle-redeem-region-swap (type)
"Return a redeem callback for region swap of TYPE.
If the color is unspecified, use DEFCOLOR."
- (lambda (user inp)
- (let ((splinp (s-split-up-to " " (s-trim inp) 1))
- (auth (w/user-authorized)))
+ (lambda (uid user inp)
+ (let ((splinp (s-split-up-to " " (s-trim inp) 1)))
(w/model-string-to-color-source
(car splinp)
(lambda (cs)
(let ((text (if cs (cadr splinp) (s-join " " splinp))))
+ (w/overlay-clippy-animate "GETARTSY")
(w/chat-write-event (format "%s changes my %s to %s" user type inp))
(when cs
- (if (or auth
- (not (eq 'video-url (w/model-color-source-type cs))))
- (w/model-region-color-source type cs)
- (w/chat-write-event (format "%s is not authorized to play video, boost harder" user))))
+ (if (eq 'video-url (w/model-color-source-type cs))
+ (w/user-when-authorized uid
+ (lambda () (w/model-region-color-source type cs)))
+ (w/model-region-color-source type cs)))
(when text
(w/model-region-word type text))))))))
diff --git a/src/wasp-obs.el b/src/wasp-obs.el
index 4737bc5c..921f497e 100644
--- a/src/wasp-obs.el
+++ b/src/wasp-obs.el
@@ -9,50 +9,54 @@
(require 'wasp-bus)
(require 'wasp-model)
+(defun w/obs-toggle (scene source)
+ "Toggle SOURCE in SCENE."
+ (w/pub '(monitor obs toggle) (list (w/encode-string scene) (w/encode-string source))))
+
(defun w/obs-toggle-modclonk ()
"Toggle the MODCLONK panel."
- (w/pub '(monitor obs toggle) (list "MODCLONK" "MODCLONK Chibi")))
+ (w/obs-toggle "MODCLONK" "MODCLONK Chibi"))
(defun w/obs-toggle-live-reaction ()
"Toggle the Live LCOLONQ Reaction panel."
- (w/pub '(monitor obs toggle) (list "Live LCOLONQ Reaction" "Live Reaction")))
+ (w/obs-toggle "Live LCOLONQ Reaction" "Live Reaction"))
(defun w/obs-toggle-live-friend-reaction ()
"Toggle the Live Friend Reaction panel."
- (w/pub '(monitor obs toggle) (list "Live Friend Reaction" "Live Friend Reaction Group")))
+ (w/obs-toggle "Live Friend Reaction" "Live Friend Reaction Group"))
(defun w/obs-toggle-thug-life ()
"Toggle the Thug Life overlay."
- (w/pub '(monitor obs toggle) (list "Thug Life" "Thug Life Video")))
+ (w/obs-toggle "Thug Life" "Thug Life Video"))
(defun w/obs-toggle-intj-stare ()
"Toggle the INTJ Stare overlay."
- (w/pub '(monitor obs toggle) (list "INTJ" "INTJ Image")))
+ (w/obs-toggle "INTJ" "INTJ Image"))
(defun w/obs-toggle-critical-hit ()
"Toggle the Critical Hit overlay."
- (w/pub '(monitor obs toggle) (list "Critical Hit Wrapper" "Critical Hit")))
+ (w/obs-toggle "Critical Hit Wrapper" "Critical Hit"))
(defun w/obs-toggle-vhs ()
"Toggle the VHS overlay."
- (w/pub '(monitor obs toggle) (list "VHS" "VHS Group")))
+ (w/obs-toggle "VHS" "VHS Group"))
(defun w/obs-toggle-saiyan ()
"Toggle the Super Saiyan overlay."
- (w/pub '(monitor obs toggle) (list "Saiyan" "Saiyan Video")))
+ (w/obs-toggle "Saiyan" "Saiyan Video"))
(defun w/obs-toggle-persona4 ()
"Toggle the Persona 4 dialogue box."
- (w/pub '(monitor obs toggle) (list "Persona 4" "Persona 4 Background")))
+ (w/obs-toggle "Persona 4" "Persona 4 Background"))
(defun w/obs-toggle-explosion ()
"Toggle the explosion effect."
- (w/pub '(monitor obs toggle) (list "Explosion" "Explosion Video")))
+ (w/obs-toggle "Explosion" "Explosion Video"))
(defun w/obs-toggle-total-clarity ()
"Toggle the total clarity effect."
- (w/pub '(monitor obs toggle) (list "Total Clarity" "Total Clarity Video"))
- (w/pub '(monitor obs toggle) (list "Main" "Mic")))
+ (w/obs-toggle "Total Clarity" "Total Clarity Video")
+ (w/obs-toggle "Main" "Mic"))
(defun w/obs-toggle-activate-nixos ()
"Toggle the explosion effect."
@@ -60,39 +64,31 @@
(defun w/obs-set-clickbait-text (msg)
"Change the clickbait text to MSG."
- (w/pub '(monitor obs set-text) (list "Red Arrow Text" (w/encode-string (s-trim msg)))))
+ (w/pub '(monitor obs set-text)
+ (list (w/encode-string "Red Arrow Text") (w/encode-string (s-trim msg)))))
(defun w/obs-toggle-clickbait (&optional msg)
"Toggle the clickbait arrow.
Optionally, change text to MSG."
(when msg
(w/obs-set-clickbait-text msg))
- (w/pub '(monitor obs toggle) (list "Red Arrow" "Red Arrow Group")))
+ (w/obs-toggle "Red Arrow" "Red Arrow Group"))
(defun w/obs-toggle-chase-dreams ()
"Toggle the Chasing Dreams effect."
- (w/pub '(monitor obs toggle) (list "Chasing Dreams" "Dreams")))
+ (w/obs-toggle "Chasing Dreams" "Dreams"))
(defun w/obs-toggle-brazil ()
"Toggle the Brazilian flag."
- (w/pub '(monitor obs toggle) (list "Main" "Brazil")))
+ (w/obs-toggle "Main" "Brazil"))
(defun w/obs-toggle-japan ()
"Toggle the Japanese flag."
- (w/pub '(monitor obs toggle) (list "Main" "Japan")))
-
-(defun w/obs-set-debate-topic-text (msg)
- "Change the debate topic text to MSG."
- (w/pub '(monitor obs set-text) (list "Debate Topic" (w/encode-string (s-trim msg)))))
-
-(defun w/obs-toggle-debate-topic ()
- "Toggle the debate topic."
- (w/pub '(monitor obs toggle) (list "Main" "Debate Topic")))
+ (w/obs-toggle "Main" "Japan"))
(defun w/obs-toggle-spatiotemporal-clarity ()
"Toggle the shader clarity effect."
- (w/model-toggle "shaderclarity")
- (w/pub '(monitor obs toggle) (list "Main" "Mic")))
+ (w/binary-pub "overlay toggle" "shaderclarity"))
(w/defstruct
w/obs-toggle
diff --git a/src/wasp-overlay.el b/src/wasp-overlay.el
index e902eb3d..b62550e7 100644
--- a/src/wasp-overlay.el
+++ b/src/wasp-overlay.el
@@ -3,56 +3,191 @@
;;; Code:
(require 'wasp-utils)
-(require 'wasp-bus)
+(require 'wasp-bus-binary)
+
+(defun w/overlay-toggle (effect)
+ "Toggle EFFECT in the overlay."
+ (w/binary-pub "overlay toggle" effect))
+(defun w/overlay-toggle-set (effect)
+ "Turn on EFFECT in the overlay."
+ (w/binary-pub "overlay toggle set" effect))
+(defun w/overlay-toggle-unset (effect)
+ "Turn off EFFECT in the overlay."
+ (w/binary-pub "overlay toggle unset" effect))
(defun w/overlay-shader (user shader)
"Set the overlay shader to SHADER by USER."
- (w/pub '(avatar overlay shader)
- (list (w/encode-string user) (w/encode-string shader))))
+ (w/binary-pub "overlay shader"
+ (s-concat
+ (w/bus-binary-build-length-prefixed user)
+ (w/bus-binary-build-length-prefixed shader))))
(defun w/overlay-chat (msg)
"Update the overlay about chat MSG."
- (w/pub '(avatar overlay chat)
- (list
- (w/encode-string (w/. user msg))
- (w/encode-string (w/. text msg))
- (format "%s" (w/unix-time))
- (format "%s" (or (w/. biblicality msg) 0.0)))))
+ ;; (w/binary-pub "overlay chat"
+ ;; (s-concat
+ ;; (w/bus-binary-build-length-prefixed (w/. user msg))
+ ;; (w/bus-binary-build-length-prefixed (w/. text msg))
+ ;; (w/bus-binary-build-length-prefixed (format "%s" (w/unix-time)))
+ ;; (w/bus-binary-build-length-prefixed (format "%s" (or (w/. biblicality msg) 0.0)))))
+ )
(defun w/overlay-muzak (user song)
"Update the overlay about Muzak SONG played by USER."
(ignore song)
- (w/pub '(avatar overlay muzak) (list (w/encode-string user))))
+ (w/binary-pub "overlay info credits music" user))
(defun w/overlay-muzak-clear ()
"Tell the overlay that there is no Muzak song playing."
- (w/pub '(avatar overlay muzak clear) (list)))
+ (w/binary-pub "overlay info credits music clear" ""))
(defvar w/overlay-last-cursor nil)
(defun w/overlay-update-cursor ()
"Inform the overlay about the current cursor position."
- (when (and (process-live-p (get-process w/bus-process)))
+ (when (and (process-live-p (get-process w/bus-binary-process)))
(when-let* ((pos (window-absolute-pixel-position)))
(when (not (equal pos w/overlay-last-cursor))
(setf w/overlay-last-cursor pos)
- (w/pub '(avatar overlay cursor) (list (car pos) (cdr pos)))))))
+ (w/binary-pub "overlay info emacs cursor"
+ (s-concat
+ (w/bus-binary-build-length-prefixed (format "%s" (car pos)))
+ (w/bus-binary-build-length-prefixed (format "%s" (cdr pos)))))))))
(add-hook 'post-command-hook #'w/overlay-update-cursor)
(defun w/overlay-emacs ()
"Update the overlay with miscellaneous data from Emacs."
- (w/pub '(avatar overlay emacs)
- (list
- (w/get-heartrate)
- )))
+ ;; (w/binary-pub "overlay info emacs" (w/bus-binary-build-int32le (w/heartrate)))
+ )
(defun w/overlay-automata (user s &optional color)
"Send a cellular automata S from USER in RLE format to the overlay.
Optionally, make the cells be COLOR."
- (w/pub '(avatar automata spawn)
- (list
- (w/encode-string s)
- (w/encode-string user)
- (w/encode-string (or color (w/random-color))))))
+ (w/binary-pub "overlay automata spawn"
+ (s-concat
+ (w/bus-binary-build-length-prefixed s)
+ (w/bus-binary-build-length-prefixed user)
+ (w/bus-binary-build-int32le (string-to-number (s-chop-prefix "#" (or color (w/random-color))) 16)))))
+
+;;;; Clippy
+(defun w/overlay-clippy-animate (anim)
+ "Set the Clippy animation to ANIM."
+ (w/binary-pub "overlay clippy animate"
+ (s-concat
+ (w/bus-binary-build-length-prefixed anim))))
+(defun w/overlay-clippy-border (border)
+ "Set the Clippy border state to BORDER."
+ (w/binary-pub
+ (if border
+ "overlay clippy border on"
+ "overlay clippy border off")
+ ""))
+
+(cl-defstruct (w/overlay-clippy-event (:constructor w/overlay-clippy-event-new))
+ timestamp
+ type)
+(define-derived-mode w/overlay-clippy-mode special-mode "Karl Klammer"
+ "Major mode for displaying Clippy dialogue."
+ :group 'wasp
+ (setq mode-line-format nil))
+(defface w/overlay-clippy-face
+ '((t
+ :foreground "black"
+ :font "Terminus (TTF):antialias=false:hinting=false:pixelsize=24"
+ ))
+ "Face for Clippy dialogue."
+ :group 'wasp)
+(defconst w/overlay-clippy-frame-buffer-name " *clippy-dialogue*")
+(defvar w/overlay-clippy-frame-background-color "#ffffcc")
+(defvar w/overlay-clippy-frame nil)
+(defvar w/overlay-clippy-last-event nil)
+(defvar w/overlay-clippy-speech-timer nil)
+(defun w/overlay-clippy-frame-buffer ()
+ "Return the buffer used to display Clippy dialogue."
+ (let ((name w/overlay-clippy-frame-buffer-name))
+ (unless (get-buffer name)
+ (with-current-buffer (get-buffer-create name)
+ (w/overlay-clippy-mode)))
+ (get-buffer name)))
+(defun w/overlay-clippy-frame-create ()
+ "Build a frame for displaying completion candidates."
+ (when (framep w/overlay-clippy-frame)
+ (delete-frame w/overlay-clippy-frame))
+ (setf
+ w/overlay-clippy-frame
+ (make-frame
+ (append
+ `((name . "clippy")
+ (unsplittable . t)
+ (undecorated . t)
+ (no-accept-focus . t)
+ (no-focus-on-map . t)
+ (override-redirect . t)
+ (user-size . t)
+ (width . 25)
+ (height . 8)
+ (user-position . t)
+ (left . 1600)
+ (top . 550)
+ (default-minibuffer-frame . ,(selected-frame))
+ (minibuffer . nil)
+ (left-fringe . 0)
+ (right-fringe . 0)
+ (cursor-type . nil)
+ (background-color . ,w/overlay-clippy-frame-background-color)))))
+ (make-frame-invisible w/overlay-clippy-frame)
+ (let ((window (frame-selected-window w/overlay-clippy-frame)))
+ (set-window-buffer window (w/overlay-clippy-frame-buffer))))
+(w/overlay-clippy-frame-create)
+(defun w/overlay-clippy-clear ()
+ "Clear the Clippy buffer."
+ (when w/overlay-clippy-frame
+ (modify-frame-parameters w/overlay-clippy-frame
+ `((background-color . ,w/overlay-clippy-frame-background-color))))
+ (with-current-buffer (w/overlay-clippy-frame-buffer)
+ (let ((inhibit-read-only t))
+ (erase-buffer))))
+(defun w/overlay-clippy-write (msg)
+ "Write MSG to the Clippy buffer."
+ (when w/overlay-clippy-frame
+ (modify-frame-parameters w/overlay-clippy-frame
+ `((background-color . ,w/overlay-clippy-frame-background-color))))
+ (let ((window (frame-selected-window w/overlay-clippy-frame)))
+ (set-window-buffer window (w/overlay-clippy-frame-buffer)))
+ (with-current-buffer (w/overlay-clippy-frame-buffer)
+ (let ((inhibit-read-only t))
+ (w/write-line msg 'w/overlay-clippy-face))))
+(defun w/overlay-clippy-say (msg)
+ "Have Clippy say MSG."
+ (w/overlay-clippy-clear)
+ (w/overlay-clippy-write msg)
+ (w/overlay-clippy-border t)
+ (w/show-frame w/overlay-clippy-frame t)
+ (raise-frame w/overlay-clippy-frame)
+ (w/overlay-clippy-animate "EXPLAIN")
+ (setf w/overlay-clippy-speech-timer 10))
+
+(defun w/overlay-clippy-check-events ()
+ "Check for events that Clippy should respond to."
+ )
+
+(defun w/overlay-clippy-update ()
+ "Update Clippy."
+ (when w/overlay-clippy-speech-timer
+ (cl-decf w/overlay-clippy-speech-timer)
+ (when (<= w/overlay-clippy-speech-timer 0)
+ (w/overlay-clippy-border nil)
+ (w/show-frame w/overlay-clippy-frame nil)
+ (setf w/overlay-clippy-speech-timer nil)))
+ (w/overlay-clippy-check-events))
+(defvar w/overlay-clippy-timer nil)
+(defun w/overlay-clippy-timer-run ()
+ "Run the gizmo update timer."
+ (when w/overlay-clippy-timer
+ (cancel-timer w/overlay-clippy-timer))
+ (w/overlay-clippy-update)
+ (setq
+ w/overlay-clippy-timer
+ (run-with-timer 1 nil #'w/overlay-clippy-timer-run)))
(provide 'wasp-overlay)
;;; wasp-overlay.el ends here
diff --git a/src/wasp-setup.el b/src/wasp-setup.el
index 90745d6a..2fea4d77 100644
--- a/src/wasp-setup.el
+++ b/src/wasp-setup.el
@@ -30,10 +30,9 @@
(windmove-down)
)
-(defun w/setup-stream ()
- "Configure windows for streaming."
+(defun w/setup-connections ()
+ "Initialize essential connections."
(interactive)
- ;; initialization
(w/connect)
(w/bus-binary-connect)
(w/db-connect)
@@ -43,7 +42,16 @@
(w/twitch-7tv-update-emotes)
(w/twitch-update-title)
(w/bible-table-populate)
- (w/user-cache-populate)
+ ;; (w/user-cache-populate)
+ )
+
+(defun w/setup-stream ()
+ "Configure windows for streaming."
+ (interactive)
+ ;; initialization
+ (w/setup-connections)
+
+ (w/badge-populate-cache)
;; timers
(w/twitch-run-shoutout-timer)
@@ -55,8 +63,9 @@
(w/start-audio-record)
(w/run-audio-record-end-timer)
(w/chatsummary-start)
- (w/fake-chatters-start)
+ ;; (w/fake-chatters-start)
(w/friend-start)
+ (w/overlay-clippy-timer-run)
;; layout
(eyebrowse-switch-to-window-config 0)
@@ -69,10 +78,10 @@
(windmove-down)
(split-window-horizontally)
(switch-to-buffer w/friend-buffer)
- (w/gizmo-tag-window)
+ ;; (w/gizmo-tag-window)
(windmove-right)
(switch-to-buffer w/heartrate-buffer)
- (w/gizmo-tag-window)
+ ;; (w/gizmo-tag-window)
(windmove-right)
(split-window-vertically -28)
(switch-to-buffer w/chat-buffer)
diff --git a/src/wasp-twitch-chat-commands.el b/src/wasp-twitch-chat-commands.el
index f815bd6c..e4018aeb 100644
--- a/src/wasp-twitch-chat-commands.el
+++ b/src/wasp-twitch-chat-commands.el
@@ -6,6 +6,7 @@
(require 'wasp-twitch)
(require 'wasp-ai)
(require 'wasp-8ball)
+(require 'wasp-tcg)
;; gizmos
(require 'wasp-pronunciation)
@@ -14,207 +15,183 @@
w/twitch-chat-commands
(list
(cons
- "!getpaid"
- (lambda (_ _)
- (w/pub '(monitor lamulana msg))))
- (cons
"!commands"
- (lambda (_ _)
+ (lambda (_ _ _)
(w/twitch-say
(s-truncate 500
(s-concat
"Available commands: "
- (s-join " " (--filter (s-contains? "!" it) (-map #'car w/twitch-chat-commands))))))))
- (cons "!youtube" (lambda (_ _) (w/twitch-say "https://youtube.com/@LCOLONQ")))
- (cons "MRBEAST" (lambda (_ _) (soundboard//play-clip "mrbeast.mp3")))
- (cons "NICECOCK" (lambda (_ _) (soundboard//play-clip "pantsintoashes.mp3")))
- (cons "hexadiCoding" (lambda (_ _) (soundboard//play-clip "developers.ogg")))
- (cons "ProgrammingTime" (lambda (_ _) (soundboard//play-clip "emacslisp.ogg")))
+ (s-join " "
+ (--filter (s-contains? "!" it)
+ (-map #'car w/twitch-chat-commands))))))))
+ (cons "MRBEAST" (lambda (_ _ _) (soundboard//play-clip "mrbeast.mp3")))
+ (cons "NICECOCK" (lambda (_ _ _) (soundboard//play-clip "pantsintoashes.mp3")))
+ (cons "hexadiCoding" (lambda (_ _ _) (soundboard//play-clip "developers.ogg")))
+ (cons "ProgrammingTime" (lambda (_ _ _) (soundboard//play-clip "emacslisp.ogg")))
(cons
"roguelike"
- (lambda (user _)
+ (lambda (uid user _)
(w/twitch-say
(if (= 0 (random 20))
(format "@%s that is a roguelike :3" user)
(format "@%s that's not a roguelike" user)))))
- (cons "arch btw" (lambda (_ _) (w/twitch-say "I use nix btw")))
- (cons "!life" (lambda (_ _) (w/twitch-say "https://conwaylife.com/patterns")))
- (cons "!advent" (lambda (_ _) (w/twitch-say "Join our private leaderboard: 3307583-b61f237c")))
- (cons "!discord" (lambda (_ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
- ;; (cons "discord IRC" (lambda (_ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
- ;; (cons "discord irc" (lambda (_ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
- ;; (cons "Discord IRC" (lambda (_ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
- ;; (cons "Discord irc" (lambda (_ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
- (cons "!irc" (lambda (_ _) (w/twitch-say "#cyberspace on IRC at colonq.computer:26697 (over TLS)")))
- ;; (cons "IRC" (lambda (_ _) (w/twitch-say "#cyberspace on IRC at colonq.computer:26697 (over TLS)")))
- (cons "Joel" (lambda (_ _) (cl-incf w/chat-joel-count) (w/chat-update-header-line)))
- (cons "+2" (lambda (_ _) (cl-incf w/chat-plus2-count) (w/chat-update-header-line)))
- (cons "-2" (lambda (_ _) (cl-incf w/chat-minus2-count) (w/chat-update-header-line)))
- (cons "ICANT" (lambda (_ _) (cl-incf w/chat-icant-count) (w/chat-update-header-line)))
- (cons "bpm" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "BPM" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "heartrate" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "Heartrate" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "heart" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "Heart" (lambda (_ _) (cl-incf w/chat-bpm-count)))
- (cons "!menu" (lambda (_ _) (w/twitch-say "https://secure.colonq.computer/menu")))
- (cons "!guy" (lambda (_ _) (w/twitch-say "[πŸŒ™] it worked")))
- (cons "!littleguy" (lambda (_ _) (w/twitch-say "[πŸŒ™] it worked")))
+ (cons "arch btw" (lambda (uid _ _) (w/twitch-say "I use nix btw")))
+ (cons "Joel" (lambda (uid _ _) (cl-incf w/chat-joel-count) (w/chat-update-header-line)))
+ (cons "+2" (lambda (uid _ _) (cl-incf w/chat-plus2-count) (w/chat-update-header-line)))
+ (cons "-2" (lambda (uid _ _) (cl-incf w/chat-minus2-count) (w/chat-update-header-line)))
+ (cons "ICANT" (lambda (uid _ _) (cl-incf w/chat-icant-count) (w/chat-update-header-line)))
+ (cons "bpm" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "BPM" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "heartrate" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "Heartrate" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "heart" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "Heart" (lambda (uid _ _) (cl-incf w/chat-bpm-count)))
+ (cons "!openpack"
+ (lambda (uid user _)
+ (w/tcg-random-user-card
+ (lambda (c)
+ (w/tcg-generate-card (s-downcase user) c)))))
+ (cons "!charsheet"
+ (lambda (uid user msg)
+ (let* ( (args (s-split " " msg))
+ (auser (cadr args)))
+ (if (s-present? auser)
+ (w/user-id-from-name auser
+ (lambda (uid)
+ (w/twitch-say (format "@%s: charsheet for %s: https://api.colonq.computer/charsheet#%s" user auser uid))))
+ (w/twitch-say (format "@%s: https://api.colonq.computer/charsheet#%s" user uid))))))
+ (cons "!binder"
+ (lambda (uid user msg)
+ (let* ( (args (s-split " " msg))
+ (auser (cadr args)))
+ (if (s-present? auser)
+ (w/user-id-from-name auser
+ (lambda (uid)
+ (w/twitch-say (format "@%s: see %s's cards at: https://api.colonq.computer/api/tcg/binder/%s" user auser uid))))
+ (w/twitch-say (format "@%s: see your cards at: https://api.colonq.computer/api/tcg/binder/%s" user uid))))))
+ (cons "!refresh"
+ (lambda (uid user _)
+ (let ((p (w/twitch-user-avatar-path user)))
+ (when (f-exists? p)
+ (f-delete p))
+ (w/twitch-get-user-avatar user
+ (lambda ()
+ (w/chat-write-event (format "Refreshed avatar for %s!" user)))))))
+ (cons "!youtube" (lambda (_ _ _) (w/twitch-say "https://youtube.com/@LCOLONQ")))
+ (cons "!meathouse" (lambda (_ _ _) (w/twitch-say "https://youtube.com/@LCOLONQMEATHOUSE")))
+ (cons "!life" (lambda (_ _ _) (w/twitch-say "https://conwaylife.com/patterns")))
+ (cons "!advent" (lambda (_ _ _) (w/twitch-say "Join our private leaderboard: 3307583-b61f237c")))
+ (cons "!discord" (lambda (_ _ _) (w/twitch-say "https://discord.gg/f4JTbgN7St")))
+ (cons "!irc" (lambda (_ _ _) (w/twitch-say "#cyberspace on IRC at colonq.computer:26697 (over TLS)")))
+ (cons "!menu" (lambda (_ _ _) (w/twitch-say "https://secure.colonq.computer/menu")))
+ (cons "!guy" (lambda (_ _ _) (w/twitch-say "[πŸŒ™] it worked")))
+ (cons "!littleguy" (lambda (_ _ _) (w/twitch-say "[πŸŒ™] it worked")))
- (cons "!today" (lambda (_ _) (w/twitch-say (s-trim (w/slurp "~/today.txt")))))
- (cons "!schedule" (lambda (_ _) (w/twitch-say "https://twitch.tv/LCOLONQ/schedule")))
- (cons "!bingo" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer/~prod/toy/bingo/")))
- (cons "!music" (lambda (_ _) (w/twitch-say "we do not play music because it can be distracting; however you can listen to the official OST at https://vgmusic.fm :3")))
- (cons "!mrgreen" (lambda (_ _) (w/twitch-say "lcolonGreen https://p.yusukekamiyamane.com/ lcolonGreen")))
+ (cons "!today" (lambda (_ _ _) (w/twitch-say (s-trim (w/slurp "~/today.txt")))))
+ (cons "!title" (lambda (_ _ _) (w/twitch-say w/twitch-current-stream-title)))
+ (cons "!schedule" (lambda (_ _ _) (w/twitch-say "https://twitch.tv/LCOLONQ/schedule")))
+ (cons "!bingo" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer/~prod/toy/bingo/")))
+ (cons "!theme" (lambda (_ _ _) (w/twitch-say "https://github.com/protesilaos/ef-themes (you may want: https://github.com/oonamo/ef-themes.nvim )")))
+ (cons "!music" (lambda (_ _ _) (w/twitch-say "we do not play music because it can be distracting; however you can listen to the official OST at https://vgmusic.fm :3")))
+ (cons "!mrgreen" (lambda (_ _ _) (w/twitch-say "lcolonGreen https://p.yusukekamiyamane.com/ lcolonGreen")))
(cons
"!music"
- (lambda (_ _) (w/twitch-say "alternatively https://www.youtube.com/playlist?list=PLQ_Vw7ACol3CN58_osDkbeKa14Hk-N-TZ")))
+ (lambda (_ _ _) (w/twitch-say "alternatively https://www.youtube.com/playlist?list=PLQ_Vw7ACol3CN58_osDkbeKa14Hk-N-TZ")))
(cons
"!fish"
- (lambda (_ _)
+ (lambda (_ _ _)
(w/twitch-say (shell-command-to-string "fishing"))))
- (cons "!nc" (lambda (_ _) (w/twitch-say "try: \"nc colonq.computer 31340\", if nc doesn't work try ncat or telnet")))
+ (cons "!nc" (lambda (_ _ _) (w/twitch-say "try: \"nc colonq.computer 31340\", if nc doesn't work try ncat or telnet")))
(cons
"!oomfie"
- (lambda (_ _)
+ (lambda (_ _ _)
(soundboard//play-clip "oomfie.ogg")
(w/twitch-say "hi!!!!!!!")))
- (cons "!helloiloveyou" (lambda (_ _) (w/twitch-say "hello i love you")))
- (cons "!pronunciation" (lambda (_ _) (w/twitch-say (w/pronuciation))))
- ;; (cons "!jetsWave" (lambda (_ _) (fig//twitch-say (fig/slurp "jetsWave.txt"))))
- ;; (cons "!forth" (lambda (_ _) (fig//twitch-say "https://github.com/lcolonq/giving")))
- (cons "!news" (lambda (_ _) (w/twitch-say "https://news.colonq.computer")))
- (cons "!oub" (lambda (_ _) (w/twitch-say "https://oub.colonq.computer")))
- (cons "!cellar" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer/~llll/cellar/index.html")))
- (cons "!game" (lambda (_ _) (w/twitch-say "https://oub.colonq.computer")))
- (cons "!voidstranger" (lambda (_ _) (w/twitch-say "https://store.steampowered.com/app/2121980/Void_Stranger/")))
- (cons "!pubnix" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer")))
- (cons "!ring" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer")))
- (cons "!webring" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer")))
- (cons "!animeguide" (lambda (_ _) (w/twitch-say "https://nixos-and-flakes.thiscute.world/introduction")))
- (cons "!greencircle" (lambda (_ _) (w/twitch-say "https://greencircle.live")))
- (cons "!circle" (lambda (_ _) (w/twitch-say "https://greencircle.live")))
- (cons "!doujincircle" (lambda (_ _) (w/twitch-say "https://greencircle.live")))
- (cons "!tsuki" (lambda (_ _) (w/twitch-say "https://forum.tsuki.games")))
- (cons "!sponsor" (lambda (_ _) (w/twitch-say "Like what you see? Don't forget to download GNU Emacs at https://www.gnu.org/software/emacs/?code=LCOLONQ")))
- (cons "!specs" (lambda (_ _) (w/twitch-say "Editor: evil-mode, WM: EXWM, OS: NixOS, hardware: steam deck")))
- (cons "!coverage" (lambda (_ _) (w/twitch-say (format "Test coverage: %s%%" (random 100)))))
- (cons "!learnprogramming" (lambda (_ _) (w/twitch-say "1) program")))
- (cons "!github" (lambda (_ _) (w/twitch-say "https://github.com/lcolonq")))
- (cons "!language" (lambda (_ _) (w/twitch-say "probably emacs lisp or maybe rust")))
- (cons "!onlyfans" (lambda (_ _) (soundboard//play-clip "pornhub.mp3")))
- (cons "!throne" (lambda (_ _) (w/twitch-say "xdding")))
- (cons "!vim" (lambda (_ _) (w/twitch-say "vi is the best text editor, emacs is the best operating system")))
- (cons "!emacs" (lambda (_ _) (w/twitch-say "i've tried everything else emacs is best girl")))
- (cons "!bells" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer/~the0x539/bells.html and https://pub.colonq.computer/~bezelea/bells/ and https://pub.colonq.computer/~prod/toy/dbkai/")))
- (cons "!shader" (lambda (_ _) (w/twitch-say "https://secure.colonq.computer/throwshade")))
- (cons "!help" (lambda (_ _) (w/twitch-say "https://pub.colonq.computer/~prod/toy/glossary/")))
+ (cons "!helloiloveyou" (lambda (_ _ _) (w/twitch-say "hello i love you")))
+ (cons "!spelling" (lambda (_ _ _) (w/twitch-say "pronunciation")))
+ (cons "!pronuciation" (lambda (_ _ _) (w/twitch-say "spelling")))
+ (cons "!pronunciation" (lambda (_ _ _) (w/twitch-say (w/pronunciation))))
+ (cons "!news" (lambda (_ _ _) (w/twitch-say "https://news.colonq.computer")))
+ (cons "!oub" (lambda (_ _ _) (w/twitch-say "https://oub.colonq.computer")))
+ (cons "!game" (lambda (_ _ _) (w/twitch-say "https://oub.colonq.computer")))
+ (cons "!cellar" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer/~llll/cellar/index.html")))
+ (cons "!voidstranger" (lambda (_ _ _) (w/twitch-say "https://store.steampowered.com/app/2121980/Void_Stranger/")))
+ (cons "!pubnix" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer")))
+ (cons "!ring" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer")))
+ (cons "!webring" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer")))
+ (cons "!animeguide" (lambda (_ _ _) (w/twitch-say "https://nixos-and-flakes.thiscute.world/introduction")))
+ (cons "!greencircle" (lambda (_ _ _) (w/twitch-say "https://greencircle.live")))
+ (cons "!circle" (lambda (_ _ _) (w/twitch-say "https://greencircle.live")))
+ (cons "!doujincircle" (lambda (_ _ _) (w/twitch-say "https://greencircle.live")))
+ (cons "!tsuki" (lambda (_ _ _) (w/twitch-say "https://forum.tsuki.games")))
+ (cons "!sponsor" (lambda (_ _ _) (w/twitch-say "Like what you see? Don't forget to download GNU Emacs at https://www.gnu.org/software/emacs/?code=LCOLONQ")))
+ (cons "!specs" (lambda (_ _ _) (w/twitch-say "Editor: evil-mode, WM: EXWM, OS: NixOS, hardware: steam deck")))
+ (cons "!coverage" (lambda (_ _ _) (w/twitch-say (format "Test coverage: %s%%" (random 100)))))
+ (cons "!learnprogramming" (lambda (_ _ _) (w/twitch-say "1) program")))
+ (cons "!github" (lambda (_ _ _) (w/twitch-say "https://github.com/lcolonq")))
+ (cons "!language" (lambda (_ _ _) (w/twitch-say "probably emacs lisp or maybe rust")))
+ (cons "!onlyfans" (lambda (_ _ _) (soundboard//play-clip "pornhub.mp3")))
+ (cons "!throne" (lambda (_ _ _) (w/twitch-say "xdding")))
+ (cons "!vim" (lambda (_ _ _) (w/twitch-say "vi is the best text editor, emacs is the best operating system")))
+ (cons "!emacs" (lambda (_ _ _) (w/twitch-say "i've tried everything else emacs is best girl")))
+ (cons "!bells" (lambda (_ _ _) (w/twitch-say "https://pub.colonq.computer/~the0x539/bells/ and https://pub.colonq.computer/~bezelea/bells/ and https://pub.colonq.computer/~prod/toy/dbkai/")))
+ (cons "!shader" (lambda (_ _ _) (w/twitch-say "https://secure.colonq.computer/throwshade")))
+ (cons "!help" (lambda (_ _ _) (w/twitch-say "https://github.com/tyumici/Green-Pages and also https://pub.colonq.computer/~prod/toy/glossary/")))
(cons
"!boost"
- (lambda (user _)
- (w/twitch-say (format "boost power for @%s: %s" user (alist-get :boost w/user-current)))))
+ (lambda (uid user _)
+ (w/user-get-property uid "boost"
+ (lambda (cur)
+ (w/twitch-say (format "boost power for @%s: %s" user cur))))))
(cons
"!tsoob"
- (lambda (user _)
- (w/twitch-say (format "boost power for @%s: %s" user (alist-get :boost w/user-current)))))
+ (lambda (uid user _)
+ (w/user-get-property uid "boost"
+ (lambda (cur)
+ (w/twitch-say (reverse (format "boost power for @%s: %s" user cur)))))))
(cons
"!faction"
- (lambda (user _)
- (w/twitch-say (format "faction for %s: %s" user (alist-get :faction w/user-current)))))
- (cons "!thanks" (lambda (user _) (w/twitch-say (format "thank you %s!" user))))
- (cons "!bible" (lambda (_ _) (w/twitch-say "https://www.youtube.com/watch?v=G5u23bh29hI")))
- (cons "!drink" (lambda (_ _) (w/twitch-say "its watah im drinkin it")))
+ (lambda (uid user _)
+ (w/user-get-property uid "faction"
+ (lambda (cur)
+ (w/twitch-say (format "faction for %s: %s" user cur))))))
+ (cons "!thanks" (lambda (_ user _) (w/twitch-say (format "thank you %s!" user))))
+ (cons "!biblicality" (lambda (_ _ _) (w/twitch-say "https://www.youtube.com/watch?v=G5u23bh29hI")))
+ (cons "!bible" (lambda (_ _ _) (w/twitch-say "https://www.youtube.com/watch?v=G5u23bh29hI")))
+ (cons "!drink" (lambda (_ _ _) (w/twitch-say "its watah im drinkin it")))
(cons
"!lore"
- (lambda (_ _)
+ (lambda (_ _ _)
(w/ai
"ITEM"
(lambda (msg) (w/twitch-say msg))
"Please produce a Dark Souls style item name and description related to LCOLONQ. Please limit your response to one sentence maximum. The sentence should be vague and incorporate archaic words that are not commonly used. LCOLONQ is a spirit that lives inside the computer. LCOLONQ is associated with: the moon, snakes, the color grey, dolls and puppets, amber, the wind, and GNU Emacs. The description should mostly describe the item, but with vague insinuations about the true nature of LCOLONQ."
"ITEM"
"Ring of Favor and Protection - A ring symbolizing the favor and protection of the goddess Fina, known in legend to possess fateful beauty.")))
- ;; (cons "!geisercounter" (lambda (_ _) (fig//twitch-say (format "The Geiser counter beeps %s times" (fig//geiser-counter)))))
(cons
"!8ball"
- (lambda (user inp)
+ (lambda (_ user inp)
(let ((trimmed (s-trim (s-replace "!8ball" "" inp))))
(w/8ball
trimmed
(lambda (answer)
(w/twitch-say (format "@%s 8ball says: %s" user answer)))))))
(cons
- "!bookrec"
- (lambda (_ _)
- (w/user-get
- "__books__"
- (lambda (books)
- (let ((choice (w/pick-random books)))
- (w/twitch-say (format "%s (recommended by %s)" (car choice) (cdr choice))))))))
- (cons
- "!quote"
- (lambda (_ _)
- (w/user-get
- "__quotes__"
- (lambda (books)
- (let ((choice (w/pick-random books)))
- (w/twitch-say (format "%s: %s" (cdr choice) (car choice))))))))
- (cons
"!leaderboard"
- (lambda (_ _)
- (let* ((user-scores (-filter #'cdr (--map (when (and (listp it) (listp (cdr it))) (cons (car it) (alist-get :boost (cdr it)))) (ht->alist w/user-cache))))
- (sorted (-sort (-on #'> #'cdr) user-scores))
- (leaders (-take 5 sorted)))
- (w/twitch-say (s-join ", " (--map (format "%s: %s" (car it) (cdr it)) leaders))))))
+ (lambda (_ _ _)
+ (w/twitch-say "the leaderboard is temporarily unavailable. rest assured, your BOOSTPOWER is still being tracked. measure is unceasing.")))
(cons
"draobredael!"
- (lambda (_ _)
- (let* ((user-scores (-filter #'cdr (--map (when (and (listp it) (listp (cdr it))) (cons (car it) (alist-get :boost (cdr it)))) (ht->alist w/user-cache))))
- (sorted (-sort (-on #'< #'cdr) user-scores))
- (leaders (-take 5 sorted)))
- (w/twitch-say (s-join ", " (--map (format "%s: %s" (reverse (car it)) (cdr it)) leaders))))))
- (cons
- "!resolution"
- (lambda (user inp)
- (let ((trimmed (s-trim (s-replace "!resolution" "" inp))))
- (if (string-empty-p trimmed)
- (w/chat-write-event "You gotta put what your resolution is.")
- (w/chat-write-event (format "%s RESOLVES: %s" (s-upcase user) trimmed))
- (setf (alist-get :resolution2025 w/user-current) trimmed)))))
- ;; (cons "!addbookrec"
- ;; (lambda (user inp)
- ;; (let ((trimmed (s-trim (s-replace "!addbookrec" "" inp))))
- ;; (fig//chat-write-event (format "%s recommends: %s" user trimmed))
- ;; (fig//add-recommended-book user trimmed))))
- ;; (cons "!quote"
- ;; (lambda (_ _)
- ;; (let ((choice (nth (random (length fig/quotes)) fig/quotes)))
- ;; (fig//twitch-say (format "%s: %s" (cdr choice) (car choice))))))
- ;; (cons "!addquote"
- ;; (lambda (user inp)
- ;; (let ((trimmed (s-trim (s-replace "!addquote" "" inp))))
- ;; (fig//chat-write-event (format "%s saves quote: %s" user trimmed))
- ;; (fig//add-quote user trimmed))))
- ;; (cons "!twitter"
- ;; (lambda (_ _)
- ;; (fig/ask "How do you feel about Twitter? Should viewers follow LCOLONQ on Twitter?" #'fig/say)
- ;; (fig//twitch-say "https://twitter.com/LCOLONQ")))
- ;; ;; (cons "!aoc" (lambda (_ _) (fig//twitch-say "Join our leaderboard: 3307583-b61f237c")))
- ;; (cons "!roll" (lambda (user _) (fig//twitch-say (fig//character-to-string (fig//roll-character user)))))
- ;; (cons
+ (lambda (_ _ _)
+ (w/twitch-say (reverse "the leaderboard is temporarily unavailable. rest assured, your BOOSTPOWER is still being tracked. measure is unceasing."))))
;; (cons
- ;; "!vippers"
- ;; (lambda (_ _)
- ;; (let ((vipperstring (s-join ", " (fig//shuffle-seq fig//twitch-vip-list))))
- ;; (fig//twitch-say (seq-take vipperstring 450)))
- ;; (fig//twitch-get-vip-list)))
- ;; (cons "!levelup"
- ;; (lambda (user _)
- ;; (fig//update-db-character
- ;; user
- ;; (lambda (c)
- ;; (cl-incf (fig//rpg-character-level c))
- ;; c))
- ;; (fig//twitch-say (fig//character-to-string (fig//get-db-character user)))))
+ ;; "!resolution"
+ ;; (lambda (_ user inp)
+ ;; (let ((trimmed (s-trim (s-replace "!resolution" "" inp))))
+ ;; (if (string-empty-p trimmed)
+ ;; (w/chat-write-event "You gotta put what your resolution is.")
+ ;; (w/chat-write-event (format "%s RESOLVES: %s" (s-upcase user) trimmed))
+ ;; (setf (alist-get :resolution2025 w/user-current) trimmed)))))
))
(provide 'wasp-twitch-chat-commands)
diff --git a/src/wasp-twitch-redeems.el b/src/wasp-twitch-redeems.el
index 9cd2c15d..4a91e4e2 100644
--- a/src/wasp-twitch-redeems.el
+++ b/src/wasp-twitch-redeems.el
@@ -26,76 +26,77 @@
w/twitch-redeems
(list
(list
- "submit quote" 1
- (lambda (user quote)
- (ignore user)
- (w/irish-contribute quote)))
- (list
"throw shade" 1
- (lambda (user shader)
+ (lambda (uid user shader)
(w/chat-write-event (format "%s threw shade" user))
(w/db-set "shader" shader)
+ (w/overlay-clippy-animate "GETARTSY")
(w/model-record-change)
(w/overlay-shader user shader)))
(list
"spawn" 1
- (lambda (user pattern)
+ (lambda (uid user pattern)
(w/chat-write-event (format "%s created life" user))
(w/model-record-change)
- (w/overlay-automata user pattern (alist-get :color w/user-current))))
+ (w/user-get-property uid "color"
+ (lambda (col)
+ (w/overlay-automata user pattern (or col "#ffffff"))))))
;; (list
;; "sound board" 1
- ;; (lambda (user cmd)
+ ;; (lambda (uid user cmd)
;; (w/chat-write-event (format "%s played sound: %s" user cmd))
;; (w/sfx cmd)))
(list
"lurker check in" 1
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s is lurking" user))))
(list
"mental clarity" 1
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s established mental clarity" user))
(w/stop-all-audio)
(w/model-reset)))
(list
"BOOST" 1
- (lambda (user _)
+ (lambda (uid user _)
(unless (-contains? w/twitch-boosters user)
(add-to-list 'w/twitch-boosters user)
(soundboard//play-clip "yougotboostpower.ogg")
+ (w/overlay-clippy-animate "WAVE")
(w/chat-write-event (s-concat user " boosted their boost number"))
- (cl-incf (alist-get :boost w/user-current 0)))))
+ (w/user-boost uid)
+ )))
(list
"TSOOB" 1
- (lambda (user _)
+ (lambda (uid user _)
(unless (-contains? w/twitch-tsoobers user)
(add-to-list 'w/twitch-tsoobers user)
(soundboard//play-clip "rewoptsoobtoguoy.ogg" 140)
(w/chat-write-event (s-reverse (s-concat user " boosted their boost number")))
- (cl-decf (alist-get :boost w/user-current 0)))))
+ (w/user-tsoob uid)
+ )))
(list
"submit headline" 1
- (lambda (user inp)
+ (lambda (uid user inp)
(w/chat-write-event (format "%s submitted a headline: %s" user inp))
(w/glossary-record inp)
(w/friend-journalism user inp)))
(list
"cycle gizmos" 1
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s cycled the gizmos" user))
(w/gizmo-cycle)))
(list
"allow streamer to drink" 1
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s allowed the streamer to \"drink\"" user))))
(list
"deslug" 1
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s inverted slug" user))))
(list
"talk to clone" 2
- (lambda (user inp)
+ (lambda (uid user inp)
(push (cons user inp) w/twitch-chat-history)
(let ((w/twitch-chat-history (list (cons user inp)))
(w/last-stream-transcription "")
@@ -109,12 +110,12 @@
w/fake-chatters))))))
(list
"spinne" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (s-concat user " activates the spinne cyclle"))
(w/model-toggle "spin")))
(list
"forsen" 3
- (lambda (user _)
+ (lambda (uid user _)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user " loudly exclaims forsenE"))
@@ -123,39 +124,39 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"SEASICKNESS GENERATOR" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (s-concat user " is a salty sea dog"))
(w/model-toggle "zoom_wave")))
(list
"The Pharaoh's Curse" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s drew the ire of the Pharaoh upon me" user))
(w/obs-activate-toggle 'pharaohs-curse)
))
(list
"pursue idol dream" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "Helping %s pursue their idol dream~" user))
(w/obs-activate-toggle 'chase-dreams)
(w/model-region-user-avatar "hair" user)))
(list
"INTJ stare" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s suggested a little more sodium chloride next time" user))
(w/obs-activate-toggle 'intj-stare)))
(list
"Live LCOLONQ Reaction" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s demanded extremely \"hype\" reactions, &c." user))
(w/obs-activate-toggle 'live-reaction)))
(list
"Live friend Reaction" 3
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s demanded extremely \"hype\" reactions, &c. but from \"friend\"!?" user))
(w/obs-activate-toggle 'live-friend-reaction)))
(list
"bells of bezelea" 4
- (lambda (user msg)
+ (lambda (uid user msg)
(w/get-song
msg
(lambda (song)
@@ -163,51 +164,23 @@
(progn
(w/chat-write-event (format "%s played a song: %s (sponsored by Bezelea)" user msg))
(w/audio-muzak-enqueue user song))
+ (w/overlay-clippy-animate "GETARTSY")
(w/chat-write-event (format "%s played the bells (sponsored by Bezelea)" user))
(w/audio-muzak-enqueue user msg))))))
- (list
- "activate spell card" 4
- (lambda (user msg)
- (w/chat-write-event
- (format
- "%s Sign: \"%s\""
- (s-titleize user)
- (s-titleized-words (alist-get user w/twitch-chat-history (w/pick-random w/overlay-spellcard-names) nil #'cl-equalp))))
- (w/overlay-decode-shorthand-bml
- msg
- (lambda (data)
- (w/overlay-start-barrage data)))))
(list "palette swap (hair)" 5 (w/model-handle-redeem-region-swap "hair"))
(list "palette swap (highlight)" 5 (w/model-handle-redeem-region-swap "highlight"))
(list "palette swap (eyes)" 5 (w/model-handle-redeem-region-swap "eyes"))
(list "palette swap (pumpkin)" 5 (w/model-handle-redeem-region-swap "hat"))
(list "palette swap (hands)" 5 (w/model-handle-redeem-region-swap "hands"))
- (list "background swap (drawing)" 5
- (lambda (user inp)
- (if (w/user-authorized)
- (progn
- (w/chat-write-event (s-concat user " changes the drawing background: " inp))
- (if (w/allowed-video-url inp)
- (w/binary-pub "background url" inp)
- (w/chat-write-event (format "%s is not a recognized video site" inp))))
- (w/chat-write-event (format "%s is not authorized to change video" user)))))
- (list
- "run program" 6
- (lambda (user inp)
- (if (w/user-authorized)
- (progn
- (w/chat-write-event (s-concat user " runs program: " inp))
- (w/bless inp 50))
- (w/chat-write-event (format "%s is not authorized to run code" user)))))
(list
"encoded clarity" 7
- (lambda (user msg)
+ (lambda (uid user msg)
(w/chat-write-event (format "%s demands greater program clarity: %s" user msg))
(with-current-buffer (window-buffer)
(w/flymake-error user msg))))
(list
"feed friend" 10
- (lambda (user inp)
+ (lambda (uid user inp)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user " feeds \"friend\" " inp))
@@ -215,7 +188,7 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"talk to friend" 10
- (lambda (user inp)
+ (lambda (uid user inp)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user " talks to \"friend\": " inp))
@@ -223,7 +196,7 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"friend composes song" 10
- (lambda (user inp)
+ (lambda (uid user inp)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user " asks \"friend\" to compose a song about: " inp))
@@ -231,7 +204,7 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"show friend wikipedia page" 10
- (lambda (user inp)
+ (lambda (uid user inp)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user " shows \"friend\" a Wikipedia page: " inp))
@@ -239,37 +212,37 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"theme: maris-dark" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: maris-dark" user))
(w/change-theme 'ef-maris-dark)))
(list
"theme: autumn" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: autumn" user))
(w/change-theme 'ef-autumn)))
(list
"theme: tritanopia-dark" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: tritanopia-dark" user))
(w/change-theme 'ef-tritanopia-dark)))
(list
"theme: duo-dark" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: duo-dark" user))
(w/change-theme 'ef-duo-dark)))
(list
"theme: bio" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: bio" user))
(w/change-theme 'ef-bio)))
(list
"theme: rosa" 50
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s changed the theme: rosa" user))
(w/change-theme 'ef-rosa)))
(list
"gamer" 500
- (lambda (user _)
+ (lambda (uid user _)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event (s-concat user "'s Gamer Sacrifice summoned an entity"))
@@ -278,30 +251,30 @@
(setq w/twitch-redeem-sound-last cur)))))
(list
"arrow" 500
- (lambda (user msg)
+ (lambda (uid user msg)
(w/chat-write-event (format "%s points and says %S" user msg))
(w/obs-activate-toggle 'clickbait msg)))
(list
"antipiracy" 25
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (format "%s does not condone any form of copyright infringement whatsoever." user))
(w/obs-activate-toggle 'activate-nixos)))
(list
"super idol" 500
- (lambda (_ _)
+ (lambda (_ _ _)
(let ((cur (float-time)))
(when (> (- cur w/twitch-redeem-sound-last) 2)
(w/chat-write-event "SuperIdoldexiaorongdoumeinidetianbayuezhengwudeyangguangdoumeiniyaoyanreai105Cdenididiqingchundezhen")
- (soundboard//play-clip "superidololdshortstyle.ogg" 0.5)
+ (soundboard//play-clip "superidololdshortstyle.ogg")
(setq w/twitch-redeem-sound-last cur)))))
(list
"enable ad block" 500
- (lambda (user _)
- (w/chat-write-event (format "%s turned on UltraBlock HYPER: Community Edition" user))
+ (lambda (uid user _)
+ (w/chat-write-event (format "%s turned on UltraBlock HYPER: Community Edition" user))
(w/banner-ad-block)))
(list
"hex" 500
- (lambda (user inp)
+ (lambda (uid user inp)
(let* ((sp (s-split " " inp))
(spell (car sp))
(target (cadr sp)))
@@ -313,19 +286,19 @@
(w/chat-write-event (s-concat user "'s hex fizzled out with a puff of smoke!"))))))
(list
"VIPPER" 35
- (lambda (user inp)
+ (lambda (uid user inp)
(soundboard//play-clip "aeiou.ogg")
(w/chat-write-event (s-concat user " gave VIP to " inp))
(w/twitch-add-vip (s-chop-prefix "@" inp))))
(list
"deVIPPER" 35
- (lambda (user inp)
+ (lambda (uid user inp)
(soundboard//play-clip "uoiea.ogg")
(w/chat-write-event (s-concat user " removed VIP from " inp))
(w/twitch-remove-vip (s-chop-prefix "@" inp))))
(list
"spatiotemporal clarity" 2000
- (lambda (user _)
+ (lambda (uid user _)
(w/stop-all-audio)
(run-with-timer
1 nil
@@ -334,7 +307,7 @@
(w/obs-activate-toggle 'spatiotemporal-clarity)))
(list
"total clarity" 5000
- (lambda (user _)
+ (lambda (uid user _)
(w/stop-all-audio)
(run-with-timer
1 nil
@@ -343,13 +316,14 @@
(w/obs-activate-toggle 'total-clarity)))
(list
"canonize me" 20000
- (lambda (user _)
+ (lambda (uid user _)
(w/chat-write-event (s-concat user " was canonized!"))
(w/bible-canonize user)))
(list
- "cloning facility" 50000
- (lambda (user _)
- (w/chat-write-event (format "%s entered the cloning facility" user))))
+ "equity Lord #FINANCE" 100000
+ (lambda (uid user _)
+ (w/chat-write-event (s-concat user " purchased a voting share of equity!"))
+ (w/user-crown uid)))
))
(provide 'wasp-twitch-redeems)
diff --git a/src/wasp-twitch.el b/src/wasp-twitch.el
index d182dddf..6559a45f 100644
--- a/src/wasp-twitch.el
+++ b/src/wasp-twitch.el
@@ -11,9 +11,6 @@
(require 'wasp-bus-binary)
(require 'wasp-chat)
(require 'wasp-user)
-(require 'wasp-hexamedia)
-(require 'wasp-copfish)
-(require 'wasp-shindaggers)
(require 'wasp-hex)
(require 'wasp-user-stats)
@@ -161,7 +158,7 @@
(defun w/twitch-user-avatar-path (user)
"Get the path to USER's avatar."
- (s-concat w/twitch-avatar-cache-dir user ".png"))
+ (s-concat w/twitch-avatar-cache-dir (s-downcase user) ".png"))
(defun w/twitch-update-title ()
"Get our stream title and update `w/twitch-current-stream-title'."
@@ -218,7 +215,12 @@ K is called when the download is finished."
(w/twitch-api-get
(s-concat "/users?login=" user)
(lambda (data)
- (let ((url (ht-get (aref (ht-get data "data") 0) "profile_image_url")))
+ (when-let*
+ ( (url
+ (-some-> data
+ (ht-get "data")
+ (w/aref 0)
+ (ht-get "profile_image_url"))))
;; (w/write-log (format "downloading avatar: %s %s" url path))
(make-process
:name "wasp-download-avatar"
@@ -266,7 +268,7 @@ K is called when the download is finished."
(w/pick-random
(-difference
w/twitch-vip-list
- '("a_tension_span" "fighting_annelids")))))
+ '("a_tension_span" "fighting_annelids" "thereal6toes")))))
(w/chat-write-event (format "Randomly removed VIP from %s - autofloor" user))
(w/twitch-remove-vip user)))
@@ -447,24 +449,31 @@ CALLBACK will be passed the winner when the poll concludes."
'display
img
'rear-nonsticky t))))
-
+
(defun w/twitch-process-emote-range (er msg)
"Given a string ER of form emoteid:start-end, add the emote MSG."
(if (string-empty-p er)
- msg
- (when-let* ((er-split (s-split ":" er))
- (emoteid (car er-split))
- (range-split (s-split "-" (cadr er-split)))
- (start (string-to-number (car range-split)))
- (end (string-to-number (cadr range-split)))
- (emotemsg (substring msg start (+ end 1)))
- (path (w/twitch-emote-path emoteid)))
- (w/twitch-cache-emote emotemsg emoteid)
+ msg
+ (when-let*
+ ( (er-split (s-split ":" er))
+ (emoteid (car er-split))
+ (path (w/twitch-emote-path emoteid))
+ (sranges (cadr er-split))
+ (ranges (s-split "," sranges)))
(w/twitch-download-emote emoteid)
- (let ((img (create-image path)))
- (w/twitch-add-image-over img msg (+ start 1) (+ end 2))
- ))))
-
+ (let ((cached nil))
+ (--each ranges
+ (when-let*
+ ( (range-split (s-split "-" it))
+ (start (string-to-number (car range-split)))
+ (end (string-to-number (cadr range-split)))
+ (emotemsg (substring msg start (+ end 1))))
+ (unless cached
+ (w/twitch-cache-emote emotemsg emoteid)
+ (setf cached t))
+ (let ((img (create-image path)))
+ (setf msg (w/twitch-add-image-over img msg (+ start 1) (+ end 2)))))))
+ msg)))
(defun w/twitch-process-emote-ranges (ers msg)
"Apply all of ERS to MSG."
(--reduce-from (w/twitch-process-emote-range it acc) msg ers))
@@ -479,17 +488,17 @@ CALLBACK will be passed the winner when the poll concludes."
(goto-char (line-beginning-position))
(while (not (eobp))
(let ((plist (text-properties-at (point)))
- (next-change
- (or (next-property-change (point) (current-buffer))
+ (next-change
+ (or (next-property-change (point) (current-buffer))
(point-max))))
(when-let* ((plist-true plist)
- (disp (plist-get plist 'display))
- (is-image (equal (car disp) 'image))
- (image-props (cdr disp))
- (image-type (plist-get image-props :type))
- (is-gif (equal image-type 'gif))
- (multi-frame (or (plist-get (cdr disp) :animate-multi-frame-data) (image-multi-frame-p disp)))
- )
+ (disp (plist-get plist 'display))
+ (is-image (equal (car disp) 'image))
+ (image-props (cdr disp))
+ (image-type (plist-get image-props :type))
+ (is-gif (equal image-type 'gif))
+ (multi-frame (or (plist-get (cdr disp) :animate-multi-frame-data) (image-multi-frame-p disp)))
+ )
(let ((frame (% w/twitch-emote-frame-counter (car multi-frame))))
(image-show-frame disp frame)))
(goto-char next-change))))))
@@ -500,83 +509,86 @@ CALLBACK will be passed the winner when the poll concludes."
(cancel-timer w/twitch-emote-frame-timer))
(w/twitch-advance-frame-in-chat-buffer)
(setq
- w/twitch-emote-frame-timer
- (run-with-timer 0.03 nil #'w/twitch-run-emote-frame-timer)))
+ w/twitch-emote-frame-timer
+ (run-with-timer 0.03 nil #'w/twitch-run-emote-frame-timer)))
(defun w/twitch-badges-sigil (badges)
"Return the sigil character BADGES for the current user."
- (let* ((equity (alist-get :equity w/user-current))
- (name (s-downcase w/user-current-name))
- (max-stars (w/aoc-max-stars))
- (aoc-stars (w/aoc-lookup-stars name)))
+ (let* ( ;; (equity (alist-get :equity w/user-current))
+ (equity 0)
+ ;; (name (s-downcase w/user-current-name))
+ (name "")
+ (max-stars (w/aoc-max-stars))
+ (aoc-stars (w/aoc-lookup-stars name)))
(apply
- #'s-concat
- (-non-nil
- (list
- (when (and aoc-stars (>= aoc-stars max-stars)) "πŸŽ„")
- (when (-contains? badges "broadcaster/1") "(it me)")
- (when (-contains? badges "moderator/1") "βš”")
- (when (-contains? badges "artist-badge/1") "πŸ–ŒοΈ")
- (when (and equity (> equity 0))
- (cond ;; The Equity Lords
- ((s-equals? name "bezelea") "β™ΏπŸ””")
- ((s-equals? name "altovt") "πŸ“ˆ")
- ((s-equals? name "prodzpod") "πŸŒ πŸŒŒπŸŽ‘") ;; owed 1 emote
- ((s-equals? name "faeliore") "😹")
- ((s-equals? name "vasher_1025") "πŸ•΄")
- ((s-equals? name "leadengin") "πŸ’ˆ")
- ;; ((s-equals? name "kettlestew") "")
- ((s-equals? name "blazynights") "πŸ€„")
- ;; ((s-equals? name "must_broke_") "")
- ((s-equals? name "bvnanana") "πŸ§‰")
- ((s-equals? name "venorrak") "πŸ“ΊπŸ“œ")
- ;; ((s-equals? name "tf_tokyo") "")
- ((s-equals? name "devts_de") "βˆƒ")
- ((s-equals? name "trap_exit") (s-concat (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_dfc4c36ccd3b4994b8ca4f082230f053"))) "β˜ πŸ’€"))
- ((s-equals? name "essento") "πŸ₯š")
- ((s-equals? name "tyumici") "🀌")
- ;; clone is lord ((s-equals? name "liquidcake1") "")
- ;; ((s-equals? name "loufbread_") "")
- ((s-equals? name "yellowberryhn") "πŸͺ΄")
- ;; ((s-equals? name "maradyne_") "") ;; owed 1
- ;; ((s-equals? name "sampie159") "")
- ((s-equals? name "zamielpayne") "πŸ¦‰")
- ((s-equals? name "xorxavier") "🌸")
- ((s-equals? name "6horntaurus") "⚰️") ;; owed 1
- ((s-equals? name "bytomancer") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_beb191005b81486c8b1c823931c88387"))))
- ;; ((s-equals? name "henriqmarq") "")
- ;; ((s-equals? name "wyndupboy") "")
- ((s-equals? name "hellpie") "πŸ₯§") ;; owed 1
- ((s-equals? name "steeledshield") "⛨")
- ((s-equals? name "asrael_io") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_a9dc5935824a4d6792f4b48f91031fcf"))))
- ((s-equals? name "nichepenguin") "πŸ’š")
- ;; ((s-equals? name "h_ingles") "")
- ;; ((s-equals? name "compilingjay") "")
- ;; ((s-equals? name "watchmakering") "")
- ((s-equals? name "the0x539") "︘")
- ((s-equals? name "colinahscopy_") "⚜")
- ;; ((s-equals? name "eighteyedsixwingedseraph") "")
- ;; ((s-equals? name "a_tension_span") "")
- ;; ((s-equals? name "tomaterr") "")
- ((s-equals? name "realnaesten") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_4d2812c659c14c64a9a4044c3eff6d30"))))
- ;; ((s-equals? name "fmega") "")
- ;; ((s-equals? name "cr4zyk1tty") "")
- ;; ((s-equals? name "devts_de") "")
- ;; ((s-equals? name "physbuzz") "")
- ((s-equals? name "sundemoniac") "🌞")
- (t "EL.")))
- (when (-contains? badges "vip/1") "πŸ’Ž")
- (when (-contains? badges "subscriber/0") "πŸ’»")
- (when (-contains? badges "founder/0") "πŸ–₯️"))))))
+ #'s-concat
+ (-non-nil
+ (list
+ (when (and aoc-stars (>= aoc-stars max-stars)) "πŸŽ„")
+ (when (-contains? badges "broadcaster/1") "(it me)")
+ (when (-contains? badges "moderator/1") "βš”")
+ (when (-contains? badges "artist-badge/1") "πŸ–ŒοΈ")
+ (when (and equity (> equity 0))
+ (cond ;; The Equity Lords
+ ((s-equals? name "bezelea") "β™ΏπŸ””") ;; owed 1
+ ((s-equals? name "altovt") "πŸ“ˆ")
+ ((s-equals? name "prodzpod") "πŸŒ πŸŒŒπŸŽ‘") ;; owed 1 emote
+ ((s-equals? name "faeliore") "😹")
+ ((s-equals? name "vasher_1025") "πŸ•΄")
+ ((s-equals? name "leadengin") "πŸ’ˆ")
+ ;; ((s-equals? name "kettlestew") "")
+ ((s-equals? name "blazynights") "πŸ€„")
+ ;; ((s-equals? name "must_broke_") "")
+ ((s-equals? name "bvnanana") "πŸ§‰")
+ ((s-equals? name "venorrak") "πŸ“ΊπŸ“œ")
+ ;; ((s-equals? name "tf_tokyo") "")
+ ((s-equals? name "devts_de") "βˆƒ")
+ ((s-equals? name "trap_exit") (s-concat (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_dfc4c36ccd3b4994b8ca4f082230f053"))) "β˜ πŸ’€"))
+ ((s-equals? name "essento") "πŸ₯š")
+ ((s-equals? name "tyumici") "🀌")
+ ;; clone is lord ((s-equals? name "liquidcake1") "")
+ ;; ((s-equals? name "loufbread_") "")
+ ((s-equals? name "yellowberryhn") "πŸͺ΄")
+ ;; ((s-equals? name "maradyne_") "") ;; owed 1
+ ;; ((s-equals? name "sampie159") "")
+ ((s-equals? name "zamielpayne") "πŸ¦‰")
+ ((s-equals? name "xorxavier") "🌸")
+ ((s-equals? name "6horntaurus") "⚰️") ;; owed 1
+ ((s-equals? name "bytomancer") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_beb191005b81486c8b1c823931c88387"))))
+ ;; ((s-equals? name "henriqmarq") "")
+ ;; ((s-equals? name "wyndupboy") "")
+ ((s-equals? name "hellpie") "πŸ₯§") ;; owed 1
+ ((s-equals? name "steeledshield") "⛨")
+ ((s-equals? name "asrael_io") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_a9dc5935824a4d6792f4b48f91031fcf"))))
+ ((s-equals? name "nichepenguin") "πŸ’š")
+ ;; ((s-equals? name "h_ingles") "")
+ ;; ((s-equals? name "compilingjay") "")
+ ;; ((s-equals? name "watchmakering") "")
+ ((s-equals? name "the0x539") "︘")
+ ((s-equals? name "colinahscopy_") "⚜")
+ ;; ((s-equals? name "eighteyedsixwingedseraph") "")
+ ;; ((s-equals? name "a_tension_span") "")
+ ;; ((s-equals? name "tomaterr") "")
+ ((s-equals? name "realnaesten") (propertize "Q" 'display (create-image (w/twitch-emote-path "emotesv2_4d2812c659c14c64a9a4044c3eff6d30"))))
+ ;; ((s-equals? name "fmega") "")
+ ;; ((s-equals? name "cr4zyk1tty") "")
+ ;; ((s-equals? name "devts_de") "")
+ ;; ((s-equals? name "physbuzz") "")
+ ((s-equals? name "sundemoniac") "🌞")
+ ((s-equals? name "pralkarz") "🌞")
+ (t "EL.")))
+ (when (-contains? badges "vip/1") "πŸ’Ž")
+ (when (-contains? badges "subscriber/0") "πŸ’»")
+ (when (-contains? badges "founder/0") "πŸ–₯️"))))))
(defun w/twitch-handle-incoming-chat (user tags text)
"Write the message TEXT from USER with TAGS to the chat buffer.
Process any commands included."
+ (w/binary-pub "overlay combo message" (s-join "," (--map (format "%s" it) (w/combo-classify text))))
(w/user-bind
user
- (lambda ()
- (let* ( (userid (car (w/saget "user-id" tags)))
- (color (car (w/saget "color" tags)))
+ (lambda (uid unm)
+ (let* ( (color (car (w/saget "color" tags)))
(emotes (car (w/saget "emotes" tags)))
(badges (s-split "," (car (w/saget "badges" tags))))
(biblicality (w/bible-colorize-sentence text))
@@ -587,27 +599,25 @@ Process any commands included."
(s-split "/" emotes)
text-colored-bible))))
(push (cons user text) w/twitch-chat-history)
- (w/user-stats-update)
- (w/user-stats-update-color color)
- (w/hexamedia-update-user user)
- (w/shindaggers-update-user user)
- (w/copfish-update-user user)
+ (w/user-stats-update uid unm)
+ (w/user-color-update uid unm color)
(when (s-equals? (s-downcase user) "modclonk")
(w/obs-activate-toggle 'modclonk))
- (w/resolve-record-user user)
(w/hex-tick user)
- (w/hex-transform
- user
- (w/make-chat-message
- :user user
- :id userid
- :text text-with-emotes
- :user-color (when (s-present? color) color)
- :biblicality (cdr biblicality)
- :sigil (w/twitch-badges-sigil badges)))
+ (w/user-badge-text uid
+ (lambda (sigils)
+ (w/hex-transform
+ user
+ (w/make-chat-message
+ :user user
+ :id uid
+ :text text-with-emotes
+ :user-color (when (s-present? color) color)
+ :biblicality (cdr biblicality)
+ :sigil (s-concat sigils (w/twitch-badges-sigil badges))))))
(--each w/twitch-chat-commands
(when (s-contains? (car it) text)
- (funcall (cdr it) user text)))))))
+ (funcall (cdr it) uid user text)))))))
(defun w/twitch-handle-incoming-chat-sexp (msg)
"Write MSG to the chat buffer, processing any commands."
@@ -622,9 +632,9 @@ Optionally, only apply redeems with point costs less than LIMIT."
(if (or (not limit) (< (car handler) limit))
(w/user-bind
user
- (lambda ()
+ (lambda (uid unm)
(condition-case err
- (funcall (cadr handler) user input)
+ (funcall (cadr handler) uid unm input)
(error
(w/chat-write-event (format "Error during redeem: %s" err))))))
(w/chat-write-event (format "User %s attempted to activate overly expensive redeem \"%s\" via API" user redeem)))
@@ -632,7 +642,7 @@ Optionally, only apply redeems with point costs less than LIMIT."
(defun w/twitch-handle-redeem (r)
"Handle the channel point redeem R."
- ;; (w/write-log r)
+ (w/write-log r)
(let* ((user (car r))
(redeem (cadr r))
(encoded-input (caddr r))
diff --git a/src/wasp-user-badges.el b/src/wasp-user-badges.el
new file mode 100644
index 00000000..d34a9fa6
--- /dev/null
+++ b/src/wasp-user-badges.el
@@ -0,0 +1,164 @@
+;;; wsp-user-badges --- badges -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'wasp-utils)
+(require 'wasp-db)
+
+(defvar w/badge-cache (ht-create))
+(defun w/badge-populate-cache ()
+ "Populate the `w/badge-cache' with all badges."
+ (message "Populating badge cache...")
+ (w/db-hgetall "badge:name"
+ (lambda (bnames)
+ (message "Retrieved names...")
+ (w/db-hgetall "badge:desc"
+ (lambda (bdescs)
+ (message "Retrieved descriptions...")
+ (w/db-hgetall "badge:mode"
+ (lambda (bmodes)
+ (message "Retrieved modes...")
+ (w/db-hgetall "badge:text"
+ (lambda (btexts)
+ (message "Retrieved text...")
+ (w/db-hgetall "badge:icon"
+ (lambda (bicons)
+ (message "Retrieved icons!")
+ (ht-clear! w/badge-cache)
+ (--each (ht-keys bnames)
+ (message "Processing badge: %s" it)
+ (ht-set! w/badge-cache it
+ (ht<-alist
+ `( (name . ,(ht-get bnames it))
+ (desc . ,(ht-get bdescs it))
+ (mode . ,(ht-get bmodes it))
+ (text . ,(ht-get btexts it))
+ (icon .
+ ,(when-let* ((data (ht-get bicons it)))
+ (create-image
+ (encode-coding-string data 'no-conversion)
+ nil t
+ :height 20
+ :ascent 'center))))))))))))))))))
+
+(defun w/user-badge-text (uid k)
+ "Pass a string of badges to attach to UID's messages to K."
+ (w/db-smembers (s-concat "user:badges:" uid)
+ (lambda (badges)
+ (funcall k
+ (s-join ""
+ (--map
+ (when-let* ((b (ht-get w/badge-cache it)))
+ (cond
+ ((s-equals? (ht-get b 'mode) "text")
+ (ht-get b 'text))
+ ((s-equals? (ht-get b 'mode) "icon")
+ (propertize "Q" 'display (ht-get b 'icon)))))
+ badges))))))
+
+(defun w/badge-create-text (bid name desc text)
+ "Create a new talent BID called NAME with DESC and TEXT."
+ (w/db-hset "badge:name" bid name)
+ (w/db-hset "badge:desc" bid desc)
+ (w/db-hset "badge:mode" bid "text")
+ (w/db-hset "badge:text" bid text))
+
+(defun w/badge-create-icon (bid name desc iconpath)
+ "Create a new talent BID called NAME with DESC and ICONPATH."
+ (let ((icon (or (f-read-bytes iconpath) (error "Failed to open icon path: %s" iconpath))))
+ (progn
+ (w/db-hset "badge:name" bid name)
+ (w/db-hset "badge:desc" bid desc)
+ (w/db-hset "badge:mode" bid "icon")
+ (w/db-hset "badge:icon" bid icon))))
+
+(defun w/badge-grant (uid bid)
+ "Give badge BID to user UID."
+ (w/db-sadd (s-concat "user:badges:" uid) bid))
+
+(defun w/badge-custom-equity-text (user text)
+ "Give badge TEXT to USER."
+ (let ((bid (w/uuid)))
+ (w/badge-create-text bid "Equity Lord" "This user holds Equity Lordship status, entitling them to a vote at the shareholders' meeting." text)
+ (w/user-id-from-name user
+ (lambda (uid)
+ (w/badge-grant uid bid)))))
+
+(defun w/badge-custom-equity-icon (user icon)
+ "Give badge ICON to USER."
+ (let ((bid (w/uuid)))
+ (w/badge-create-icon bid "Equity Lord" "This user holds Equity Lordship status, entitling them to a vote at the shareholders' meeting." icon)
+ (w/user-id-from-name user
+ (lambda (uid)
+ (w/badge-grant uid bid)))))
+
+(defun w/badge-default-equity (user)
+ "Give USER the default equity badge."
+ (w/user-id-from-name user
+ (lambda (uid)
+ (w/badge-grant uid "equitylord"))))
+
+;; (w/badge-create-icon "equitylord" "Equity Lord" "This user holds Equity Lordship status, entitling them to a vote at the shareholders' meeting." (w/asset "badges/equitylord.png"))
+;; (w/badge-create-text "themale" "MALE" "This user is known as \"The Male\" by some." "πŸ§β€β™‚οΈ")
+;; (w/badge-create-icon "slime" "Slime" "This user is \"slime\"." (w/asset "badges/slime.png"))
+;; (w/user-id-from-name "LCOLONQ"
+;; (lambda (uid)
+;; (w/badge-grant uid "equitylord")
+;; (w/badge-grant uid "themale")
+;; ))
+;; (w/user-id-from-name "acher0_" (lambda (uid) (w/badge-grant uid "slime")))
+;; (w/badge-custom-equity-text "bezelea" "β™Ώ")
+;; (w/badge-custom-equity-text "bezelea" "πŸ””")
+;; (w/badge-custom-equity-text "altovt" "πŸ“ˆ")
+;; (w/badge-custom-equity-text "prodzpod" "🌠")
+;; (w/badge-custom-equity-text "prodzpod" "🌌")
+;; (w/badge-custom-equity-text "prodzpod" "πŸŽ‘")
+;; (w/badge-custom-equity-text "faeliore" "😹")
+;; (w/badge-custom-equity-text "vasher_1025" "πŸ•΄")
+;; (w/badge-custom-equity-text "leadengin" "πŸ’ˆ")
+;; (w/badge-default-equity "kettlestew")
+;; (w/badge-custom-equity-text "blazynights" "πŸ€„")
+;; (w/badge-default-equity "must_broke_")
+;; (w/badge-custom-equity-text "bvnanana" "πŸ§‰")
+;; (w/badge-custom-equity-text "venorrak" "πŸ“Ί")
+;; (w/badge-custom-equity-text "venorrak" "πŸ“œ")
+;; (w/badge-default-equity "tf_tokyo")
+;; (w/badge-custom-equity-text "devts_de" "βˆƒ")
+;; (w/badge-custom-equity-icon "trap_exit" (w/twitch-emote-path "emotesv2_dfc4c36ccd3b4994b8ca4f082230f053"))
+;; (w/badge-custom-equity-text "trap_exit" "☠")
+;; (w/badge-custom-equity-text "trap_exit" "πŸ’€")
+;; (w/badge-custom-equity-text "essento" "πŸ₯š")
+;; (w/badge-custom-equity-text "tyumici" "🀌")
+;; (w/badge-default-equity "liquidcake1")
+;; (w/badge-default-equity "loufbread")
+;; (w/badge-custom-equity-text "yellowberryhn" "πŸͺ΄")
+;; (w/badge-default-equity "maradyne_")
+;; (w/badge-default-equity "sampie159")
+;; (w/badge-custom-equity-text "zamielpayne" "πŸ¦‰")
+;; (w/badge-custom-equity-text "xorxavier" "🌸")
+;; (w/badge-custom-equity-text "6horntaurus" "⚰")
+;; (w/badge-custom-equity-icon "bytomancer" (w/twitch-emote-path "emotesv2_beb191005b81486c8b1c823931c88387"))
+;; (w/badge-default-equity "henriqmarq")
+;; (w/badge-default-equity "wyndupboy")
+;; (w/badge-custom-equity-text "steeledshield" "⛨")
+;; (w/badge-custom-equity-icon "asrael_io" (w/twitch-emote-path "emotesv2_a9dc5935824a4d6792f4b48f91031fcf"))
+;; (w/badge-custom-equity-text "nichepenguin" "πŸ’š")
+;; (w/badge-default-equity "h_ingles")
+;; (w/badge-default-equity "compilingjay")
+;; (w/badge-default-equity "watchmakering")
+;; (w/badge-custom-equity-text "the0x539" "︘")
+;; (w/badge-custom-equity-text "colinahscopy_" "⚜")
+;; (w/badge-default-equity "eighteyedsixwingedseraph")
+;; (w/badge-default-equity "a_tension_span")
+;; (w/badge-default-equity "tomaterr")
+;; (w/badge-custom-equity-icon "realnaesten" (w/twitch-emote-path "emotesv2_4d2812c659c14c64a9a4044c3eff6d30"))
+;; (w/badge-default-equity "fmega")
+;; (w/badge-default-equity "cr4zyk1tty")
+;; (w/badge-default-equity "physbuzz")
+;; (w/badge-custom-equity-text "sundemoniac" "🌞")
+;; (w/badge-custom-equity-text "pralkewe67" "🌞")
+;; (w/badge-custom-equity-text "nichePenguin" "πŸ‹β€πŸŸ©")
+;; (w/badge-custom-equity-icon "ellg" (w/twitch-emote-path "emotesv2_27a7f5d066144fb59490efae6c91820a"))
+
+(provide 'wasp-user-badges)
+;;; wasp-user-badges.el ends here
diff --git a/src/wasp-user-stats.el b/src/wasp-user-stats.el
index 74c320aa..6fd52f81 100644
--- a/src/wasp-user-stats.el
+++ b/src/wasp-user-stats.el
@@ -7,24 +7,26 @@
(require 'ht)
(require 'wasp-user)
+;;;; Factions
(defvar w/user-faction-exemptions
(list
"LCOLONQ"
"MODCLONK"
"fn_lumi"))
-
(defun w/user-initial-faction (user)
"Determine the initial faction for USER."
(unless (-contains? w/user-faction-exemptions user)
- (let* ((factions '(nate lever tony)))
+ (let* ((factions '("nate" "lever" "tony")))
(nth (random (length factions)) factions))))
-(defun w/user-ensure-faction ()
- "Ensure that the current user has a faction assigned."
- (let ((cur (alist-get :faction w/user-current)))
- (unless cur
- (setf (alist-get :faction w/user-current)
- (w/user-initial-faction w/user-current-name)))))
+(defun w/user-ensure-faction (uid nm)
+ "Ensure that user UID named NM has a faction assigned."
+ (w/user-get-property uid "faction"
+ (lambda (cur)
+ (unless cur
+ (when-let* ((new (w/user-initial-faction nm)))
+ (w/user-set-property uid "faction" new))))))
+;;;; Elements
(defconst w/user-elements
'(("fire" "πŸ”₯" "red")
("water" "🌊" "blue")
@@ -36,44 +38,67 @@
"Determine the initial faction for USER."
(let* ((factions w/user-elements))
(car (nth (random (length factions)) factions))))
-(defun w/user-ensure-element ()
- "Ensure that the current user has a faction assigned."
- (let ((cur (alist-get :element w/user-current)))
- (unless cur
- (setf (alist-get :element w/user-current)
- (w/user-initial-element)))))
+(defun w/user-ensure-element (uid nm)
+ "Ensure that the user UID named NM has a faction assigned."
+ (ignore nm)
+ (w/user-get-property uid "element"
+ (lambda (cur)
+ (unless cur
+ (w/user-set-property uid "element" (w/user-initial-element))))))
-(defun w/user-faction-total (faction)
- "Compute the boost totals for FACTION."
- (-sum
- (-non-nil
- (--map
- (alist-get :boost it)
- (--filter
- (and (listp it) (eq faction (alist-get :faction it)))
- (ht-values w/user-cache))))))
-(defun w/user-faction-totals ()
- "Compute the boost totals for each FACTION."
- (list
- (w/user-faction-total 'nate)
- (w/user-faction-total 'tony)
- (w/user-faction-total 'lever)))
+(defun w/user-ensure-name (uid nm)
+ "Ensure that the user UID named NM has a name assigned."
+ (w/user-get-property uid "name"
+ (lambda (cur)
+ (unless (and cur (s-equals? (s-downcase cur) (s-downcase nm)))
+ (w/user-set-property uid "name"
+ (s-downcase nm))))))
+
+(defun w/user-ensure-attribute (uid nm attr init)
+ "Ensure that the user UID named NM has attribute ATTR assigned.
+INIT is the initial value."
+ (ignore nm)
+ (w/user-get-stat uid attr
+ (lambda (cur)
+ (unless (integerp cur)
+ (w/user-set-stat uid attr init)))))
+
+(defun w/user-ensure-attribute-nonzero (uid nm attr init)
+ "Ensure that the user UID named NM has attribute ATTR assigned.
+INIT is the initial value."
+ (ignore nm)
+ (w/user-get-stat uid attr
+ (lambda (cur)
+ (unless (and (integerp cur) (> cur 0))
+ (w/user-set-stat uid attr init)))))
-(defun w/user-ensure-name ()
- "Ensure that the current user has a name assigned."
- (let ((cur (alist-get :name w/user-current)))
- (unless cur
- (setf (alist-get :name w/user-current) w/user-current-name))))
+(defun w/user-ensure-icon (uid nm)
+ "Ensure that the user UID named NM has an icon uploaded."
+ (let ((key (format "user:avatar:%s" uid)))
+ (w/db-exists key
+ (lambda (exists)
+ (unless exists
+ (w/twitch-get-user-avatar nm
+ (lambda ()
+ (when-let* ((avatar (w/user-avatar nm)))
+ (w/db-set key avatar)))))))))
-(defun w/user-stats-update ()
- "Ensure that the current user has all stats."
- (w/user-ensure-name)
- (w/user-ensure-faction)
- (w/user-ensure-element))
+(defun w/user-stats-update (uid nm)
+ "Ensure that the user UID named NM has all stats."
+ (w/user-ensure-faction uid nm)
+ (w/user-ensure-element uid nm)
+ (w/user-ensure-name uid nm)
+ (w/user-ensure-icon uid nm)
+ (w/user-ensure-attribute uid nm "power" (+ 9 (random 3)))
+ (w/user-ensure-attribute uid nm "speed" (+ 9 (random 3)))
+ (w/user-ensure-attribute uid nm "majjyka" (+ 9 (random 3)))
+ (w/user-ensure-attribute uid nm "wisdom" (+ 9 (random 3)))
+ (w/user-ensure-attribute-nonzero uid nm "talentpoints" 5))
-(defun w/user-stats-update-color (color)
- "Ensure that COLOR is set for the current user."
- (setf (alist-get :color w/user-current) color))
+(defun w/user-color-update (uid nm color)
+ "Ensure that COLOR is set for the user UID named NM."
+ (ignore nm)
+ (w/user-set-property uid "color" color))
(provide 'wasp-user-stats)
;;; wasp-user-stats.el ends here
diff --git a/src/wasp-user-talents.el b/src/wasp-user-talents.el
new file mode 100644
index 00000000..8b251c9f
--- /dev/null
+++ b/src/wasp-user-talents.el
@@ -0,0 +1,27 @@
+;;; wasp-user-talents --- skills and perks and talents -*- lexical-binding: t; -*-
+;;; Commentary:
+;;; Code:
+
+(require 'wasp-utils)
+(require 'wasp-db)
+
+;; talent:name -> hashmap from tids to names
+;; talent:desc -> hashmap from tids to description
+;; talent:icon -> hashmap from tids to image bytes
+(defun w/talent-create (tid name desc iconpath)
+ "Create a new talent TID called NAME with DESC and ICONPATH."
+ (let ((icon (or (f-read-bytes iconpath) (error "Failed to open icon path: %s" iconpath))))
+ (progn
+ (w/db-hset "talent:name" tid name)
+ (w/db-hset "talent:desc" tid desc)
+ (w/db-hset "talent:icon" tid icon))))
+
+;; (w/talent-create "bigjoel" "Bigger Joels" "Increase the size of Joels posted." (w/asset "talents/bigjoel.png"))
+;; (w/talent-create "shaderopacity" "Opacity" "Make shaders more opaque." (w/asset "talents/shaderopacity.png"))
+;; (w/talent-create "bonuscard" "Bonus Cards" "Gain a chance to draw extra cards." (w/asset "talents/bonuscard.png"))
+;; (w/talent-create "rarityboost" "Rarity Boost" "Cards you draw have higher rarity." (w/asset "talents/rarityboost.png"))
+;; (w/talent-create "beastmaster" "Beastmaster" "Friend likes you more." (w/asset "talents/beastmaster.png"))
+;; (w/talent-create "multicast" "Multi-cast" "Hexes have a chance to cast on an additional target." (w/asset "talents/multicast.png"))
+
+(provide 'wasp-user-talents)
+;;; wasp-user-talents.el ends here
diff --git a/src/wasp-user-whitelist.el b/src/wasp-user-whitelist.el
index 1ca04f5d..ac3ab490 100644
--- a/src/wasp-user-whitelist.el
+++ b/src/wasp-user-whitelist.el
@@ -274,6 +274,12 @@
"whomstve340"
"adamantris"
"bussy_smasher"
+ "talon1024"
+ "LesbianMonad"
+ "lolcatswag"
+ "charstring88"
+ "bigbookofbug"
+ "luasoft10"
)))
(setq
diff --git a/src/wasp-user.el b/src/wasp-user.el
index 08e5c971..de5df6f8 100644
--- a/src/wasp-user.el
+++ b/src/wasp-user.el
@@ -9,139 +9,108 @@
(defvar w/user-whitelist nil)
-(defvar w/user-current-name nil)
-(defvar w/user-current nil)
-
-(defvar w/user-cache (ht-create)
- "A read-only cache of user records for the current session.")
-
-(defun w/user-cache-update (nm d)
- "Set the cache entry for user NM to D."
- (ht-set! w/user-cache (s-downcase nm) d))
-
-(defun w/user-cache-get (nm)
- "Get the cache entry for user NM."
- (ht-get w/user-cache (s-downcase nm)))
-
-(defun w/user-cache-populate ()
- "Populate `w/user-cache' with entries for all users.
-\(This is slow, so it happens once at startup.\)"
- (ht-clear! w/user-cache)
- (w/db-keys
- "user:*"
- (lambda (users)
- (--each users
- (let ((nm (cadr (s-split ":" it))))
- (w/user-get
- nm
- (lambda (_)
- (message "Updated cache for %s" nm))))))))
-
-(defun w/user-db-key (nm)
- "Return the database key for user NM."
- (s-concat "user:" (s-downcase nm)))
-
-(defun w/user-get (nm k)
- "Fetch user data for user NM.
-Pass the resulting Lisp form to K."
- (when (and nm (stringp nm) (functionp k))
- (w/db-get
- (w/user-db-key nm)
- (lambda (d)
- (if-let*
- ((d)
- (stringp d)
- (res (w/read-sexp d)))
- (progn
- (w/user-cache-update nm res)
- (funcall k res))
- (funcall k nil))))))
-
-(defun w/user-set (nm d)
- "Save the Lisp form D as the user data for NM."
- (when (and nm (stringp nm) d)
- (w/user-cache-update nm d)
- (w/db-set
- (w/user-db-key nm)
- (format "%S" d))))
+(defun w/user-id-from-name (nm k)
+ "Pass user ID corresponding to NM to K."
+ (let ((key (s-concat "user-id:" (s-downcase nm))))
+ (w/db-get key
+ (lambda (uid)
+ (if (s-present? uid)
+ (funcall k uid)
+ (w/twitch-get-user-id nm
+ (lambda (v)
+ (w/db-set key v)
+ (funcall k v))))))))
(defun w/user-bind (nm k)
- "Bind the data for user NM to `w/user-current' during K.
-Save it back to the database after K returns."
- (w/user-get
- nm
- (lambda (d)
- (let ((w/user-current d)
- (w/user-current-name nm))
- (funcall k)
- (w/user-set nm w/user-current)))))
-
-(defun w/user-authorized ()
- "Return non-nil if the current user is authorized to use advanced techniques."
- (let ((boost (alist-get :boost w/user-current)))
- (or (and boost (> boost 2))
- (and boost (< boost -2))
- (-contains? w/user-whitelist (s-downcase w/user-current-name)))))
-
-(defun w/user-boost (user)
- "Increase USER's boost power by 1."
- (w/user-get
- user
- (lambda (d)
- (cl-incf (alist-get :boost d 0))
- (w/user-set user d))))
-
-(defun w/user-tsoob (user)
- "Decrement USER's boost power by 1."
- (w/user-get
- user
- (lambda (d)
- (cl-decf (alist-get :boost d 0))
- (w/user-set user d))))
-
-(defun w/user-add-bookrec (user book)
- "Add a recommendation for BOOK from USER."
- (w/user-get
- "__books__"
- (lambda (b)
- (w/user-set "__books__" (cons (cons book user) b)))))
-
-(defun w/user-add-quote (user q)
- "Add a recommendation for BOOK from USER."
- (w/user-get
- "__quotes__"
- (lambda (qs)
- (w/user-set "__quotes__" (cons (cons q user) qs)))))
-
-(defun w/user-crown (user)
- "Increment USER's equity status."
- (w/user-get
- user
- (lambda (u)
- (let ((old (or (alist-get :equity u) 0)))
- (setf (alist-get :equity u) (+ old 1)))
- (print u)
- (w/user-set user u))))
-
-(defun w/user-decrown (user)
- "Decrement USER's equity status."
- (w/user-get
- user
- (lambda (u)
- (let ((old (or (alist-get :equity u) 0)))
- (setf (alist-get :equity u) (- old 1)))
- (print u)
- (w/user-set user u))))
-
-(defun w/user-boost-compensation (user)
- "Give USER a consolation BOOSTPOWER prize."
- (w/user-get
- user
- (lambda (u)
- (let ((old (or (alist-get :boost u) 0)))
- (setf (alist-get :boost u) (+ old 20)))
- (print u)
- (w/user-set user u))))
+ "Pass the ID and NM for the named user to K."
+ (w/user-id-from-name nm
+ (lambda (uid)
+ (funcall k uid nm))))
+
+(defun w/user-get-stat (uid stat k)
+ "Get STAT from user UID and pass it to K."
+ (when (stringp uid)
+ (let ((key (s-concat "user:stats:" uid)))
+ (w/db-hget key stat
+ (lambda (v)
+ (funcall k
+ (if-let*
+ ( (_ v)
+ (_ (s-present? v))
+ (cur (string-to-number v))
+ (_ (integerp cur)))
+ cur
+ nil)))))))
+
+(defun w/user-set-stat (uid stat val)
+ "Set STAT for user UID to VAL."
+ (when (stringp uid)
+ (let ((key (s-concat "user:stats:" uid)))
+ (if (integerp val)
+ (w/db-hset key stat (format "%s" val))
+ (message "Attempted to write stat %s to non-integer %s" stat val)))))
+
+(defun w/user-get-property (uid prop k)
+ "Get PROP from user UID and pass it to K."
+ (when (stringp uid)
+ (let ((key (s-concat "user:properties:" uid)))
+ (w/db-hget key prop
+ (lambda (v)
+ (funcall k (if (s-present? v) v nil)))))))
+
+(defun w/user-set-property (uid prop val)
+ "Set PROP for user UID to VAL."
+ (when (stringp uid)
+ (let ((key (s-concat "user:properties:" uid)))
+ (w/db-hset key prop (format "%s" val)))))
+
+(defun w/user-when-authorized (uid k)
+ "Call K when user UID is authorized."
+ (w/user-get-property uid "boost"
+ (lambda (scur)
+ (w/user-get-property uid "name"
+ (lambda (name)
+ (let ((cur (or (w/read-sexp scur) 0)))
+ (if (or (> (abs cur) 2) (-contains? w/user-whitelist name))
+ (funcall k)
+ (w/chat-write-event (format "%s is not authorized, boost harder" name)))))))))
+
+(defun w/user-crown (&optional uid)
+ "Increase UID's equity by 1."
+ (when uid
+ (w/user-get-stat uid "equity"
+ (lambda (cur)
+ (message "Former equity was %s, new equity is %s" (or cur 0) (+ (or cur 0) 1))
+ (w/user-set-stat uid "equity" (+ (or cur 0) 1))))))
+
+(defun w/user-boost (uid)
+ "Increase UID's boost power by 1."
+ (when uid
+ (w/user-get-property uid "boost"
+ (lambda (scur)
+ (when-let* ( (cur (or (w/read-sexp scur) 0))
+ (_ (integerp cur)))
+ (w/user-set-property uid "boost" (format "%s" (+ cur 1))))))))
+
+(defun w/user-tsoob (uid)
+ "Decrement UID's boost power by 1."
+ (when uid
+ (w/user-get-property uid "boost"
+ (lambda (scur)
+ (when-let* ( (cur (or (w/read-sexp scur) 0))
+ (_ (integerp cur)))
+ (w/user-set-property uid "boost" (format "%s" (- cur 1))))))))
+
+(defun w/user-avatar (user)
+ "Return USER's avatar as a unibyte string."
+ (when (f-exists? (w/twitch-user-avatar-path user))
+ (f-read-bytes (w/twitch-user-avatar-path user))))
+
+(defun w/user-charsheet (user)
+ "Browser USER's character sheet."
+ (w/user-id-from-name user
+ (lambda (uid)
+ (browse-url (format "https://api.colonq.computer/charsheet#%s" uid)))))
(provide 'wasp-user)
;;; wasp-user.el ends here
diff --git a/src/wasp-utils.el b/src/wasp-utils.el
index 58040a60..14e4368d 100644
--- a/src/wasp-utils.el
+++ b/src/wasp-utils.el
@@ -4,6 +4,7 @@
(require 's)
(require 'f)
+(require 'ht)
(require 'rx)
(require 'cl-lib)
(require 'eieio)
@@ -23,7 +24,8 @@
(defun w/read-sexp (s)
"Read string S into a Lisp form.
Return nil on error."
- (condition-case nil (read s) (error nil)))
+ (when s
+ (condition-case nil (read s) (error nil))))
(defun w/write (text &optional face)
"Write TEXT to the current buffer and apply FACE."
@@ -61,6 +63,19 @@ BODY is passed directly to `cl-defstruct'."
"Lookup SLOT in the struct S."
`(eieio-oref ,s (quote ,slot)))
+(defun w/struct-fields (cl)
+ "Return a list of fields in CL."
+ (when-let* ((class (cl--find-class cl)))
+ (ht-keys (cl--class-index-table class))))
+
+(defun w/struct-get (s f)
+ "Get field F from S."
+ (eieio-oref s f))
+
+(defun w/aref (a i)
+ "Take element I from A."
+ (ignore-errors (aref a i)))
+
(defun w/pick-random (xs)
"Pick a random element of XS."
(and xs (nth (random (length xs)) xs)))
@@ -159,6 +174,20 @@ Optionally append EXT to the path."
(lambda (data)
(funcall k (json-parse-string data)))))
+(defun w/process (cmd k)
+ "Run CMD in the background and pass its output as a string to K."
+ (let ((buf (generate-new-buffer " *wasp-process-generic*" t)))
+ (make-process
+ :name "wasp-process-generic"
+ :command (if (listp cmd) cmd (list cmd))
+ :buffer buf
+ :coding 'utf-8
+ :sentinel
+ (lambda (_ _)
+ (let ((s (with-current-buffer buf (buffer-string))))
+ (kill-buffer buf)
+ (funcall k s))))))
+
(defun w/devour (start end)
"Delete and return the region from START to END."
;; (w/write-log (format "devouring: %s %s %s" start end (buffer-string)))
@@ -246,7 +275,8 @@ If TEXT is nil, use the empty string instead."
:foreground fg-main
:background bg-alt
:weight 'bold
- :extend t)))
+ :extend t))
+ )
(defun w/random-color ()
"Return a random color string."
@@ -327,5 +357,37 @@ Return a list of the width, height, and pixels of the image."
(let ((res (shell-command-to-string (format "df %s" disk))))
(string-to-number (s-chop-suffix "%" (nth 4 (s-split " " (cadr (s-lines res)) t))))))
+(defun w/show-frame (f vis)
+ "If VIS is non-nil, make the frame F visible.
+Otherwise make it invisible."
+ (when f
+ (if vis
+ (make-frame-visible f)
+ (make-frame-invisible f))))
+(defun w/move-frame (f x y)
+ "Move the frame F to X, Y."
+ (when f
+ (modify-frame-parameters
+ f
+ (list
+ (cons 'top y)
+ (cons 'left x)))))
+(defun w/resize-frame (f w h)
+ "Resize the frame F to W, H."
+ (when f
+ (modify-frame-parameters
+ f
+ (list
+ (cons 'width w)
+ (cons 'height h)))))
+
+(defun w/random-flymake-error ()
+ "Return a random Flymake error from the current buffer or nil."
+ (w/pick-random (flymake-diagnostics)))
+
+(defun w/uuid ()
+ "Generate a UUID."
+ (s-trim (shell-command-to-string "uuidgen")))
+
(provide 'wasp-utils)
;;; wasp-utils.el ends here