{´◕ ◡ ◕`}
This requires both expect and tmux to be installed. They are only used in the background. You can use this function with GUI emacs fine. No other dependencies are required for this elisp function to work. The bash/tcl/expect script is embedded in elisp.
Update
The builtin edmacro-format-keys gives me the functionality I want without the rigmarole.

make-kbd-from-string is a function that takes a string literal and gives you a keyboard macro.

I don’t know if it’s possible to do this in emacs lisp. What I ended up doing was making an expect script that spawns a vanilla emacs and runs kmacro-start-macro.

tcl/expect then enters the literal string in and emacs saves the recorded macro to a temporary file which emacs retrieves.

All of this happens inside a temporary tmux window in the background. That was needed because the shell-command function would usually hang if asked to run a program that requires a tty.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(defun e/chomp (str)
  "Chomp leading and tailing whitespace from STR."
  (while (string-match "\\`\n+\\|^\\s-+\\|\\s-+$\\|\n+\\'"
                       str)
    (setq str (replace-match "" t t str)))
  str)

(defun make-kbd-from-string (s)
  (let ((quoted-string (let ((print-escape-newlines t))
                         (prin1-to-string s)))
        (tf (make-temp-file "emacskbm" nil ".exp")))

    (ignore-errors (with-temp-buffer
                     (insert (concat
                              "outfile=/tmp/emacskbm.txt\n"
                              "rm -f \"$outfile\"\n"
                              "\n"
                              "cat > /tmp/emacskbm.exp <<HEREDOC\n"
                              "if { \\$argc >= 1 } {\n"
                              "    set literal [lindex \\$argv 0]\n"
                              "}\n"
                              "\n"
                              "spawn sh\n"
                              "send -- \"emacs -Q -nw\"\n"
                              "send -- \\015\n"
                              "expect -exact \"scratch\"\n"
                              "send -- \\030\n"
                              "send -- \"(\"\n"
                              "send -- \"\\$literal\"\n"
                              "send -- \\030\n"
                              "send -- \")\"\n"
                              "send -- \\033:\n"
                              "send -- \"(with-temp-buffer (insert (replace-regexp-in-string \\\"^Last macro: \\\" \\\"\\\" (kmacro-view-macro))) (write-file \\\"$outfile\\\"))\"\n"
                              "send -- \\015\n"
                              "send -- \\033:\n"
                              "send -- \"(kill-emacs)\"\n"
                              "send -- \\015\n"
                              "send -- \\004\n"
                              "interact\n"
                              "HEREDOC\n"
                              "\n"
                              "{\n"
                              "expect -f /tmp/emacskbm.exp \"$@\"\n"
                              "} &>/dev/null\n"
                              "tmux wait-for -S emacskbm\n"))
                     (write-file tf)))

    (shell-command (concat "tmux neww -d bash " tf " " quoted-string "; tmux wait-for emacskbm"))
    (e/chomp (with-temp-buffer
               (insert-file-contents "/tmp/emacskbm.txt")
               (buffer-string)))))

How to use this function:

1
(make-kbd-from-string "/msg yo hi")

An example of output

1
/msg SPC yo SPC hi

On second thoughts, I suppose this could’ve all been done through term-mode if you had a term-mode inside of a term-mode in which case it could all be done in emacs-lisp. Do I want to find out? I’m not sure.

What is the whole point of this function you may ask? The issue is you can’t (insert "some text") inside of term. You must use a keyboard macro.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(defun irssi-search-channels (pattern)
  (interactive (list (read-string "pattern:")))
  (execute-kbd-macro
   (kbd "M-7"))
  (execute-kbd-macro
   (kbd "C-a C-k"))
  (execute-kbd-macro
   (kbd
    (make-kbd-from-string (concat "/msg alis LIST * -topic " pattern))))
  (execute-kbd-macro
   (kbd "C-m"))
  (execute-kbd-macro
   (kbd
    (make-kbd-from-string "/query alis")))
  (execute-kbd-macro
   (kbd "C-m")))

(define-key irssi-term-mode-map (kbd "M-/") #'irssi-search-channels)

Demonstration

asciinema recording

Extra stuff

expect code generation script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
export TTY

( hs "$(basename "$0")" "$@" "#" "<==" "$(ps -o comm= $PPID)" 0</dev/null ) &>/dev/null

s="$1"

fp=/tmp/emacskbm.txt

{
unbuffer x \
    -sh "emacs -Q -nw" \
    -e scratch \
    -c x \
    -s "(" \
    -s "$1" \
    -c x \
    -s ")" \
    -m : -s "(with-temp-buffer (insert (replace-regexp-in-string \"^Last macro: \" \"\" (kmacro-view-macro))) (write-file \"$fp\"))" -c m \
    -m : -s "(kill-emacs)" -c m \
    -i
} &>/dev/null

cat "$fp"

The more terse version

It’s operational slowness is made up for by its caching.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(defun type-keys (s)
  "Type out the string"
  (interactive (list (read-string "string:")))
  (ekm (make-kbd-from-string s)))
(defalias 'ekl 'type-keys)

(defun make-kbd-from-string (s)
  (let ((quoted-string (let ((print-escape-newlines t))
                         (prin1-to-string s))))
    (chomp (eval `(ci (sh (concat "ci emacs-string2kbm " (q ,s)) nil t))))))

(defun irssi-search-channels (pattern)
  (interactive (list (read-string "pattern:")))
  ;; The 7th window is probably a freenode window
  (ekm "M-7")
  (ekm "C-a C-k")
  ;; (insert "/msg alis LIST * -topic github")
  (ekm (make-kbd-from-string (concat "/msg alis LIST * -topic " pattern)))
  (ekm "C-m")
  (ekm (make-kbd-from-string "/query alis"))
  (ekm "C-m"))