(in-package :cl-user) (defpackage chatikbot.server (:use :cl :chatikbot.utils) (:import-from :chatikbot.bot :handle-update) (:import-from :chatikbot.telegram :*telegram-token*) (:export :def-webhook-handler :get-webhook-url :get-oauth-url)) (in-package :chatikbot.server) (defvar *web-path* nil "Set to externally accessible url") (defvar *web-iface* nil "Interface to listen on") (defvar *web-port* 4242 "Port to listen on") (defvar *web-acceptor* nil "Running hunchentoot acceptor") (defun web-start () (when *web-acceptor* (hunchentoot:stop *web-acceptor*)) (when *web-path* (setf *web-acceptor* (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :address *web-iface* :port *web-port*))))) (add-hook :starting #'(lambda () (web-start) (values))) (defun telegram-hook-p (request) (and (equal (hunchentoot:request-method request) :post) (equal (concatenate 'string "/" *telegram-token*) (hunchentoot:script-name request)))) (hunchentoot:define-easy-handler (telegram-webhook-handler :uri #'telegram-hook-p) () (handler-case (let ((stream (hunchentoot:raw-post-data :want-stream t))) (setf *random-state* (make-random-state t)) (setf (flex:flexi-stream-external-format stream) :utf-8) (handle-update (yason:parse stream :object-as :alist))) (error (e) (log:error e))) "OK") (defun get-oauth-url () (quri:render-uri (quri:merge-uris (quri:uri "/oauth") (quri:uri *web-path*)))) (hunchentoot:define-easy-handler (oauth-handler :uri "/oauth") (code error state) (handler-case (run-hooks :oauth code error state) (error (e) (log:error e) (hunchentoot:redirect "/error")))) (hunchentoot:define-easy-handler (error-handler :uri "/error") () " Internal error

Error :'(

Internal server error

") (hunchentoot:define-easy-handler (oauth-success-handler :uri "/oauth/success") () " Auth Success

Success!

OAuth successfully completed. You could close this tab and go back to telegram bot

") (hunchentoot:define-easy-handler (oauth-fail-handler :uri "/oauth/fail") () " Auth failed

Failed :(

OAuth failed. You probably didn't allow the access. Try again.

") (defun webhookp (request) (let ((name (hunchentoot:script-name request))) (and (> (length name) 6) (equal (subseq name 0 6) "/hook/")))) (hunchentoot:define-easy-handler (webhook-handler :uri #'webhookp) () (handler-case (let ((hook (subseq (hunchentoot:script-name*) 6)) (data (hunchentoot:raw-post-data :external-format :utf-8))) (setf *random-state* (make-random-state t)) (run-hooks :webhook hook (when data (yason:parse data :object-as :alist)) (hunchentoot:headers-in*))) (error (e) (log:error e))) "OK") (defmacro def-webhook-handler (name (&rest routes) &body body) (let ((parts (gensym "parts"))) `(progn (defun ,name (hook data headers) (declare (ignorable data headers)) (let ((,parts (split-sequence:split-sequence #\/ hook))) (when (member (car ,parts) (list ,@routes) :test #'equal) (log:info ,parts) (handler-case (let ((paths (rest ,parts))) ,@body) (error (e) (log:error "~A" e)))))) (add-hook :webhook ',name)))) (defun get-webhook-url (route &rest path) (when *web-path* (quri:render-uri (quri:merge-uris (quri:uri (format nil "/hook/~A~{/~A~}" route path)) (quri:uri *web-path*)))))