telegram.lisp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. (defparameter +telegram-max-callback-data-length+ 64)
  6. (defvar *telegram-timeout* 30 "Default Telegram timeout")
  7. (defun %telegram-api-call (method &optional args)
  8. (let* ((params (loop for (k . v) in args when v
  9. collect (cons
  10. (princ-to-string k)
  11. (if (pathnamep v) v
  12. (princ-to-string v)))))
  13. (timeout (+ 5 (or (aget "timeout" args)
  14. *telegram-timeout*)))
  15. (response
  16. (handler-case
  17. (bordeaux-threads:with-timeout (timeout)
  18. (json-request (format nil +telegram-api-format+
  19. *telegram-token* method)
  20. :method :post
  21. :parameters params))
  22. (bordeaux-threads:timeout () (error "Timeout")))))
  23. (unless (aget "ok" response)
  24. (error (aget "description" response)))
  25. (aget "result" response)))
  26. (defun telegram-get-updates (&key offset limit timeout)
  27. (%telegram-api-call
  28. "getUpdates"
  29. (list (cons "offset" offset)
  30. (cons "limit" limit)
  31. (cons "timeout" timeout))))
  32. (defun telegram-get-me ()
  33. (%telegram-api-call "getMe"))
  34. (defun telegram-set-webhook (&optional url certificate)
  35. (%telegram-api-call "setWebhook" (list (cons "url" url) (cons "certificate" certificate))))
  36. (defun telegram-send-message (chat-id text &key parse-mode disable-web-preview disable-notification reply-to reply-markup)
  37. (%telegram-api-call
  38. "sendMessage"
  39. (list (cons "chat_id" chat-id)
  40. (cons "text" text)
  41. (cons "parse_mode" parse-mode)
  42. (cons "disable_web_page_preview" disable-web-preview)
  43. (cons "disable_notification" disable-notification)
  44. (cons "reply_to_message_id" reply-to)
  45. (cons "reply_markup" reply-markup))))
  46. (defun telegram-forward-message (chat-id from-chat-id message-id)
  47. (%telegram-api-call
  48. "forwardMessage"
  49. `(("chat_id" . ,chat-id)
  50. ("from_chat_id" . ,from-chat-id)
  51. ("message_id" . ,message-id))))
  52. (defun telegram-send-photo (chat-id photo &key caption reply-to reply-markup)
  53. (%telegram-api-call
  54. "sendPhoto"
  55. (list (cons "chat_id" chat-id)
  56. (cons "photo" photo)
  57. (cons "caption" caption)
  58. (cons "reply_to_message_id" reply-to)
  59. (cons "reply_markup" reply-markup))))
  60. (defun telegram-send-audio (chat-id audio &key duration performer title reply-to reply-markup)
  61. (%telegram-api-call
  62. "sendAudio"
  63. (list (cons "chat_id" chat-id)
  64. (cons "audio" audio)
  65. (cons "duration" duration)
  66. (cons "performer" performer)
  67. (cons "title" title)
  68. (cons "reply_to_message_id" reply-to)
  69. (cons "reply_markup" reply-markup))))
  70. (defun telegram-send-document (chat-id document &key reply-to reply-markup)
  71. (%telegram-api-call
  72. "sendDocument"
  73. (list (cons "chat_id" chat-id)
  74. (cons "document" document)
  75. (cons "reply_to_message_id" reply-to)
  76. (cons "reply_markup" reply-markup))))
  77. (defun telegram-send-sticker (chat-id sticker &key reply-to reply-markup)
  78. (%telegram-api-call
  79. "sendSticker"
  80. (list (cons "chat_id" chat-id)
  81. (cons "sticker" sticker)
  82. (cons "reply_to_message_id" reply-to)
  83. (cons "reply_markup" reply-markup))))
  84. (defun telegram-send-video (chat-id video &key duration caption reply-to reply-markup)
  85. (%telegram-api-call
  86. "sendVideo"
  87. (list (cons "chat_id" chat-id)
  88. (cons "video" video)
  89. (cons "duration" duration)
  90. (cons "caption" caption)
  91. (cons "reply_to_message_id" reply-to)
  92. (cons "reply_markup" reply-markup))))
  93. (defun telegram-send-voice (chat-id voice &key duration reply-to reply-markup)
  94. (%telegram-api-call
  95. "sendVoice"
  96. (list (cons "chat_id" chat-id)
  97. (cons "voice" voice)
  98. (cons "duration" duration)
  99. (cons "reply_to_message_id" reply-to)
  100. (cons "reply_markup" reply-markup))))
  101. (defun telegram-send-location (chat-id latitude longitude &key reply-to reply-markup)
  102. (%telegram-api-call
  103. "sendLocation"
  104. (list (cons "chat_id" chat-id)
  105. (cons "latitude" latitude)
  106. (cons "longitude" longitude)
  107. (cons "reply_to_message_id" reply-to)
  108. (cons "reply_markup" reply-markup))))
  109. (defun telegram-send-chat-action (chat-id action)
  110. (%telegram-api-call
  111. "sendChatAction"
  112. (list (cons "chat_id" chat-id)
  113. (cons "action" action))))
  114. (defun telegram-get-user-profile-photos (user-id &key offset limit)
  115. (%telegram-api-call
  116. "getUserProfilePhotos"
  117. (list (cons "user_id" user-id)
  118. (cons "offset" offset)
  119. (cons "limit" limit))))
  120. (defun telegram-get-file (file-id)
  121. (%telegram-api-call "getFile" (list (cons "file_id" file-id))))
  122. (defun telegram-answer-callback-query (query-id &key text show-alert)
  123. (%telegram-api-call
  124. "answerCallbackQuery"
  125. (list (cons "callback_query_id" query-id)
  126. (cons "text" text)
  127. (cons "show_alert" show-alert))))
  128. (defun telegram-edit-message-text (text &key chat-id message-id inline-message-id parse-mode disable-web-preview reply-markup)
  129. (%telegram-api-call
  130. "editMessageText"
  131. (list (cons "chat_id" chat-id)
  132. (cons "message_id" message-id)
  133. (cons "inline_message_id" inline-message-id)
  134. (cons "text" text)
  135. (cons "parse_mode" parse-mode)
  136. (cons "disable_web_page_preview" disable-web-preview)
  137. (cons "reply_markup" reply-markup))))
  138. (defun telegram-edit-message-caption (caption &key chat-id message-id inline-message-id reply-markup)
  139. (%telegram-api-call
  140. "editMessageCaption"
  141. (list (cons "chat_id" chat-id)
  142. (cons "message_id" message-id)
  143. (cons "inline_message_id" inline-message-id)
  144. (cons "caption" caption)
  145. (cons "reply_markup" reply-markup))))
  146. (defun telegram-edit-message-reply-markup (reply-markup &key chat-id message-id inline-message-id)
  147. (%telegram-api-call
  148. "editMessageReplyMarkup"
  149. (list (cons "chat_id" chat-id)
  150. (cons "message_id" message-id)
  151. (cons "inline_message_id" inline-message-id)
  152. (cons "reply_markup" reply-markup))))
  153. (defun telegram-answer-inline-query (query-id results &key cache-time is-personal next-offset switch-pm-text switch-pm-parameter)
  154. (%telegram-api-call
  155. "answerInlineQuery"
  156. (list (cons "inline_query_id" query-id)
  157. (cons "results" (plist-json results))
  158. (cons "cache_time" cache-time)
  159. (cons "is_personal" is-personal)
  160. (cons "next_offset" next-offset)
  161. (cons "switch_pm_text" switch-pm-text)
  162. (cons "switch_pm_parameter" switch-pm-parameter))))
  163. (defun telegram-file-contents (file-id)
  164. (let* ((file (telegram-get-file file-id))
  165. (file-path (aget "file_path" file))
  166. (file-url (format nil +telegram-file-format+ *telegram-token* file-path)))
  167. (drakma:http-request file-url :force-binary t :decode-content t)))
  168. (defun telegram-inline-keyboard-markup (inline-keyboard)
  169. (when inline-keyboard
  170. (plist-json
  171. (list :inline-keyboard inline-keyboard))))
  172. (defun telegram-reply-keyboard-markup (keyboard &key resize-keyboard one-time-keyboard selective)
  173. (when keyboard
  174. (plist-json
  175. (list :keyboard keyboard
  176. :resize-keyboard resize-keyboard
  177. :one-time-keyboard one-time-keyboard
  178. :selective selective))))
  179. (defun telegram-reply-keyboard-hide (&optional selective)
  180. (plist-json (list :hide-keyboard t :selective selective)))
  181. (defun telegram-force-reply (&optional selective)
  182. (plist-json (list :force-reply t :selective selective)))
  183. ;; Simplified interface
  184. ;;
  185. (defun send-response (chat-id response &optional reply-id)
  186. (if (consp response)
  187. (if (keywordp (car response))
  188. (case (car response)
  189. (:text (telegram-send-message chat-id (cdr response) :reply-to reply-id))
  190. (:voice (telegram-send-voice chat-id (cdr response) :reply-to reply-id))
  191. (:sticker (telegram-send-sticker chat-id (cdr response) :reply-to reply-id)))
  192. (mapc #'(lambda (r) (send-response chat-id r reply-id)) response))
  193. (telegram-send-message chat-id response :reply-to reply-id)))
  194. (defun bot-send-message (chat-id text &key parse-mode disable-web-preview disable-notification reply-to reply-markup)
  195. (handler-case (telegram-send-message chat-id text :parse-mode parse-mode
  196. :disable-web-preview disable-web-preview
  197. :disable-notification disable-notification
  198. :reply-to reply-to
  199. :reply-markup reply-markup)
  200. (error (e)
  201. (log:error e))))