forecast.lisp 4.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. (in-package :cl-user)
  2. (defpackage chatikbot.plugins.forecast
  3. (:use :cl :chatikbot.common)
  4. (:export :*forecast-api-key*))
  5. (in-package :chatikbot.plugins.forecast)
  6. (defsetting *forecast-api-key* nil "forecast.io APIKEY")
  7. (defparameter +forecast-api-url+ "https://api.darksky.net/forecast" "forecast.io API endpoint")
  8. (defun forecast (lat lon &key time (currently t) minutely hourly daily alerts)
  9. (json-request (format nil
  10. "~A/~A/~A,~A~@[,~A~]?units=si&exclude=~:[currently,~;~]~:[minutely,~;~]~:[hourly,~;~]~:[daily,~;~]~:[alerts,~;~]flags&lang=ru"
  11. +forecast-api-url+ *forecast-api-key* lat lon time
  12. currently minutely hourly daily alerts)
  13. :timeout 5))
  14. (defvar *forecast-point-formats*
  15. '((:current . (:year "-" (:month 2) "-" (:day 2) " " (:hour 2) ":" (:min 2)))
  16. (:hour . ((:hour 2) ":" (:min 2)))
  17. (:day . ((:day 2) " " :short-month " " :short-weekday)))
  18. "local-time:format-timestring formats for different points")
  19. (defvar *forecast-emojis*
  20. '(("clear-day" . "☀")
  21. ("clear-night" . "⭐")
  22. ("rain" . "☔")
  23. ("snow" . "❄")
  24. ("sleet" . "❄☔")
  25. ("wind" . "💨")
  26. ("fog" . "🌁")
  27. ("cloudy" . "☁")
  28. ("partly-cloudy-day" . "⛅")
  29. ("partly-cloudy-night" . "⛅"))
  30. "Weather-to-emoji mapping")
  31. (defun forecast-format-unixtime (unix format tz)
  32. (local-time:format-timestring nil (local-time:unix-to-timestamp unix)
  33. :format format :timezone tz))
  34. (defun forecast-format-point (type point tz)
  35. (format nil "~A -~@[ ~A~]~@[ ~A~]~:[ ~A-~A~;~:* ~A~2*~]°C"
  36. (forecast-format-unixtime (aget "time" point)
  37. (aget type *forecast-point-formats*)
  38. tz)
  39. (aget (aget "icon" point) *forecast-emojis*)
  40. (aget "summary" point)
  41. (aget "temperature" point) (aget "temperatureMin" point) (aget "temperatureMax" point)))
  42. (defun forecast-format-currently (currently &optional (tz local-time:*default-timezone*))
  43. (when currently
  44. (forecast-format-point :current currently tz)))
  45. (defun forecast-format-hourly (hourly &optional (tz local-time:*default-timezone*))
  46. (when hourly
  47. (format nil "~@[~A~]~{~&~A~}"
  48. (aget "summary" hourly)
  49. (mapcar #'(lambda (point) (forecast-format-point :hour point tz))
  50. (subseq (aget "data" hourly) 0 24)))))
  51. (defun forecast-format-daily (daily &optional (tz local-time:*default-timezone*))
  52. (when daily
  53. (format nil "~@[~A~]~{~&~A~}"
  54. (aget "summary" daily)
  55. (mapcar #'(lambda (point) (forecast-format-point :day point tz))
  56. (subseq (aget "data" daily) 0 7)))))
  57. (defun forecast-format (forecast)
  58. (let* ((timezone (local-time:find-timezone-by-location-name
  59. (aget "timezone" forecast))))
  60. (format nil "~@[Сейчас: ~A~]~@[~&Сегодня: ~A~]~@[~&Неделя: ~A~]"
  61. (forecast-format-currently (aget "currently" forecast) timezone)
  62. (forecast-format-hourly (aget "hourly" forecast) timezone)
  63. (forecast-format-daily (aget "daily" forecast) timezone))))
  64. ;;; Hooks
  65. (defvar *chat-locations* nil "ALIST of chat->location")
  66. (def-message-handler handle-location (message)
  67. (let ((chat-id (aget "id" (aget "chat" message)))
  68. (location (aget "location" message)))
  69. (when location
  70. (log:info "handler-location" chat-id location)
  71. (set-setting '*chat-locations* (cons (cons chat-id location) *chat-locations*))
  72. (bot-send-message chat-id "Взял на карандаш")
  73. t)))
  74. (def-message-cmd-handler handle-cmd-weather (:weather :hourly :daily)
  75. (let* ((location (cdr (assoc chat-id *chat-locations*)))
  76. (response (if location
  77. (forecast-format
  78. (forecast
  79. (aget "latitude" location)
  80. (aget "longitude" location)
  81. :hourly (find "hourly" (cons (string cmd) args) :key #'string-downcase :test #'equal)
  82. :daily (find "daily" (cons (string cmd) args) :key #'string-downcase :test #'equal)))
  83. "Так а ты чьих будешь?")))
  84. (bot-send-message chat-id response)))