telegram.lisp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. (in-package :cl-user)
  2. (defpackage chatikbot.telegram
  3. (:use :cl :chatikbot.utils)
  4. (:export :*telegram-token*
  5. :+telegram-max-callback-data-length+
  6. :telegram-get-updates
  7. :telegram-get-me
  8. :telegram-set-webhook
  9. :telegram-send-message
  10. :telegram-forward-message
  11. :telegram-send-photo
  12. :telegram-send-audio
  13. :telegram-send-document
  14. :telegram-send-sticker
  15. :telegram-send-video
  16. :telegram-send-voice
  17. :telegram-send-video-note
  18. :telegram-send-media-group
  19. :telegram-send-location
  20. :telegram-send-venue
  21. :telegram-edit-message-live-location
  22. :telegram-stop-message-live-location
  23. :telegram-send-contact
  24. :telegram-send-chat-action
  25. :telegram-send-get-user-profile-photos
  26. :telegram-send-get-file
  27. :telegram-answer-callback-query
  28. :telegram-edit-message-text
  29. :telegram-edit-message-caption
  30. :telegram-edit-message-reply-markup
  31. :telegram-delete-message
  32. :telegram-answer-inline-query
  33. :telegram-file-contents
  34. :telegram-inline-keyboard-markup
  35. :telegram-reply-keyboard-markup
  36. :telegram-reply-keyboard-hide
  37. :telegram-force-reply
  38. :bot-send-message))
  39. (in-package :chatikbot.telegram)
  40. (defvar *telegram-token* nil "Telegram bot token")
  41. (defparameter +telegram-api-format+ "https://api.telegram.org/bot~A/~A")
  42. (defparameter +telegram-file-format+ "https://api.telegram.org/file/bot~A/~A")
  43. (defparameter +telegram-max-callback-data-length+ 64)
  44. (defvar *telegram-timeout* 30 "Default Telegram timeout")
  45. (defun %telegram-api-call (method &optional args)
  46. (handler-case
  47. (let* ((params (loop for (k . v) in args when v
  48. collect (cons
  49. (princ-to-string k)
  50. (if (pathnamep v) v
  51. (princ-to-string v)))))
  52. (timeout (+ 5 (or (aget "timeout" args)
  53. *telegram-timeout*)))
  54. (response
  55. (json-request (format nil +telegram-api-format+
  56. *telegram-token* method)
  57. :method :post
  58. :content params
  59. :read-timeout timeout)))
  60. (unless (aget "ok" response)
  61. (error (aget "description" response)))
  62. (aget "result" response))
  63. (dex:http-request-forbidden (e)
  64. (log:info "Forbidden" e))))
  65. (defun telegram-get-updates (&key offset limit timeout)
  66. (%telegram-api-call
  67. "getUpdates"
  68. (list (cons "offset" offset)
  69. (cons "limit" limit)
  70. (cons "read-timeout" timeout))))
  71. (defun telegram-get-me ()
  72. (%telegram-api-call "getMe"))
  73. (defun telegram-set-webhook (&optional url certificate)
  74. (%telegram-api-call "setWebhook" (list (cons "url" url) (cons "certificate" certificate))))
  75. (defun telegram-send-message (chat-id text &key parse-mode disable-web-preview disable-notification reply-to reply-markup)
  76. (%telegram-api-call
  77. "sendMessage"
  78. (list (cons "chat_id" chat-id)
  79. (cons "text" text)
  80. (cons "parse_mode" parse-mode)
  81. (cons "disable_web_page_preview" disable-web-preview)
  82. (cons "disable_notification" disable-notification)
  83. (cons "reply_to_message_id" reply-to)
  84. (cons "reply_markup" reply-markup))))
  85. (defun telegram-forward-message (chat-id from-chat-id message-id)
  86. (%telegram-api-call
  87. "forwardMessage"
  88. `(("chat_id" . ,chat-id)
  89. ("from_chat_id" . ,from-chat-id)
  90. ("message_id" . ,message-id))))
  91. (defun telegram-send-photo (chat-id photo &key caption disable-notification reply-to reply-markup)
  92. (%telegram-api-call
  93. "sendPhoto"
  94. (list (cons "chat_id" chat-id)
  95. (cons "photo" photo)
  96. (cons "caption" caption)
  97. (cons "disable_notification" disable-notification)
  98. (cons "reply_to_message_id" reply-to)
  99. (cons "reply_markup" reply-markup))))
  100. (defun telegram-send-audio (chat-id audio &key caption duration performer title disable-notification reply-to reply-markup)
  101. (%telegram-api-call
  102. "sendAudio"
  103. (list (cons "chat_id" chat-id)
  104. (cons "audio" audio)
  105. (cons "caption" caption)
  106. (cons "duration" duration)
  107. (cons "performer" performer)
  108. (cons "title" title)
  109. (cons "disable_notification" disable-notification)
  110. (cons "reply_to_message_id" reply-to)
  111. (cons "reply_markup" reply-markup))))
  112. (defun telegram-send-document (chat-id document &key caption disable-notification reply-to reply-markup)
  113. (%telegram-api-call
  114. "sendDocument"
  115. (list (cons "chat_id" chat-id)
  116. (cons "document" document)
  117. (cons "caption" caption)
  118. (cons "disable_notification" disable-notification)
  119. (cons "reply_to_message_id" reply-to)
  120. (cons "reply_markup" reply-markup))))
  121. (defun telegram-send-sticker (chat-id sticker &key disable-notification reply-to reply-markup)
  122. (%telegram-api-call
  123. "sendSticker"
  124. (list (cons "chat_id" chat-id)
  125. (cons "sticker" sticker)
  126. (cons "disable_notification" disable-notification)
  127. (cons "reply_to_message_id" reply-to)
  128. (cons "reply_markup" reply-markup))))
  129. (defun telegram-send-video (chat-id video &key duration width height caption disable-notification reply-to reply-markup)
  130. (%telegram-api-call
  131. "sendVideo"
  132. (list (cons "chat_id" chat-id)
  133. (cons "video" video)
  134. (cons "duration" duration)
  135. (cons "width" width)
  136. (cons "height" height)
  137. (cons "caption" caption)
  138. (cons "disable_notification" disable-notification)
  139. (cons "reply_to_message_id" reply-to)
  140. (cons "reply_markup" reply-markup))))
  141. (defun telegram-send-voice (chat-id voice &key caption duration disable-notification reply-to reply-markup)
  142. (%telegram-api-call
  143. "sendVoice"
  144. (list (cons "chat_id" chat-id)
  145. (cons "voice" voice)
  146. (cons "caption" caption)
  147. (cons "duration" duration)
  148. (cons "disable_notification" disable-notification)
  149. (cons "reply_to_message_id" reply-to)
  150. (cons "reply_markup" reply-markup))))
  151. (defun telegram-send-video-note (chat-id video-note &key duration length disable-notification reply-to reply-markup)
  152. (%telegram-api-call
  153. "sendVideoNote"
  154. (list (cons "chat_id" chat-id)
  155. (cons "video_note" video-note)
  156. (cons "duration" duration)
  157. (cons "length" length)
  158. (cons "disable_notification" disable-notification)
  159. (cons "reply_to_message_id" reply-to)
  160. (cons "reply_markup" reply-markup))))
  161. (defun telegram-send-media-group (chat-id media &key disable-notification reply-to)
  162. (%telegram-api-call
  163. "sendMediaGroup"
  164. (list (cons "chat_id" chat-id)
  165. (cons "media" media)
  166. (cons "disable_notification" disable-notification)
  167. (cons "reply_to_message_id" reply-to))))
  168. (defun telegram-send-location (chat-id latitude longitude &key live-period disable-notification reply-to reply-markup)
  169. (%telegram-api-call
  170. "sendLocation"
  171. (list (cons "chat_id" chat-id)
  172. (cons "latitude" latitude)
  173. (cons "longitude" longitude)
  174. (cons "live_period" live-period)
  175. (cons "disable_notification" disable-notification)
  176. (cons "reply_to_message_id" reply-to)
  177. (cons "reply_markup" reply-markup))))
  178. (defun telegram-edit-message-live-location (latitude longitude &key chat-id message-id inline-message-id reply-markup)
  179. (%telegram-api-call
  180. "editMessageLiveLocation"
  181. (list (cons "chat_id" chat-id)
  182. (cons "message_id" message-id)
  183. (cons "inline_message_id" inline-message-id)
  184. (cons "latitude" latitude)
  185. (cons "longitude" longitude)
  186. (cons "reply_markup" reply-markup))))
  187. (defun telegram-stop-message-live-location (&key chat-id message-id inline-message-id reply-markup)
  188. (%telegram-api-call
  189. "stopMessageLiveLocation"
  190. (list (cons "chat_id" chat-id)
  191. (cons "message_id" message-id)
  192. (cons "inline_message_id" inline-message-id)
  193. (cons "reply_markup" reply-markup))))
  194. (defun telegram-send-venue (chat-id latitude longitude title address &key foursquare-id disable-notification reply-to reply-markup)
  195. (%telegram-api-call
  196. "sendVenue"
  197. (list (cons "chat_id" chat-id)
  198. (cons "latitude" latitude)
  199. (cons "longitude" longitude)
  200. (cons "title" title)
  201. (cons "address" address)
  202. (cons "foursquare_id" foursquare-id)
  203. (cons "disable_notification" disable-notification)
  204. (cons "reply_to_message_id" reply-to)
  205. (cons "reply_markup" reply-markup))))
  206. (defun telegram-send-contact (chat-id phone-number first-name &key last-name disable-notification reply-to reply-markup)
  207. (%telegram-api-call
  208. "sendContact"
  209. (list (cons "chat_id" chat-id)
  210. (cons "phone_number" phone-number)
  211. (cons "first_name" first-name)
  212. (cons "last_name" last-name)
  213. (cons "disable_notification" disable-notification)
  214. (cons "reply_to_message_id" reply-to)
  215. (cons "reply_markup" reply-markup))))
  216. (defun telegram-send-chat-action (chat-id action)
  217. (%telegram-api-call
  218. "sendChatAction"
  219. (list (cons "chat_id" chat-id)
  220. (cons "action" action))))
  221. (defun telegram-get-user-profile-photos (user-id &key offset limit)
  222. (%telegram-api-call
  223. "getUserProfilePhotos"
  224. (list (cons "user_id" user-id)
  225. (cons "offset" offset)
  226. (cons "limit" limit))))
  227. (defun telegram-get-file (file-id)
  228. (%telegram-api-call "getFile" (list (cons "file_id" file-id))))
  229. (defun telegram-answer-callback-query (query-id &key text show-alert)
  230. (%telegram-api-call
  231. "answerCallbackQuery"
  232. (list (cons "callback_query_id" query-id)
  233. (cons "text" text)
  234. (cons "show_alert" show-alert))))
  235. (defun telegram-edit-message-text (text &key chat-id message-id inline-message-id parse-mode disable-web-preview reply-markup)
  236. (%telegram-api-call
  237. "editMessageText"
  238. (list (cons "chat_id" chat-id)
  239. (cons "message_id" message-id)
  240. (cons "inline_message_id" inline-message-id)
  241. (cons "text" text)
  242. (cons "parse_mode" parse-mode)
  243. (cons "disable_web_page_preview" disable-web-preview)
  244. (cons "reply_markup" reply-markup))))
  245. (defun telegram-edit-message-caption (caption &key chat-id message-id inline-message-id reply-markup)
  246. (%telegram-api-call
  247. "editMessageCaption"
  248. (list (cons "chat_id" chat-id)
  249. (cons "message_id" message-id)
  250. (cons "inline_message_id" inline-message-id)
  251. (cons "caption" caption)
  252. (cons "reply_markup" reply-markup))))
  253. (defun telegram-edit-message-reply-markup (reply-markup &key chat-id message-id inline-message-id)
  254. (%telegram-api-call
  255. "editMessageReplyMarkup"
  256. (list (cons "chat_id" chat-id)
  257. (cons "message_id" message-id)
  258. (cons "inline_message_id" inline-message-id)
  259. (cons "reply_markup" reply-markup))))
  260. (defun telegram-delete-message (chat-id message-id)
  261. (%telegram-api-call
  262. "deleteMessage" (list (cons "chat_id" chat-id)
  263. (cons "message_id" message-id))))
  264. (defun telegram-answer-inline-query (query-id results &key cache-time is-personal next-offset switch-pm-text switch-pm-parameter)
  265. (%telegram-api-call
  266. "answerInlineQuery"
  267. (list (cons "inline_query_id" query-id)
  268. (cons "results" (plist-json results))
  269. (cons "cache_time" cache-time)
  270. (cons "is_personal" is-personal)
  271. (cons "next_offset" next-offset)
  272. (cons "switch_pm_text" switch-pm-text)
  273. (cons "switch_pm_parameter" switch-pm-parameter))))
  274. (defun telegram-file-contents (file-id)
  275. (let* ((file (telegram-get-file file-id))
  276. (file-path (aget "file_path" file))
  277. (file-url (format nil +telegram-file-format+ *telegram-token* file-path)))
  278. (http-request file-url :force-binary t)))
  279. (defun telegram-inline-keyboard-markup (inline-keyboard)
  280. (when inline-keyboard
  281. (plist-json
  282. (list :inline-keyboard inline-keyboard))))
  283. (defun telegram-reply-keyboard-markup (keyboard &key resize-keyboard one-time-keyboard selective)
  284. (when keyboard
  285. (plist-json
  286. (list :keyboard keyboard
  287. :resize-keyboard resize-keyboard
  288. :one-time-keyboard one-time-keyboard
  289. :selective selective))))
  290. (defun telegram-reply-keyboard-hide (&optional selective)
  291. (plist-json (list :hide-keyboard t :selective selective)))
  292. (defun telegram-force-reply (&optional selective)
  293. (plist-json (list :force-reply t :selective selective)))
  294. ;; Simplified interface
  295. ;;
  296. (defun bot-send-message (message &key (chat-id *chat-id*) parse-mode disable-web-preview disable-notification reply-to reply-markup duration)
  297. (handler-case (if (consp message)
  298. (if (keywordp (car message))
  299. (case (car message)
  300. (:text (telegram-send-message chat-id (cdr message)
  301. :parse-mode parse-mode
  302. :disable-web-preview disable-web-preview
  303. :disable-notification disable-notification
  304. :reply-to reply-to
  305. :reply-markup reply-markup))
  306. (:voice (telegram-send-voice chat-id (cdr message)
  307. :duration duration
  308. :reply-to reply-to
  309. :reply-markup reply-markup))
  310. (:sticker (telegram-send-sticker chat-id (cdr message)
  311. :reply-to reply-to
  312. :reply-markup reply-markup)))
  313. (mapc #'(lambda (m) (bot-send-message m
  314. :chat-id chat-id
  315. :parse-mode parse-mode
  316. :disable-web-preview disable-web-preview
  317. :disable-notification disable-notification
  318. :reply-to reply-to
  319. :reply-markup reply-markup
  320. :duration duration)) message))
  321. (telegram-send-message chat-id message
  322. :parse-mode parse-mode
  323. :disable-web-preview disable-web-preview
  324. :disable-notification disable-notification
  325. :reply-to reply-to
  326. :reply-markup reply-markup))
  327. (error (e)
  328. (log:error e))))