1
0

telegram.lisp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. (in-package #:chatikbot)
  2. (defvar *telegram-token* nil "Telegram bot token")
  3. (defparameter +telegram-api-format+ "https://api.telegram.org/bot~A/~A")
  4. (defparameter +telegram-file-format+ "https://api.telegram.org/file/bot~A/~A")
  5. (defvar *telegram-timeout* 30 "Default Telegram timeout")
  6. (defun %telegram-api-call (method &optional args)
  7. (let* ((params (loop for (k . v) in args when v
  8. collect (cons
  9. (princ-to-string k)
  10. (if (pathnamep v) v
  11. (princ-to-string v)))))
  12. (timeout (+ 5 (or (aget "timeout" args)
  13. *telegram-timeout*)))
  14. (response
  15. (handler-case
  16. (bordeaux-threads:with-timeout (timeout)
  17. (json-request (format nil +telegram-api-format+
  18. *telegram-token* method)
  19. :method :post
  20. :parameters params))
  21. (bordeaux-threads:timeout () (error "Timeout")))))
  22. (unless (aget "ok" response)
  23. (error (aget "description" response)))
  24. (aget "result" response)))
  25. (defun telegram-get-updates (&key offset limit timeout)
  26. (%telegram-api-call
  27. "getUpdates"
  28. (list (cons "offset" offset)
  29. (cons "limit" limit)
  30. (cons "timeout" timeout))))
  31. (defun telegram-get-me ()
  32. (%telegram-api-call "getMe"))
  33. (defun telegram-set-webhook (&optional url certificate)
  34. (%telegram-api-call "setWebhook" (list (cons "url" url) (cons "certificate" certificate))))
  35. (defun telegram-send-message (chat-id text &key parse-mode disable-web-preview reply-to reply-markup)
  36. (%telegram-api-call
  37. "sendMessage"
  38. (list (cons "chat_id" chat-id)
  39. (cons "text" text)
  40. (cons "parse_mode" parse-mode)
  41. (cons "disable_web_page_preview" disable-web-preview)
  42. (cons "reply_to_message_id" reply-to)
  43. (cons "reply_markup" reply-markup))))
  44. (defun telegram-forward-message (chat-id from-chat-id message-id)
  45. (%telegram-api-call
  46. "forwardMessage"
  47. `(("chat_id" . ,chat-id)
  48. ("from_chat_id" . ,from-chat-id)
  49. ("message_id" . ,message-id))))
  50. (defun telegram-send-photo (chat-id photo &key caption reply-to reply-markup)
  51. (%telegram-api-call
  52. "sendPhoto"
  53. (list (cons "chat_id" chat-id)
  54. (cons "photo" photo)
  55. (cons "caption" caption)
  56. (cons "reply_to_message_id" reply-to)
  57. (cons "reply_markup" reply-markup))))
  58. (defun telegram-send-audio (chat-id audio &key duration performer title reply-to reply-markup)
  59. (%telegram-api-call
  60. "sendAudio"
  61. (list (cons "chat_id" chat-id)
  62. (cons "audio" audio)
  63. (cons "duration" duration)
  64. (cons "performer" performer)
  65. (cons "title" title)
  66. (cons "reply_to_message_id" reply-to)
  67. (cons "reply_markup" reply-markup))))
  68. (defun telegram-send-document (chat-id document &key reply-to reply-markup)
  69. (%telegram-api-call
  70. "sendDocument"
  71. (list (cons "chat_id" chat-id)
  72. (cons "document" document)
  73. (cons "reply_to_message_id" reply-to)
  74. (cons "reply_markup" reply-markup))))
  75. (defun telegram-send-sticker (chat-id sticker &key reply-to reply-markup)
  76. (%telegram-api-call
  77. "sendSticker"
  78. (list (cons "chat_id" chat-id)
  79. (cons "sticker" sticker)
  80. (cons "reply_to_message_id" reply-to)
  81. (cons "reply_markup" reply-markup))))
  82. (defun telegram-send-video (chat-id video &key duration caption reply-to reply-markup)
  83. (%telegram-api-call
  84. "sendVideo"
  85. (list (cons "chat_id" chat-id)
  86. (cons "video" video)
  87. (cons "duration" duration)
  88. (cons "caption" caption)
  89. (cons "reply_to_message_id" reply-to)
  90. (cons "reply_markup" reply-markup))))
  91. (defun telegram-send-voice (chat-id voice &key duration reply-to reply-markup)
  92. (%telegram-api-call
  93. "sendVoice"
  94. (list (cons "chat_id" chat-id)
  95. (cons "voice" voice)
  96. (cons "duration" duration)
  97. (cons "reply_to_message_id" reply-to)
  98. (cons "reply_markup" reply-markup))))
  99. (defun telegram-send-location (chat-id latitude longitude &key reply-to reply-markup)
  100. (%telegram-api-call
  101. "sendLocation"
  102. (list (cons "chat_id" chat-id)
  103. (cons "latitude" latitude)
  104. (cons "longitude" longitude)
  105. (cons "reply_to_message_id" reply-to)
  106. (cons "reply_markup" reply-markup))))
  107. (defun telegram-send-chat-action (chat-id action)
  108. (%telegram-api-call
  109. "sendChatAction"
  110. (list (cons "chat_id" chat-id)
  111. (cons "action" action))))
  112. (defun telegram-get-user-profile-photos (user-id &key offset limit)
  113. (%telegram-api-call
  114. "getUserProfilePhotos"
  115. `(("user_id" . ,user-id) ("offset" . ,offset) ("limit" . ,limit))))
  116. (defun telegram-get-file (file-id)
  117. (%telegram-api-call "getFile" `(("file_id" . ,file-id))))
  118. (defun telegram-file-contents (file-id)
  119. (let* ((file (telegram-get-file file-id))
  120. (file-path (aget "file_path" file))
  121. (file-url (format nil +telegram-file-format+ *telegram-token* file-path)))
  122. (drakma:http-request file-url :force-binary t :decode-content t)))
  123. ;; Simplified interface
  124. ;;
  125. (defun send-response (chat-id response &optional reply-id)
  126. (if (consp response)
  127. (if (keywordp (car response))
  128. (case (car response)
  129. (:text (telegram-send-message chat-id (cdr response) :reply-to reply-id))
  130. (:voice (telegram-send-voice chat-id (cdr response) :reply-to reply-id))
  131. (:sticker (telegram-send-sticker chat-id (cdr response) :reply-to reply-id)))
  132. (mapc #'(lambda (r) (send-response chat-id r reply-id)) response))
  133. (telegram-send-message chat-id response :reply-to reply-id)))
  134. (defun bot-send-message (chat-id text &key parse-mode disable-web-preview reply-to reply-markup)
  135. (handler-case (telegram-send-message chat-id text :parse-mode parse-mode
  136. :disable-web-preview disable-web-preview
  137. :reply-to reply-to
  138. :reply-markup reply-markup)
  139. (error (e)
  140. (log:error e))))