;; -*- lexical-binding: t; -*- (require 'rx) (defconst exercise "mob-in-the-middle") (defun elog (format-string &rest args) (apply #'message (concat "[%.2f] " format-string) (float-time) args)) (defconst upstream-host "chat.protohackers.com") (defconst upstream-port 16963) (defvar upstream-counter 0) (defun upstream-gen-name () (setq upstream-counter (1+ upstream-counter)) (format "upstream-%d" upstream-counter)) (defconst boguscoin-address-rx (rx (or bol " ") (group (: "7" (repeat 25 34 (any "a-zA-Z0-9")))) (or eol " "))) (defconst boguscoin-dest-address "7YWHMfk9JZe0LM0g1ZauHuiSxhI") (defun process-line (string) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward boguscoin-address-rx nil t) (goto-char (match-end 1)) (replace-match boguscoin-dest-address t t nil 1)) (buffer-string))) (defun client-filter (server-process string) ;; (elog "Client received string (%s): %S" server-process string) (let ((downstream (process-get server-process :downstream)) (edited (process-line string))) ;; (elog "Client passed string: %S" edited) (process-send-string downstream edited))) (defun client-sentinel (server-process event) (when (eq (process-status server-process) 'closed) (let ((downstream (process-get server-process :downstream))) (process-put server-process :downstream nil) (delete-process server-process) (when downstream (process-put downstream :upstream nil) (delete-process downstream))))) (defun server-filter (client-process string) ;; (elog "Server received string (%s): %S" client-process string) (let ((buf (process-buffer client-process))) (when (buffer-live-p buf) (with-current-buffer buf (save-excursion (goto-char (process-mark client-process)) (insert string) (set-marker (process-mark client-process) (point))) (let ((upstream (process-get client-process :upstream))) (while (looking-at ".*\n") (let* ((line (buffer-substring (line-beginning-position) (line-end-position))) (edited (process-line line))) (elog "Server passed line: %S" edited) (process-send-string upstream (concat edited "\n"))) (forward-line 1))))))) (defun server-sentinel (client-process event) ;; (elog "Event (%s): %S" client-process event) (when (not (process-buffer client-process)) (set-process-buffer client-process (generate-new-buffer exercise))) (when (not (process-get client-process :upstream)) (let ((upstream (make-network-process :name (upstream-gen-name) :host upstream-host :service upstream-port :family 'ipv4 :coding 'utf-8 :filter #'client-filter :sentinel #'client-sentinel))) (elog "Wiring up %S <-> %S" client-process upstream) (process-put client-process :upstream upstream) (process-put upstream :downstream client-process))) (when (eq (process-status client-process) 'closed) (let ((upstream (process-get client-process :upstream))) (process-put client-process :upstream nil) (delete-process client-process) (when upstream (process-put upstream :downstream nil) (delete-process upstream))))) (defvar server (make-network-process :name exercise :server t :host "0.0.0.0" :service 10000 :family 'ipv4 :coding 'utf-8 :filter #'server-filter :sentinel #'server-sentinel)) (while t (accept-process-output nil 0.01))