google_calendar_publish_agent.rb 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. require 'json'
  2. require 'google/apis/calendar_v3'
  3. module Agents
  4. class GoogleCalendarPublishAgent < Agent
  5. cannot_be_scheduled!
  6. no_bulk_receive!
  7. gem_dependency_check { defined?(Google) && defined?(Google::Apis::CalendarV3) }
  8. description <<~MD
  9. The Google Calendar Publish Agent creates events on your Google Calendar.
  10. #{'## Include `google-api-client` in your Gemfile to use this Agent!' if dependencies_missing?}
  11. This agent relies on service accounts, rather than oauth.
  12. Setup:
  13. 1. Visit [the google api console](https://code.google.com/apis/console/b/0/)
  14. 2. New project -> Huginn
  15. 3. APIs & Auth -> Enable google calendar
  16. 4. Credentials -> Create new Client ID -> Service Account
  17. 5. Download the JSON keyfile and save it to a path, ie: `/home/huginn/Huginn-5d12345678cd.json`. Or open that file and copy the `private_key`.
  18. 6. Grant access via google calendar UI to the service account email address for each calendar you wish to manage. For a whole google apps domain, you can [delegate authority](https://developers.google.com/+/domains/authentication/delegation)
  19. An earlier version of Huginn used PKCS12 key files to authenticate. This will no longer work, you should generate a new JSON format keyfile, that will look something like:
  20. <pre><code>{
  21. "type": "service_account",
  22. "project_id": "huginn-123123",
  23. "private_key_id": "6d6b476fc6ccdb31e0f171991e5528bb396ffbe4",
  24. "private_key": "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n",
  25. "client_email": "huginn-calendar@huginn-123123.iam.gserviceaccount.com",
  26. "client_id": "123123...123123",
  27. "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  28. "token_uri": "https://accounts.google.com/o/oauth2/token",
  29. "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  30. "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/huginn-calendar%40huginn-123123.iam.gserviceaccount.com"
  31. }</code></pre>
  32. Agent Configuration:
  33. `calendar_id` - The id the calendar you want to publish to. Typically your google account email address. Liquid formatting (e.g. `{{ cal_id }}`) is allowed here in order to extract the calendar_id from the incoming event.
  34. `google` A hash of configuration options for the agent.
  35. `google` `service_account_email` - The authorised service account email address.
  36. `google` `key_file` OR `google` `key` - The path to the JSON key file above, or the key itself (the value of `private_key`). [Liquid](https://github.com/huginn/huginn/wiki/Formatting-Events-using-Liquid) formatting is supported if you want to use a Credential. (E.g., `{% credential google_key %}`)
  37. Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
  38. Use it with a trigger agent to shape your payload!
  39. A hash of event details. See the [Google Calendar API docs](https://developers.google.com/google-apps/calendar/v3/reference/events/insert)
  40. The prior version Google's API expected keys like `dateTime` but in the latest version they expect snake case keys like `date_time`.
  41. Example payload for trigger agent:
  42. <pre><code>{
  43. "message": {
  44. "visibility": "default",
  45. "summary": "Awesome event",
  46. "description": "An example event with text. Pro tip: DateTimes are in RFC3339",
  47. "start": {
  48. "date_time": "2017-06-30T17:00:00-05:00"
  49. },
  50. "end": {
  51. "date_time": "2017-06-30T18:00:00-05:00"
  52. }
  53. }
  54. }</code></pre>
  55. MD
  56. event_description <<~MD
  57. Events look like:
  58. {
  59. 'success' => true,
  60. 'published_calendar_event' => {
  61. ....
  62. },
  63. 'agent_id' => 1234,
  64. 'event_id' => 3432
  65. }
  66. MD
  67. def validate_options
  68. errors.add(:base,
  69. "expected_update_period_in_days is required") unless options['expected_update_period_in_days'].present?
  70. end
  71. def working?
  72. event_created_within?(options['expected_update_period_in_days']) && most_recent_event && most_recent_event.payload['success'] == true && !recent_error_logs?
  73. end
  74. def default_options
  75. {
  76. 'expected_update_period_in_days' => "10",
  77. 'calendar_id' => 'you@email.com',
  78. 'google' => {
  79. 'key_file' => '/path/to/private.key',
  80. 'key' => '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n',
  81. 'service_account_email' => ''
  82. }
  83. }
  84. end
  85. def receive(incoming_events)
  86. require 'google_calendar'
  87. incoming_events.each do |event|
  88. GoogleCalendar.open(interpolate_options(options, event), Rails.logger) do |calendar|
  89. cal_message = event.payload["message"]
  90. if cal_message["start"].present? && cal_message["start"]["dateTime"].present? && !cal_message["start"]["date_time"].present?
  91. cal_message["start"]["date_time"] = cal_message["start"].delete "dateTime"
  92. end
  93. if cal_message["end"].present? && cal_message["end"]["dateTime"].present? && !cal_message["end"]["date_time"].present?
  94. cal_message["end"]["date_time"] = cal_message["end"].delete "dateTime"
  95. end
  96. calendar_event = calendar.publish_as(
  97. interpolated(event)['calendar_id'],
  98. cal_message
  99. )
  100. create_event payload: {
  101. 'success' => true,
  102. 'published_calendar_event' => calendar_event,
  103. 'agent_id' => event.agent_id,
  104. 'event_id' => event.id
  105. }
  106. end
  107. end
  108. end
  109. end
  110. end