1
0

inline.lisp 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. (in-package :cl-user)
  2. (defpackage chatikbot.inline
  3. (:use :cl :chatikbot.utils :chatikbot.telegram :chatikbot.crypto
  4. :chatikbot.macros)
  5. (:export :get-inline-keyboard
  6. :inline-button))
  7. (in-package :chatikbot.inline)
  8. (defstruct button callback created)
  9. (defparameter +max-id+ 2000000 "Buttons store key range")
  10. (defparameter +max-tries+ 100 "Tries to find free store key")
  11. (defparameter +ttl+ (* 60 60 24 7) "Weekly TTL for buttons in store")
  12. (defvar *inline-buttons* (make-hash-table) "Hashtable storing button actions")
  13. (defvar *buttons-lock* (bt:make-recursive-lock "inline buttons lock"))
  14. (defun add-button (callback)
  15. (bt:with-recursive-lock-held (*buttons-lock*)
  16. (let ((id (loop for id = (random +max-id+) then (random +max-id+)
  17. for try from 1
  18. unless (gethash id *inline-buttons*)
  19. do (return id)
  20. when (> try +max-tries+)
  21. do (error "Can't get inline button id in ~A tries" try))))
  22. (setf (gethash id *inline-buttons*)
  23. (make-button :callback callback
  24. :created (get-universal-time)))
  25. id)))
  26. (defun prune-buttons ()
  27. (bt:with-recursive-lock-held (*buttons-lock*)
  28. (let ((now (get-universal-time)))
  29. (loop for id being the hash-keys in *inline-buttons* using (hash-value button)
  30. when (> (- now (button-created button))
  31. +ttl+)
  32. do (remhash id *inline-buttons*)))))
  33. (defcron process-prune-buttons ()
  34. (prune-buttons))
  35. (defmacro inline-button ((text) &body body)
  36. `(cons ,text
  37. (lambda (callback)
  38. (with-parsed-callback callback
  39. (handler-case (progn ,@body)
  40. (error (e)
  41. (log:error "~A" e)
  42. (telegram-answer-callback-query
  43. *query-id*
  44. :text (format nil "Ошибочка вышла~@[: ~A~]"
  45. (when (is-admin *source-chat-id*) e)))))))))
  46. (defun get-inline-keyboard (buttons &optional (chat-id *chat-id*))
  47. (telegram-inline-keyboard-markup
  48. (loop for row in buttons
  49. when row
  50. collect (loop for (text . callback) in row
  51. when text
  52. collect (list
  53. :text text
  54. :callback-data
  55. (encode-callback-data
  56. chat-id :inline (add-button callback) +ttl+))))))
  57. (def-callback-section-handler cb-handle-inline (:inline)
  58. (let ((button (gethash (parse-integer *data*) *inline-buttons*)))
  59. (if button
  60. (funcall (button-callback button) *callback*)
  61. (progn
  62. (log:error "Can't find button id ~A" *data*)
  63. (telegram-answer-callback-query *query-id* :text "Ошибка")))))