checkins.lisp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. (in-package :cl-user)
  2. (ql:quickload :address-formatting)
  3. (defpackage chatikbot.plugins.checkins
  4. (:use :cl :chatikbot.common :address-formatting))
  5. (in-package :chatikbot.plugins.checkins)
  6. (def-db-init
  7. (db-execute "create table if not exists checkins_chats (from_id, chat_id, primary key (from_id, chat_id)) without rowid")
  8. (db-execute "create table if not exists checkins_history (ts, from_id, lat, lon, place_id, place_name)"))
  9. (defun db/get-chats (from-id)
  10. (mapcar #'car (db-select "select chat_id from checkins_chats where from_id = ?" from-id)))
  11. (defun db/add-chat (from-id chat-id)
  12. (db-execute "insert into checkins_chats (from_id, chat_id) values (?, ?)" from-id chat-id))
  13. (defun db/del-chat (from-id chat-id)
  14. (db-execute "delete from checkins_chats where from_id = ? and chat_id = ?" from-id chat-id))
  15. (defun db/add-checkin (from-id lat lon place-id place-name)
  16. (db-execute "insert into checkins_history (ts, from_id, lat, lon, place_id, place_name) values (?, ?, ?, ?, ?, ?)"
  17. (local-time:timestamp-to-unix (local-time:now))
  18. from-id lat lon place-id place-name))
  19. (defun get-icon (res)
  20. (let ((cat (keyify (aget "category" res)))
  21. (type (keyify (aget "type" res)))
  22. (sport (keyify (aget "sport" res))))
  23. (case sport
  24. (:soccer "⚽️")
  25. (:tennis "🎾")
  26. (:basketball "🏀")
  27. (:baseball "⚾️")
  28. (:multi "🏆")
  29. (:swimming "🏊")
  30. (:golf "🏌️")
  31. (:equestrian "🏇")
  32. (:billiards "🎱")
  33. (:running "🏃")
  34. (:fitnes "🤸")
  35. (:athletics "🏋️")
  36. (:table-tennis "🏓")
  37. (:beachvolleyball "🏐")
  38. (:climbing "🧗")
  39. (:volleyball "🏐")
  40. (:skateboard "🛹")
  41. (:boules "🎳")
  42. (:american-football "🏈")
  43. (:motor "🏎")
  44. (:bowls "🎳")
  45. (:shooting "🎯")
  46. (:cricket "🏏")
  47. (:netball "🥍")
  48. (:horse-racing "🏇")
  49. (:cycling "🚴")
  50. (:motocross "🏍")
  51. (:rugby-union "🏉")
  52. (:skiing "⛷")
  53. (:gymnastics "🤸")
  54. (:karting "🏎")
  55. (:handball "🏉")
  56. (:badminton "🏸")
  57. (:ice-hockey "🏒")
  58. (:field-hockey "🏑")
  59. (t (case cat
  60. (:amenity (case type
  61. (:atm "🏧")
  62. (:bank "🏦")
  63. (:restaurant "🍽")
  64. (:cafe "☕️")
  65. (:fast-food "🌯")
  66. (:pharmacy "💊")
  67. (:kindergarten "🧸")
  68. (:bar "🍸")
  69. (:hospital "🏥")
  70. (:post-office "📯")
  71. (:pub "🍺")
  72. (:library "📚")
  73. ))
  74. (:leisure (case type
  75. (:pitch)))
  76. )))))
  77. (defvar *revgeo-format* "http://osmnames-search/r/~,6f/~,6f.js?count=10")
  78. (defun get-places (lat lon)
  79. (handler-case
  80. (loop for res in (agets (json-request (format nil *revgeo-format* lon lat))
  81. "results")
  82. collect (list (cons :id (agets res "id"))
  83. (cons :name (agets res "name"))
  84. (cons :icon (get-icon res))
  85. (cons :address (format-address res :one-line t))))
  86. (error (e)
  87. (log:error e)
  88. `(((:id . 0) (:name . "Не нашлось"))))))
  89. (defun add-place (name lat lon from-id)
  90. `((:name . ,name) (:id . ,from-id) (:lat . ,lat) (:lon . ,lon)))
  91. (defun get-name (from)
  92. (let ((username (agets from "username"))
  93. (first-name (agets from "first_name"))
  94. (last-name (agets from "last_name")))
  95. (if username
  96. (format nil "@~a" username)
  97. (format nil "~a ~a" first-name last-name))))
  98. (defun checkin (from place lat lon)
  99. (let* ((from-id (agets from "id"))
  100. (place-id (agets place :id))
  101. (place-name (agets place :name))
  102. (place-icon (agets place :icon))
  103. (place-address (agets place :address))
  104. (message (format nil "~a ~@[~A~]~@[ в ~A~]~@[ (~A)~]"
  105. (or place-icon "📍")
  106. (get-name from) place-name place-address)))
  107. (db/add-checkin from-id lat lon place-id place-name)
  108. (loop for chat-id in (db/get-chats from-id)
  109. do (telegram-send-venue chat-id lat lon
  110. (format nil "~a ~@[~A~]~@[ в ~A~]"
  111. (or place-icon "📍")
  112. (get-name from) place-name)
  113. place-address)
  114. ;;do (bot-send-message message :chat-id chat-id :parse-mode "markdown")
  115. )
  116. (telegram-edit-message-reply-markup nil
  117. :chat-id *source-chat-id*
  118. :message-id *source-message-id*)))
  119. (defparameter +button-columns+ 2)
  120. (def-message-handler handle-location (10)
  121. (alexandria:when-let ((location (aget "location" *message*)))
  122. (alexandria:when-let ((chats (db/get-chats *from-id*))
  123. (lat (agets location "latitude"))
  124. (lon (agets location "longitude")))
  125. (log:info "handler-location" *from-id* location)
  126. (let ((places (get-places lat lon)))
  127. (log:info places)
  128. (bot-send-message
  129. (if places "Выбери место" "Не нашлось ничего :(")
  130. :reply-markup
  131. (get-inline-keyboard
  132. (append
  133. (let* ((buttons (loop for place in places
  134. for idx from 0
  135. collect (cons idx (let ((this-place place))
  136. (inline-button ((format nil "~@[~a ~]~a"
  137. (agets place :icon)
  138. (agets place :name)))
  139. (checkin *from* this-place lat lon))))))
  140. (groups (group-by buttons (lambda (p) (truncate (car p) +button-columns+)))))
  141. (loop for (i . pairs) in (reverse groups)
  142. collect (mapcar #'cdr pairs)))
  143. (list (list (inline-button ("+")
  144. (bot-send-message "Введи название")
  145. (let ((source-chat-id *source-chat-id*)
  146. (source-message-id *source-message-id*))
  147. (on-next-message
  148. (let ((*source-chat-id* source-chat-id)
  149. (*source-message-id* source-message-id))
  150. (checkin *from*
  151. (add-place *text* lat lon *from-id*)
  152. lat lon)))))))))))
  153. t)))
  154. (def-message-cmd-handler handler-checkins (:checkins)
  155. (let ((chats (db/get-chats *from-id*)))
  156. (if (member *chat-id* chats)
  157. (progn
  158. (db/del-chat *from-id* *chat-id*)
  159. (bot-send-message (format nil "Больше сюда не шлём чекины ~a" (get-name *from*))))
  160. (progn
  161. (db/add-chat *from-id* *chat-id*)
  162. (bot-send-message (format nil "Теперь шлём сюда чекины ~a" (get-name *from*)))))))
  163. (def-message-cmd-handler handler-checkin (:checkin)
  164. (if (equal "private" (agets *message* "chat" "type"))
  165. (bot-send-message "Отправь локейшн"
  166. :reply-markup (telegram-reply-keyboard-markup
  167. '(((:request-location t :text "Послать координаты")))
  168. :resize-keyboard t
  169. :one-time-keyboard t))
  170. (bot-send-message "Нужно в личку"
  171. :reply-markup (telegram-inline-keyboard-markup
  172. `(((:text "Перейти"
  173. :url ,(format nil "tg://user?id=~a" *bot-user-id*))))))))