user_location_agent.rb 2.7KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. require 'securerandom'
  2. module Agents
  3. class UserLocationAgent < Agent
  4. cannot_be_scheduled!
  5. description do
  6. <<-MD
  7. The UserLocationAgent creates events based on WebHook POSTS that contain a `latitude` and `longitude`. You can use the [POSTLocation](https://github.com/cantino/post_location) or [PostGPS](https://github.com/chriseidhof/PostGPS) iOS app to post your location.
  8. Your POST path will be `https://#{ENV['DOMAIN']}/users/#{user.id}/update_location/:secret` where `:secret` is specified in your options.
  9. If you want to only keep more precise locations, set `max_accuracy` to the upper bound, in meters. The default name for this field is `accuracy`, but you can change this by setting a value for `accuracy_field`.
  10. MD
  11. end
  12. event_description <<-MD
  13. Assuming you're using the iOS application, events look like this:
  14. {
  15. "latitude": "37.12345",
  16. "longitude": "-122.12345",
  17. "timestamp": "123456789.0",
  18. "altitude": "22.0",
  19. "horizontal_accuracy": "5.0",
  20. "vertical_accuracy": "3.0",
  21. "speed": "0.52595",
  22. "course": "72.0703",
  23. "device_token": "..."
  24. }
  25. MD
  26. def working?
  27. event_created_within?(2) && !recent_error_logs?
  28. end
  29. def default_options
  30. {
  31. 'secret' => SecureRandom.hex(7),
  32. 'max_accuracy' => ''
  33. }
  34. end
  35. def validate_options
  36. errors.add(:base, "secret is required and must be longer than 4 characters") unless options['secret'].present? && options['secret'].length > 4
  37. end
  38. def receive(incoming_events)
  39. incoming_events.each do |event|
  40. interpolate_with(event) do
  41. handle_payload event.payload
  42. end
  43. end
  44. end
  45. def receive_web_request(params, method, format)
  46. params = params.symbolize_keys
  47. if method != 'post'
  48. return ['Not Found', 404]
  49. end
  50. if interpolated['secret'] != params[:secret]
  51. return ['Not Authorized', 401]
  52. end
  53. handle_payload params.except(:secret)
  54. return ['ok', 200]
  55. end
  56. private
  57. def handle_payload(payload)
  58. location = Location.new(payload)
  59. if interpolated[:accuracy_field].present?
  60. accuracy_field = interpolated[:accuracy_field]
  61. else
  62. accuracy_field = 'accuracy'
  63. end
  64. if location.present? && (!interpolated[:max_accuracy].present? || !payload[accuracy_field] || payload[accuracy_field] < interpolated[:max_accuracy])
  65. if !payload[accuracy_field]
  66. log "Accuracy field missing; all locations will be kept"
  67. end
  68. create_event payload: payload, location: location
  69. end
  70. end
  71. end
  72. end