├── tmp
└── .gitkeep
├── VERSION
├── vendor
├── plugins
│ └── .gitkeep
├── gems
│ └── dotenv-2.0.1
│ │ ├── lib
│ │ ├── dotenv-rails.rb
│ │ └── dotenv
│ │ │ ├── version.rb
│ │ │ ├── tasks.rb
│ │ │ ├── rails-now.rb
│ │ │ ├── environment.rb
│ │ │ ├── cli.rb
│ │ │ └── substitutions
│ │ │ └── variable.rb
│ │ ├── bin
│ │ └── dotenv
│ │ ├── dotenv-rails.gemspec
│ │ ├── dotenv.gemspec
│ │ └── LICENSE
└── assets
│ ├── images
│ ├── json-editor
│ │ ├── add.png
│ │ └── delete.png
│ ├── glyphicons-halflings.png
│ └── glyphicons-halflings-white.png
│ ├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
│ ├── javascripts
│ ├── bootstrap.js
│ └── jquery.serializeObject.js
│ └── stylesheets
│ ├── bootstrap
│ ├── _wells.scss
│ ├── _component-animations.scss
│ ├── _breadcrumbs.scss
│ ├── _close.scss
│ ├── _thumbnails.scss
│ ├── _utilities.scss
│ ├── _jumbotron.scss
│ ├── _media.scss
│ ├── _pager.scss
│ └── bootstrap.scss
│ └── jquery.json-editor.css
├── doc
├── configuration
│ ├── dotenv.md
│ └── procfile.md
├── imgs
│ ├── peaks.png
│ ├── diagram.png
│ ├── the-name.png
│ ├── new-agent.png
│ ├── my-locations.png
│ └── your-agents.png
├── manual
│ └── README.md
├── heroku
│ └── update.md
├── README.md
└── deployment
│ └── unicorn
│ └── production.rb
├── .openshift
├── cron
│ ├── daily
│ │ └── .gitignore
│ ├── hourly
│ │ └── .gitignore
│ ├── minutely
│ │ └── .gitignore
│ ├── monthly
│ │ └── .gitignore
│ ├── weekly
│ │ ├── chrono.dat
│ │ ├── chronograph
│ │ ├── jobs.deny
│ │ ├── jobs.allow
│ │ └── README
│ └── README.cron
├── action_hooks
│ ├── post_deploy
│ ├── build
│ ├── post_stop_ruby-2.0
│ ├── pre_stop_ruby-2.0
│ ├── post_start_ruby-2.0
│ ├── pre_start_ruby-2.0
│ ├── pre_build
│ └── deploy
└── markers
│ └── README
├── app
├── helpers
│ ├── logs_helper.rb
│ ├── markdown_helper.rb
│ ├── users_helper.rb
│ └── scenario_helper.rb
├── assets
│ ├── stylesheets
│ │ ├── jobs.css.scss
│ │ ├── scenarios.css.scss
│ │ ├── diagram.css.scss
│ │ └── tables.css.scss
│ ├── images
│ │ ├── odin.jpg
│ │ └── spinner-arrows.gif
│ └── javascripts
│ │ ├── ace.js.coffee
│ │ ├── application.js.coffee
│ │ ├── pages
│ │ ├── scenario-form-page.js.coffee
│ │ ├── scenario-show-page.js.coffee
│ │ └── user-credential-page.js.coffee
│ │ ├── components
│ │ ├── json-editor.js.coffee.erb
│ │ ├── search.js.coffee
│ │ └── core.js.coffee
│ │ ├── diagram.js.coffee
│ │ └── map_marker.js.coffee
├── views
│ ├── home
│ │ ├── index.html.erb
│ │ ├── about.html.erb
│ │ └── _signed_out_index.html.erb
│ ├── admin
│ │ └── users
│ │ │ ├── edit.html.erb
│ │ │ ├── new.html.erb
│ │ │ └── _form.html.erb
│ ├── devise
│ │ ├── mailer
│ │ │ ├── confirmation_instructions.html.erb
│ │ │ ├── unlock_instructions.html.erb
│ │ │ └── reset_password_instructions.html.erb
│ │ ├── confirmations
│ │ │ └── new.html.erb
│ │ ├── shared
│ │ │ └── _links.html.erb
│ │ ├── unlocks
│ │ │ └── new.html.erb
│ │ ├── passwords
│ │ │ └── new.html.erb
│ │ └── registrations
│ │ │ └── _common_registration_fields.html.erb
│ ├── system_mailer
│ │ ├── send_message.text.erb
│ │ └── send_message.html.erb
│ ├── agents
│ │ ├── _mini_action_menu.html.erb
│ │ ├── agent_views
│ │ │ ├── webhook_agent
│ │ │ │ └── _show.html.erb
│ │ │ └── data_output_agent
│ │ │ │ └── _show.html.erb
│ │ ├── _oauth_dropdown.html.erb
│ │ ├── new.html.erb
│ │ ├── dry_runs
│ │ │ ├── index.html.erb
│ │ │ └── create.html.erb
│ │ ├── edit.html.erb
│ │ └── index.html.erb
│ ├── layouts
│ │ └── _messages.html.erb
│ ├── scenarios
│ │ ├── edit.html.erb
│ │ ├── new.html.erb
│ │ └── _enable_agents_modal.html.erb
│ ├── user_credentials
│ │ ├── edit.html.erb
│ │ └── new.html.erb
│ └── scenario_imports
│ │ ├── _step_one.html.erb
│ │ └── new.html.erb
├── jobs
│ ├── agent_cleanup_expired_job.rb
│ ├── agent_run_schedule_job.rb
│ ├── agent_check_job.rb
│ ├── agent_receive_job.rb
│ └── agent_propagate_job.rb
├── models
│ ├── scenario_membership.rb
│ ├── control_link.rb
│ ├── link.rb
│ ├── contact.rb
│ ├── user_credential.rb
│ └── agents
│ │ └── read_file_agent.rb
├── controllers
│ ├── home_controller.rb
│ ├── users
│ │ └── registrations_controller.rb
│ ├── logs_controller.rb
│ ├── diagrams_controller.rb
│ ├── scenario_imports_controller.rb
│ ├── omniauth_callbacks_controller.rb
│ ├── services_controller.rb
│ ├── worker_status_controller.rb
│ └── events_controller.rb
├── concerns
│ ├── has_guid.rb
│ ├── inheritance_tracking.rb
│ ├── working_helpers.rb
│ ├── oauthable.rb
│ ├── tumblr_concern.rb
│ ├── weibo_concern.rb
│ ├── dropbox_concern.rb
│ └── markdown_class_attributes.rb
├── validators
│ └── owned_by_validator.rb
├── importers
│ └── default_scenario_importer.rb
└── mailers
│ └── system_mailer.rb
├── config
├── initializers
│ ├── requires.rb
│ ├── ar_mysql_column_charset.rb
│ ├── timezone.rb
│ ├── session_store.rb
│ ├── mime_types.rb
│ ├── aws.rb
│ ├── sanitizer.rb
│ ├── filter_parameter_logging.rb
│ ├── action_mailer.rb
│ ├── liquid.rb
│ ├── secret_token.rb
│ ├── silence_worker_status_logger.rb
│ ├── backtrace_silencers.rb
│ ├── wrap_parameters.rb
│ ├── mysqlpls.rb
│ ├── inflections.rb
│ ├── assets.rb
│ └── delayed_job.rb
├── deploy
│ └── production.rb
├── boot.rb
├── environment.rb
├── unicorn.rb.example
└── locales
│ └── en.yml
├── docker
├── multi-process
│ ├── Makefile
│ ├── scripts
│ │ └── standalone-packages
│ ├── Dockerfile
│ └── docker-compose.yml
├── single-process
│ ├── environment.yml
│ ├── Dockerfile
│ └── docker-compose.yml
└── README.md
├── lib
├── tasks
│ ├── ar_mysql_column_charset.rake
│ ├── database_test.rake
│ └── rspec.rake
├── delayed_job_worker.rb
├── ar_mysql_column_charset.rb
├── json_with_indifferent_access.rb
├── rdbms_functions.rb
└── time_tracker.rb
├── .graphviz
├── public
├── favicon.ico
├── android-chrome-36x36.png
├── android-chrome-48x48.png
├── android-chrome-72x72.png
├── android-chrome-96x96.png
├── android-chrome-144x144.png
├── android-chrome-192x192.png
├── apple-touch-icon-57x57.png
├── apple-touch-icon-60x60.png
├── apple-touch-icon-72x72.png
├── apple-touch-icon-76x76.png
├── apple-touch-icon-114x114.png
├── apple-touch-icon-120x120.png
├── apple-touch-icon-144x144.png
├── apple-touch-icon-152x152.png
├── apple-touch-icon-180x180.png
├── robots.txt
├── 500.html
├── 422.html
├── 404.html
└── manifest.json
├── media
├── huginn-logo.png
├── iOS
│ ├── Icon-40.png
│ ├── Icon-76.png
│ ├── Icon-40@2x.png
│ ├── Icon-60@2x.png
│ ├── Icon-60@3x.png
│ ├── Icon-76@2x.png
│ ├── Icon-Small.png
│ ├── Icon-Small@2x.png
│ ├── Icon-Small@3x.png
│ ├── iTunesArtwork.png
│ └── iTunesArtwork@2x.png
├── huginn-icon-16.png
└── huginn-icon-64.png
├── spec
├── data_fixtures
│ ├── private.key
│ ├── stubhub_data.json
│ ├── adioso_parse.json
│ ├── witai.json
│ ├── imap2.eml
│ ├── imap1.eml
│ ├── basecamp.json
│ ├── urlTest.html
│ └── services
│ │ └── 37signals.json
├── fixtures
│ ├── links.yml
│ ├── events.yml
│ ├── users.yml
│ ├── services.yml
│ ├── user_credentials.yml
│ ├── agent_logs.yml
│ ├── scenarios.yml
│ ├── scenario_memberships.yml
│ ├── user_credentials.json
│ └── multiple_user_credentials.json
├── support
│ ├── vcr_support.rb
│ ├── shared_examples
│ │ ├── has_guid.rb
│ │ └── file_handling_consumer.rb
│ └── spec_helpers.rb
├── helpers
│ ├── markdown_helper_spec.rb
│ ├── jobs_helper_spec.rb
│ └── scenario_helper_spec.rb
├── concerns
│ ├── inheritance_tracking_spec.rb
│ └── liquid_droppable_spec.rb
├── env.test
├── features
│ ├── edit_an_agent_spec.rb
│ └── toggle_visibility_of_disabled_agents.rb
├── models
│ ├── agents
│ │ ├── weather_agent_spec.rb
│ │ ├── pdf_agent_spec.rb
│ │ ├── weibo_user_agent_spec.rb
│ │ └── commander_agent_spec.rb
│ ├── concerns
│ │ └── oauthable.rb
│ └── user_credential_spec.rb
├── lib
│ ├── time_tracker_spec.rb
│ └── delayed_job_worker_spec.rb
├── jobs
│ └── agent_propagate_job_spec.rb
├── db
│ └── seeds
│ │ └── admin_and_default_scenario_spec.rb
├── controllers
│ ├── admin
│ │ └── users_controller_spec.rb
│ ├── scenario_imports_controller_spec.rb
│ ├── omniauth_callbacks_controller_spec.rb
│ ├── users
│ │ └── registrations_controller_spec.rb
│ └── services_controller_spec.rb
└── capybara_helper.rb
├── Procfile.CF
├── bin
├── bundle
├── rspec
├── rake
├── rails
├── threaded.rb
├── schedule.rb
├── pre_runner_boot.rb
├── agent_runner.rb
├── twitter_stream.rb
├── decrypt_backup.rb
└── spring
├── db
├── migrate
│ ├── 20130124050117_add_indexes.rb
│ ├── 20121231170705_add_memory_to_agents.rb
│ ├── 20160419150930_add_icon_to_scenarios.rb
│ ├── 20140525150040_add_service_id_to_agents.rb
│ ├── 20140809211540_remove_service_index_on_user_id.rb
│ ├── 20120728215449_add_admin_to_users.rb
│ ├── 20130509053743_add_last_webhook_at_to_agents.rb
│ ├── 20130107050049_add_invitation_code_to_users.rb
│ ├── 20140403043556_add_disabled_to_agent.rb
│ ├── 20131105063248_add_expires_at_to_events.rb
│ ├── 20131222211558_add_keep_events_for_to_agents.rb
│ ├── 20140210062747_add_mode_to_user_credentials.rb
│ ├── 20160302095413_add_deactivated_at_to_users.rb
│ ├── 20140820003139_add_tag_color_to_scenarios.rb
│ ├── 20140811200922_add_uid_column_to_services.rb
│ ├── 20160307084729_add_deactivated_to_agents.rb
│ ├── 20150808115436_remove_requirement_from_users_invitation_code.rb
│ ├── 20140602014917_add_indices_to_scenarios.rb
│ ├── 20140408150825_rename_webhook_to_web_request.rb
│ ├── 20121222074732_create_links.rb
│ ├── 20140216201250_add_propagate_immediately_to_agent.rb
│ ├── 20140509170443_create_scenario_memberships.rb
│ ├── 20140531232016_add_fields_to_scenarios.rb
│ ├── 20140509170420_create_scenarios.rb
│ ├── 20140906030139_set_events_count_default.rb
│ ├── 20140605032822_add_guid_to_agents.rb
│ ├── 20130819160603_create_agent_logs.rb
│ ├── 20150507153436_update_keep_events_for_to_be_in_seconds.rb
│ ├── 20140901143732_add_control_links.rb
│ ├── 20140121075418_create_user_credentials.rb
│ ├── 20160224120316_add_mode_option_to_ftpsite_agents.rb
│ ├── 20160405072512_post_agent_set_event_header_style.rb
│ ├── 20121216025930_create_events.rb
│ ├── 20120919061122_enable_lockable_strategy_for_devise.rb
│ ├── 20120919063304_add_username_to_users.rb
│ ├── 20140127164931_change_handler_to_medium_text.rb
│ ├── 20160423163416_add_xml_namespace_option_to_data_output_agents.rb
│ ├── 20150219213604_add_type_option_attribute_to_pushbullet_agents.rb
│ ├── 20140515211100_create_services.rb
│ ├── 20121220053905_create_agents.rb
│ ├── 20160301113717_add_confirmable_attributes_to_users.rb
│ ├── 20131227000021_add_cached_dates_to_agent.rb
│ ├── 20130126080736_change_memory_to_long_text.rb
│ ├── 20140722131220_convert_efa_skip_agent.rb
│ ├── 20140730005210_convert_efa_skip_created_at.rb
│ ├── 20140603104211_rename_digest_email_to_email_digest.rb
│ ├── 20160307085545_warn_about_duplicate_usernames.rb
│ ├── 20140213053001_add_event_id_at_creation_to_links.rb
│ ├── 20140723110551_adopt_xpath_in_website_agent.rb
│ └── 20160108221620_website_agent_does_not_use_event_url.rb
├── seeds.rb
└── seeds
│ └── seeder.rb
├── config.ru
├── .buildpacks
├── script
├── delayed_job
└── rails
├── Capfile
├── Rakefile
├── deployment
├── heroku
│ └── Procfile.heroku
└── logrotate
│ └── huginn
├── manifest.yml.sample
├── nitrous.json
├── .gitignore
├── .dockerignore
├── app.json
├── LICENSE
└── Guardfile
/tmp/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 0.3
2 |
--------------------------------------------------------------------------------
/vendor/plugins/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/configuration/dotenv.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.openshift/cron/daily/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.openshift/cron/hourly/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/configuration/procfile.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.openshift/cron/minutely/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.openshift/cron/monthly/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/helpers/logs_helper.rb:
--------------------------------------------------------------------------------
1 | module LogsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/config/initializers/requires.rb:
--------------------------------------------------------------------------------
1 | require 'pp'
2 | HuginnAgent.require!
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv-rails.rb:
--------------------------------------------------------------------------------
1 | require "dotenv/rails"
2 |
--------------------------------------------------------------------------------
/docker/multi-process/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | docker build -t cantino/huginn .
3 |
--------------------------------------------------------------------------------
/lib/tasks/ar_mysql_column_charset.rake:
--------------------------------------------------------------------------------
1 | require 'ar_mysql_column_charset'
2 |
--------------------------------------------------------------------------------
/.graphviz:
--------------------------------------------------------------------------------
1 | http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.38.0.tar.gz
2 |
--------------------------------------------------------------------------------
/.openshift/cron/weekly/chrono.dat:
--------------------------------------------------------------------------------
1 | Time And Relative D...n In Execution (Open)Shift!
2 |
--------------------------------------------------------------------------------
/config/initializers/ar_mysql_column_charset.rb:
--------------------------------------------------------------------------------
1 | require 'ar_mysql_column_charset'
2 |
--------------------------------------------------------------------------------
/doc/imgs/peaks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/peaks.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/favicon.ico
--------------------------------------------------------------------------------
/app/assets/stylesheets/jobs.css.scss:
--------------------------------------------------------------------------------
1 | .big-modal-dialog {
2 | width: 90% !important;
3 | }
--------------------------------------------------------------------------------
/doc/imgs/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/diagram.png
--------------------------------------------------------------------------------
/doc/imgs/the-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/the-name.png
--------------------------------------------------------------------------------
/media/huginn-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/huginn-logo.png
--------------------------------------------------------------------------------
/media/iOS/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-40.png
--------------------------------------------------------------------------------
/media/iOS/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-76.png
--------------------------------------------------------------------------------
/doc/imgs/new-agent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/new-agent.png
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/version.rb:
--------------------------------------------------------------------------------
1 | module Dotenv
2 | VERSION = "2.0.1"
3 | end
4 |
--------------------------------------------------------------------------------
/app/assets/images/odin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/app/assets/images/odin.jpg
--------------------------------------------------------------------------------
/doc/imgs/my-locations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/my-locations.png
--------------------------------------------------------------------------------
/doc/imgs/your-agents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/doc/imgs/your-agents.png
--------------------------------------------------------------------------------
/media/huginn-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/huginn-icon-16.png
--------------------------------------------------------------------------------
/media/huginn-icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/huginn-icon-64.png
--------------------------------------------------------------------------------
/media/iOS/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-40@2x.png
--------------------------------------------------------------------------------
/media/iOS/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-60@2x.png
--------------------------------------------------------------------------------
/media/iOS/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-60@3x.png
--------------------------------------------------------------------------------
/media/iOS/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-76@2x.png
--------------------------------------------------------------------------------
/media/iOS/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-Small.png
--------------------------------------------------------------------------------
/media/iOS/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-Small@2x.png
--------------------------------------------------------------------------------
/media/iOS/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/Icon-Small@3x.png
--------------------------------------------------------------------------------
/media/iOS/iTunesArtwork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/iTunesArtwork.png
--------------------------------------------------------------------------------
/.openshift/cron/weekly/chronograph:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "`date`: `cat $(dirname \"$0\")/chrono.dat`"
4 |
--------------------------------------------------------------------------------
/media/iOS/iTunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/media/iOS/iTunesArtwork@2x.png
--------------------------------------------------------------------------------
/public/android-chrome-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-36x36.png
--------------------------------------------------------------------------------
/public/android-chrome-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-48x48.png
--------------------------------------------------------------------------------
/public/android-chrome-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-72x72.png
--------------------------------------------------------------------------------
/public/android-chrome-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-96x96.png
--------------------------------------------------------------------------------
/spec/data_fixtures/private.key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/spec/data_fixtures/private.key
--------------------------------------------------------------------------------
/public/android-chrome-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-144x144.png
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-57x57.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/app/assets/images/spinner-arrows.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/app/assets/images/spinner-arrows.gif
--------------------------------------------------------------------------------
/public/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-144x144.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/public/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/bin/dotenv:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "dotenv/cli"
4 | Dotenv::CLI.new(ARGV).run
5 |
--------------------------------------------------------------------------------
/vendor/assets/images/json-editor/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/images/json-editor/add.png
--------------------------------------------------------------------------------
/Procfile.CF:
--------------------------------------------------------------------------------
1 | web: bundle exec rake db:migrate && bundle exec rails server -p $PORT
2 | jobs: bundle exec rails runner bin/threaded.rb
3 |
--------------------------------------------------------------------------------
/config/deploy/production.rb:
--------------------------------------------------------------------------------
1 | server ENV['CAPISTRANO_DEPLOY_SERVER'], user: ENV['CAPISTRANO_DEPLOY_USER'] || 'huginn', roles: %w{app db web}
--------------------------------------------------------------------------------
/vendor/assets/images/json-editor/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/images/json-editor/delete.png
--------------------------------------------------------------------------------
/vendor/assets/images/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/images/glyphicons-halflings.png
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/vendor/assets/images/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/images/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/vendor/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/vendor/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/vendor/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simple/huginn/master/vendor/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | <% if user_signed_in? %>
2 | <%= render "signed_in_index" %>
3 | <% else %>
4 | <%= render "signed_out_index" %>
5 | <% end %>
--------------------------------------------------------------------------------
/app/assets/javascripts/ace.js.coffee:
--------------------------------------------------------------------------------
1 | #= require ace/ace
2 | #= require ace/mode-javascript.js
3 | #= require ace/mode-markdown.js
4 | #= require ace/mode-coffee.js
5 |
--------------------------------------------------------------------------------
/config/initializers/timezone.rb:
--------------------------------------------------------------------------------
1 | # Set the timezone for the JavascriptAgent (libv8 only relies on the TZ variable)
2 | ENV['TZ'] = Time.zone.tzinfo.canonical_identifier
3 |
--------------------------------------------------------------------------------
/db/migrate/20130124050117_add_indexes.rb:
--------------------------------------------------------------------------------
1 | class AddIndexes < ActiveRecord::Migration
2 | def change
3 | add_index :links, [:receiver_id, :source_id]
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_rails_session'
--------------------------------------------------------------------------------
/db/migrate/20121231170705_add_memory_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddMemoryToAgents < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :memory, :text
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/helpers/markdown_helper.rb:
--------------------------------------------------------------------------------
1 | module MarkdownHelper
2 |
3 | def markdown(text)
4 | Kramdown::Document.new(text, :auto_ids => false).to_html.html_safe
5 | end
6 |
7 | end
8 |
--------------------------------------------------------------------------------
/app/jobs/agent_cleanup_expired_job.rb:
--------------------------------------------------------------------------------
1 | class AgentCleanupExpiredJob < ActiveJob::Base
2 | queue_as :default
3 |
4 | def perform
5 | Event.cleanup_expired!
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/jobs/agent_run_schedule_job.rb:
--------------------------------------------------------------------------------
1 | class AgentRunScheduleJob < ActiveJob::Base
2 | queue_as :default
3 |
4 | def perform(time)
5 | Agent.run_schedule(time)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 |
5 | run Huginn::Application
6 |
--------------------------------------------------------------------------------
/db/migrate/20160419150930_add_icon_to_scenarios.rb:
--------------------------------------------------------------------------------
1 | class AddIconToScenarios < ActiveRecord::Migration
2 | def change
3 | add_column :scenarios, :icon, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/.buildpacks:
--------------------------------------------------------------------------------
1 | https://github.com/cantino/heroku-selectable-procfile.git
2 | https://github.com/heroku/heroku-buildpack-ruby.git
3 | https://github.com/weibeld/heroku-buildpack-graphviz.git
4 |
--------------------------------------------------------------------------------
/.openshift/cron/weekly/jobs.deny:
--------------------------------------------------------------------------------
1 | #
2 | # Any script or job files listed in here (one entry per line) will NOT be
3 | # executed (read as ignored by run-parts).
4 | #
5 |
6 | README
7 |
8 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path("../spring", __FILE__)
4 | rescue LoadError
5 | end
6 | require 'bundler/setup'
7 | load Gem.bin_path('rspec-core', 'rspec')
8 |
--------------------------------------------------------------------------------
/db/migrate/20140525150040_add_service_id_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddServiceIdToAgents < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :service_id, :integer
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/tasks.rb:
--------------------------------------------------------------------------------
1 | desc "Load environment settings from .env"
2 | task :dotenv do
3 | require "dotenv"
4 | Dotenv.load
5 | end
6 |
7 | task :environment => :dotenv
8 |
--------------------------------------------------------------------------------
/db/migrate/20140809211540_remove_service_index_on_user_id.rb:
--------------------------------------------------------------------------------
1 | class RemoveServiceIndexOnUserId < ActiveRecord::Migration
2 | def change
3 | remove_index :services, :user_id
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path("../spring", __FILE__)
4 | rescue LoadError
5 | end
6 | require_relative '../config/boot'
7 | require 'rake'
8 | Rake.application.run
9 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/db/migrate/20120728215449_add_admin_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddAdminToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :admin, :boolean, :default => false, :null => false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20130509053743_add_last_webhook_at_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddLastWebhookAtToAgents < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :last_webhook_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/script/delayed_job:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
4 | require 'delayed/command'
5 | Delayed::Command.new(ARGV).daemonize
6 |
--------------------------------------------------------------------------------
/app/models/scenario_membership.rb:
--------------------------------------------------------------------------------
1 | class ScenarioMembership < ActiveRecord::Base
2 | belongs_to :agent, :inverse_of => :scenario_memberships
3 | belongs_to :scenario, :inverse_of => :scenario_memberships
4 | end
5 |
--------------------------------------------------------------------------------
/db/migrate/20130107050049_add_invitation_code_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddInvitationCodeToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :invitation_code, :string, :null => false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140403043556_add_disabled_to_agent.rb:
--------------------------------------------------------------------------------
1 | class AddDisabledToAgent < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :disabled, :boolean, :default => false, :null => false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | # Set up gems listed in the Gemfile.
4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5 |
6 | require 'bundler/setup' # Set up gems listed in the Gemfile.
7 |
--------------------------------------------------------------------------------
/config/initializers/aws.rb:
--------------------------------------------------------------------------------
1 | if defined?(RTurk) && !Rails.env.test?
2 | RTurk::logger.level = Logger::DEBUG
3 | RTurk.setup(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_ACCESS_KEY'], :sandbox => ENV['AWS_SANDBOX'] == "true")
4 | end
5 |
--------------------------------------------------------------------------------
/config/initializers/sanitizer.rb:
--------------------------------------------------------------------------------
1 | ActionView::Base.sanitized_allowed_tags += Set.new(%w(style table thead tbody tr th td))
2 | ActionView::Base.sanitized_allowed_attributes += Set.new(%w(border cellspacing cellpadding valign))
--------------------------------------------------------------------------------
/app/views/home/about.html.erb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/db/migrate/20131105063248_add_expires_at_to_events.rb:
--------------------------------------------------------------------------------
1 | class AddExpiresAtToEvents < ActiveRecord::Migration
2 | def change
3 | add_column :events, :expires_at, :datetime
4 | add_index :events, :expires_at
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/spec/fixtures/links.yml:
--------------------------------------------------------------------------------
1 | jane_rain_notifier_agent_link:
2 | receiver: jane_rain_notifier_agent
3 | source: jane_weather_agent
4 |
5 | bob_rain_notifier_agent_link:
6 | receiver: bob_rain_notifier_agent
7 | source: bob_weather_agent
--------------------------------------------------------------------------------
/app/views/admin/users/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Edit User
5 |
6 | <%= render partial: 'form' %>
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/db/migrate/20131222211558_add_keep_events_for_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddKeepEventsForToAgents < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :keep_events_for, :integer, :null => false, :default => 0
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140210062747_add_mode_to_user_credentials.rb:
--------------------------------------------------------------------------------
1 | class AddModeToUserCredentials < ActiveRecord::Migration
2 | def change
3 | add_column :user_credentials, :mode, :string, :default => 'text', :null => false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-Agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/app/views/admin/users/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Create new User
5 |
6 | <%= render partial: 'form' %>
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
--------------------------------------------------------------------------------
/db/migrate/20160302095413_add_deactivated_at_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddDeactivatedAtToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :deactivated_at, :datetime
4 |
5 | add_index :users, :deactivated_at
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20140820003139_add_tag_color_to_scenarios.rb:
--------------------------------------------------------------------------------
1 | class AddTagColorToScenarios < ActiveRecord::Migration
2 | def change
3 | add_column :scenarios, :tag_bg_color, :string
4 | add_column :scenarios, :tag_fg_color, :string
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/Capfile:
--------------------------------------------------------------------------------
1 | # Load DSL and set up stages
2 | require 'capistrano/setup'
3 | # Include default deployment tasks
4 | require 'capistrano/deploy'
5 |
6 | require 'capistrano/bundler'
7 | require 'capistrano/rails/assets'
8 | require 'capistrano/rails/migrations'
9 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path("../spring", __FILE__)
4 | rescue LoadError
5 | end
6 | APP_PATH = File.expand_path('../../config/application', __FILE__)
7 | require_relative '../config/boot'
8 | require 'rails/commands'
9 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/post_deploy:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This is a simple post deploy hook executed after your application
3 | # is deployed and started. This script gets executed directly, so
4 | # it could be python, php, ruby, etc.
5 |
6 | echo "-> Post deploy step"
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | skip_before_action :authenticate_user!
3 |
4 | before_action :upgrade_warning, only: :index
5 |
6 | def index
7 | end
8 |
9 | def about
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20140811200922_add_uid_column_to_services.rb:
--------------------------------------------------------------------------------
1 | class AddUidColumnToServices < ActiveRecord::Migration
2 | def change
3 | add_column :services, :uid, :string
4 | add_index :services, :uid
5 | add_index :services, :provider
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160307084729_add_deactivated_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddDeactivatedToAgents < ActiveRecord::Migration
2 | def change
3 | add_column :agents, :deactivated, :boolean, default: false
4 | add_index :agents, [:disabled, :deactivated]
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/concerns/has_guid.rb:
--------------------------------------------------------------------------------
1 | module HasGuid
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | before_save :make_guid
6 | end
7 |
8 | protected
9 |
10 | def make_guid
11 | self.guid = SecureRandom.hex unless guid.present?
12 | end
13 | end
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Welcome <%= @email %>!
2 |
3 | You can confirm your account email through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 |
4 | require_relative 'seeds/seeder'
5 |
6 | Seeder.seed
7 |
--------------------------------------------------------------------------------
/db/migrate/20150808115436_remove_requirement_from_users_invitation_code.rb:
--------------------------------------------------------------------------------
1 | class RemoveRequirementFromUsersInvitationCode < ActiveRecord::Migration
2 | def change
3 | change_column_null :users, :invitation_code, true, ENV['INVITATION_CODE'].presence || 'try-huginn'
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/support/vcr_support.rb:
--------------------------------------------------------------------------------
1 | require 'vcr'
2 |
3 | VCR.configure do |c|
4 | c.cassette_library_dir = 'spec/cassettes'
5 | c.allow_http_connections_when_no_cassette = false
6 | c.hook_into :webmock
7 | c.default_cassette_options = { record: :new_episodes}
8 | c.configure_rspec_metadata!
9 | end
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | Huginn::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/deployment/heroku/Procfile.heroku:
--------------------------------------------------------------------------------
1 | # This Procfile is intended for Heroku, and is detected by the Gemfile. DO NOT REMOVE THIS LINE!
2 |
3 | # deployment/heroku/unicorn.rb is a special Unicorn config file that also spawns workers.
4 | web: bundle exec unicorn -p $PORT -c ./deployment/heroku/unicorn.rb
5 |
--------------------------------------------------------------------------------
/manifest.yml.sample:
--------------------------------------------------------------------------------
1 | ## This file is used for deployment to cloudfoundry.
2 |
3 | ---
4 | applications:
5 | - name: huginn
6 | url:
7 | path: .
8 | memory: 512M
9 | command: nohup foreman start --procfile Procfile.CF
10 | instances: 1
11 | services:
12 | - huginn-db
13 |
--------------------------------------------------------------------------------
/app/validators/owned_by_validator.rb:
--------------------------------------------------------------------------------
1 | class OwnedByValidator < ActiveModel::EachValidator
2 | def validate_each(record, attribute, association)
3 | return if association.all? {|s| s[options[:with]] == record[options[:with]] }
4 | record.errors[attribute] << "must be owned by you"
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/lib/tasks/database_test.rake:
--------------------------------------------------------------------------------
1 | namespace :database_test do
2 | desc "Ping the database"
3 | task :ping do
4 | require 'active_record'
5 | require 'mysql2'
6 | require 'pg'
7 | ActiveRecord::Base.establish_connection
8 | ActiveRecord::Base.connection.verify!
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/nitrous.json:
--------------------------------------------------------------------------------
1 | {
2 | "template": "rails",
3 | "ports": [3000],
4 | "name": "Huginn",
5 | "description": "Build agents that monitor and act on your behalf. Your agents are standing by!",
6 | "scripts": {
7 | "Start Huginn": "cd ~/code/huginn && bundle exec foreman start"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/config/initializers/action_mailer.rb:
--------------------------------------------------------------------------------
1 | # Read smtp config out of a config/smtp.yml file
2 |
3 | smtp_config = YAML::load(ERB.new(File.read(Rails.root.join('config', 'smtp.yml'))).result)
4 | if smtp_config.keys.include? Rails.env
5 | ActionMailer::Base.smtp_settings = smtp_config[Rails.env].symbolize_keys
6 | end
--------------------------------------------------------------------------------
/db/migrate/20140602014917_add_indices_to_scenarios.rb:
--------------------------------------------------------------------------------
1 | class AddIndicesToScenarios < ActiveRecord::Migration
2 | def change
3 | add_index :scenarios, [:user_id, :guid], :unique => true
4 | add_index :scenario_memberships, :agent_id
5 | add_index :scenario_memberships, :scenario_id
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/.openshift/markers/README:
--------------------------------------------------------------------------------
1 | Markers
2 | ===========
3 |
4 | Adding marker files to this directory will have the following effects:
5 |
6 | force_clean_build - Previous output from bundle install --deployment will be
7 | removed and all gems will be reinstalled according to the current
8 | Gemfile/Gemfile.lock.
9 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/scenarios.css.scss:
--------------------------------------------------------------------------------
1 | .scenario-import {
2 | .agent-import-list {
3 | .agent-import {
4 | margin-bottom: 20px;
5 |
6 | .instructions {
7 | margin-bottom: 10px;
8 | }
9 |
10 | .current {
11 | font-weight: bold;
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/app/views/system_mailer/send_message.text.erb:
--------------------------------------------------------------------------------
1 | <% if @body.present? %><%= sanitize @body -%>
2 | <% else -%>
3 | <% if @headline %><%= @headline %>
4 |
5 | <% end %><% @groups.each do |group| %><%= group[:title] %>
6 | <% group[:entries].each do |entry| %> <%= entry %>
7 | <% end %>
8 |
9 | <% end %>
10 | <% end %>
--------------------------------------------------------------------------------
/bin/threaded.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative './pre_runner_boot'
4 |
5 | agent_runner = AgentRunner.new
6 |
7 | # We need to wait a bit to let delayed_job set it's traps so we can override them
8 | Thread.new do
9 | sleep 5
10 | agent_runner.set_traps
11 | end
12 |
13 | agent_runner.run
14 |
--------------------------------------------------------------------------------
/db/migrate/20140408150825_rename_webhook_to_web_request.rb:
--------------------------------------------------------------------------------
1 | class RenameWebhookToWebRequest < ActiveRecord::Migration
2 | def up
3 | rename_column :agents, :last_webhook_at, :last_web_request_at
4 | end
5 |
6 | def down
7 | rename_column :agents, :last_web_request_at, :last_webhook_at
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This is a simple build script and will be executed on your CI system if
3 | # available. Otherwise it will execute while your application is stopped
4 | # before the deploy step. This script gets executed directly, so it
5 | # could be python, php, ruby, etc.
6 |
7 | echo "-> Build step"
--------------------------------------------------------------------------------
/db/migrate/20121222074732_create_links.rb:
--------------------------------------------------------------------------------
1 | class CreateLinks < ActiveRecord::Migration
2 | def change
3 | create_table :links do |t|
4 | t.integer :source_id
5 | t.integer :receiver_id
6 |
7 | t.timestamps
8 | end
9 |
10 | add_index :links, [:source_id, :receiver_id]
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/delayed_job_worker.rb:
--------------------------------------------------------------------------------
1 | class DelayedJobWorker < LongRunnable::Worker
2 | include LongRunnable
3 |
4 | def run
5 | @dj = Delayed::Worker.new
6 | @dj.start
7 | end
8 |
9 | def stop
10 | @dj.stop if @dj
11 | end
12 |
13 | def self.setup_worker
14 | [new(id: self.to_s)]
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/doc/manual/README.md:
--------------------------------------------------------------------------------
1 | # Manual Installation
2 |
3 | - [Requirements](requirements.md) Software and hardware requirements to run the Huginn installation
4 | - [Install](installation.md) Installation guide for Ubuntu/Debian
5 | - [Update](update.md) Update an existing Huginn installation
6 | - Deploy updates via [Capistrano](capistrano.md)
7 |
--------------------------------------------------------------------------------
/lib/tasks/rspec.rake:
--------------------------------------------------------------------------------
1 | if defined? RSpec
2 | namespace :spec do
3 | desc 'Run all specs in spec directory (exluding feature specs)'
4 | RSpec::Core::RakeTask.new(:nofeatures) do |task|
5 | ENV['RSPEC_TASK'] = 'spec:nofeatures'
6 | task.exclude_pattern = "spec/features/**/*_spec.rb"
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/agents/_mini_action_menu.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= render 'agents/action_menu', agent: agent, return_to: (defined?(return_to) && return_to) || request.path %>
6 |
7 |
--------------------------------------------------------------------------------
/db/migrate/20140216201250_add_propagate_immediately_to_agent.rb:
--------------------------------------------------------------------------------
1 | class AddPropagateImmediatelyToAgent < ActiveRecord::Migration
2 | def up
3 | add_column :agents, :propagate_immediately, :boolean, :default => false, :null => false
4 | end
5 |
6 | def down
7 | remove_column :agents, :propagate_immediately
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140509170443_create_scenario_memberships.rb:
--------------------------------------------------------------------------------
1 | class CreateScenarioMemberships < ActiveRecord::Migration
2 | def change
3 | create_table :scenario_memberships do |t|
4 | t.integer :agent_id, :null => false
5 | t.integer :scenario_id, :null => false
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | Click the link below to unlock your account:
6 |
7 | <%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
8 |
--------------------------------------------------------------------------------
/bin/schedule.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This process is used to maintain Huginn's upkeep behavior, automatically running scheduled Agents and
4 | # periodically propagating and expiring Events. It's typically run via foreman and the included Procfile.
5 |
6 | require_relative './pre_runner_boot'
7 |
8 | AgentRunner.new(only: HuginnScheduler).run
--------------------------------------------------------------------------------
/spec/fixtures/events.yml:
--------------------------------------------------------------------------------
1 | bob_website_agent_event:
2 | user: bob
3 | agent: bob_website_agent
4 | payload: <%= { :title => "foo", :url => "http://foo.com" }.to_json.inspect %>
5 |
6 | jane_website_agent_event:
7 | user: jane
8 | agent: jane_website_agent
9 | payload: <%= { :title => "foo", :url => "http://foo.com" }.to_json.inspect %>
10 |
--------------------------------------------------------------------------------
/app/controllers/users/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class RegistrationsController < Devise::RegistrationsController
3 | after_action :create_default_scenario, only: :create
4 |
5 | private
6 |
7 | def create_default_scenario
8 | DefaultScenarioImporter.import(@user) if @user.persisted?
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/views/agents/agent_views/webhook_agent/_show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Send WebHooks (POST requests) to this Agent at <%= link_to web_requests_url(:agent_id => @agent.id, :user_id => current_user.id, :secret => @agent.options['secret']), web_requests_url(:agent_id => @agent.id, :user_id => current_user.id, :secret => @agent.options['secret']), :target => :blank %>
3 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Remove the XML parser from the list that will be used to initialize the application's XML parser list.
5 | ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)
6 |
7 | # Initialize the rails application
8 | Huginn::Application.initialize!
9 |
--------------------------------------------------------------------------------
/docker/single-process/environment.yml:
--------------------------------------------------------------------------------
1 | huginn_base:
2 | environment:
3 | DATABASE_ADAPTER: mysql2
4 | DATABASE_NAME: huginn
5 | DATABASE_USERNAME: huginn
6 | DATABASE_PASSWORD: myhuginnpassword
7 | APP_SECRET_TOKEN: 3bd139f9186b31a85336bb89cd1a1337078921134b2f48e022fd09c234d764d3e19b018b2ab789c6e0e04a1ac9e3365116368049660234c2038dc9990513d49c
8 |
--------------------------------------------------------------------------------
/app/views/agents/_oauth_dropdown.html.erb:
--------------------------------------------------------------------------------
1 | <% if agent.try(:oauthable?) %>
2 |
3 | <%= label_tag :service %>
4 | <%= select_tag 'agent[service_id]', options_for_select(agent.valid_services_for(current_user).collect { |s| [service_label_text(s), s.id] }, agent.service_id), class: 'form-control' %>
5 |
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/docker/multi-process/scripts/standalone-packages:
--------------------------------------------------------------------------------
1 | DEBIAN_FRONTEND=noninteractive
2 | apt-get update
3 | apt-get install -y python2.7 python-docutils mysql-server \
4 | supervisor python-pip && \
5 | pip install supervisor-stdout
6 | rm -rf /var/lib/apt/lists/*
7 | rm -rf /usr/share/doc/
8 | rm -rf /usr/share/man/
9 | rm -rf /usr/share/locale/
10 |
--------------------------------------------------------------------------------
/bin/pre_runner_boot.rb:
--------------------------------------------------------------------------------
1 | unless defined?(Rails)
2 | puts
3 | puts "Please run me with rails runner, for example:"
4 | puts " RAILS_ENV=production bundle exec rails runner bin/#{File.basename($0)}"
5 | puts
6 | exit 1
7 | end
8 |
9 | Rails.configuration.cache_classes = true
10 |
11 | Dotenv.load if ENV['APP_SECRET_TOKEN'].blank?
12 |
13 | require 'agent_runner'
14 |
--------------------------------------------------------------------------------
/spec/helpers/markdown_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe MarkdownHelper do
4 |
5 | describe '#markdown' do
6 |
7 | it 'renders HTML from a markdown text' do
8 | expect(markdown('# Header')).to match(/Header<\/h1>/)
9 | expect(markdown('## Header 2')).to match(/Header 2<\/h2>/)
10 | end
11 |
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140531232016_add_fields_to_scenarios.rb:
--------------------------------------------------------------------------------
1 | class AddFieldsToScenarios < ActiveRecord::Migration
2 | def change
3 | add_column :scenarios, :description, :text
4 | add_column :scenarios, :public, :boolean, :default => false, :null => false
5 | add_column :scenarios, :guid, :string, :null => false
6 | add_column :scenarios, :source_url, :string
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/ar_mysql_column_charset.rb:
--------------------------------------------------------------------------------
1 | require 'active_support'
2 |
3 | ActiveSupport.on_load :active_record do
4 | class << ActiveRecord::Base
5 | def establish_connection(spec = nil)
6 | super.tap { |ret|
7 | if /mysql/i === connection.adapter_name
8 | require 'ar_mysql_column_charset/main'
9 | end
10 | }
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/bin/agent_runner.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This process is used to maintain Huginn's upkeep behavior, automatically running scheduled Agents and
4 | # periodically propagating and expiring Events. It also running TwitterStreamAgents and Agents that support long running
5 | # background jobs.
6 |
7 | require_relative './pre_runner_boot'
8 |
9 | AgentRunner.new(except: DelayedJobWorker).run
--------------------------------------------------------------------------------
/db/migrate/20140509170420_create_scenarios.rb:
--------------------------------------------------------------------------------
1 | class CreateScenarios < ActiveRecord::Migration
2 | def change
3 | create_table :scenarios do |t|
4 | t.string :name, :null => false
5 | t.integer :user_id, :null => false
6 |
7 | t.timestamps
8 | end
9 |
10 | add_column :users, :scenario_count, :integer, :null => false, :default => 0
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js.coffee:
--------------------------------------------------------------------------------
1 | #= require jquery
2 | #= require jquery_ujs
3 | #= require typeahead.bundle
4 | #= require bootstrap
5 | #= require select2
6 | #= require json2
7 | #= require jquery.json-editor
8 | #= require jquery.serializeObject
9 | #= require latlon_and_geo
10 | #= require spectrum
11 | #= require_tree ./components
12 | #= require_tree ./pages
13 | #= require_self
14 |
--------------------------------------------------------------------------------
/config/initializers/liquid.rb:
--------------------------------------------------------------------------------
1 | module Liquid
2 | # https://github.com/Shopify/liquid/pull/623
3 | remove_const :PartialTemplateParser
4 | remove_const :TemplateParser
5 |
6 | PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}(?:(?:[^'"{}]+|#{QuotedString})*?|.*?)#{VariableIncompleteEnd}/m
7 | TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/m
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20140906030139_set_events_count_default.rb:
--------------------------------------------------------------------------------
1 | class SetEventsCountDefault < ActiveRecord::Migration
2 | def up
3 | change_column_default(:agents, :events_count, 0)
4 | change_column_null(:agents, :events_count, false, 0)
5 | end
6 |
7 | def down
8 | change_column_null(:agents, :events_count, true)
9 | change_column_default(:agents, :events_count, nil)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/bin/twitter_stream.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This process is used by TwitterStreamAgents to watch the Twitter stream in real time. It periodically checks for
4 | # new or changed TwitterStreamAgents and starts to follow the stream for them. It is typically run by foreman via
5 | # the included Procfile.
6 |
7 | require_relative './pre_runner_boot'
8 |
9 | AgentRunner.new(only: Agents::TwitterStreamAgent).run
--------------------------------------------------------------------------------
/app/models/control_link.rb:
--------------------------------------------------------------------------------
1 | # A ControlLink connects Agents in a control flow from the `controller` to the `control_target`.
2 | class ControlLink < ActiveRecord::Base
3 | attr_accessible :controller_id, :target_id
4 |
5 | belongs_to :controller, class_name: 'Agent', inverse_of: :control_links_as_controller
6 | belongs_to :control_target, class_name: 'Agent', inverse_of: :control_links_as_control_target
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/shared_examples/has_guid.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | shared_examples_for HasGuid do
4 | it "gets created before_save, but only if it's not present" do
5 | instance = new_instance
6 | expect(instance.guid).to be_nil
7 | instance.save!
8 | expect(instance.guid).not_to be_nil
9 |
10 | expect { instance.save! }.not_to change { instance.reload.guid }
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140605032822_add_guid_to_agents.rb:
--------------------------------------------------------------------------------
1 | class AddGuidToAgents < ActiveRecord::Migration
2 | class Agent < ActiveRecord::Base; end
3 |
4 | def change
5 | add_column :agents, :guid, :string
6 |
7 | Agent.find_each do |agent|
8 | agent.update_attribute :guid, SecureRandom.hex
9 | end
10 |
11 | change_column_null :agents, :guid, false
12 |
13 | add_index :agents, :guid
14 | end
15 | end
--------------------------------------------------------------------------------
/spec/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2 |
3 | bob:
4 | email: "bob@example.com"
5 | username: bob
6 | invitation_code: <%= User::INVITATION_CODES.last %>
7 | scenario_count: 1
8 |
9 | jane:
10 | email: "jane@example.com"
11 | username: jane
12 | invitation_code: <%= User::INVITATION_CODES.last %>
13 | scenario_count: 1
14 | admin: true
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/rails-now.rb:
--------------------------------------------------------------------------------
1 | # If you use gems that require environment variables to be set before they are
2 | # loaded, then list `dotenv-rails` in the `Gemfile` before those other gems and
3 | # require `dotenv/rails-now`.
4 | #
5 | # gem "dotenv-rails", :require => "dotenv/rails-now"
6 | # gem "gem-that-requires-env-variables"
7 | #
8 |
9 | require "dotenv/rails"
10 | Dotenv::Railtie.load
11 |
--------------------------------------------------------------------------------
/app/controllers/logs_controller.rb:
--------------------------------------------------------------------------------
1 | class LogsController < ApplicationController
2 | before_action :load_agent
3 |
4 | def index
5 | @logs = @agent.logs.all
6 | render :action => :index, :layout => false
7 | end
8 |
9 | def clear
10 | @agent.delete_logs!
11 | index
12 | end
13 |
14 | protected
15 |
16 | def load_agent
17 | @agent = current_user.agents.find(params[:agent_id])
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20130819160603_create_agent_logs.rb:
--------------------------------------------------------------------------------
1 | class CreateAgentLogs < ActiveRecord::Migration
2 | def change
3 | create_table :agent_logs do |t|
4 | t.integer :agent_id, :null => false
5 | t.text :message, :null => false
6 | t.integer :level, :default => 3, :null => false
7 | t.integer :inbound_event_id
8 | t.integer :outbound_event_id
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | Huginn::Application.config.secret_key_base = ENV['APP_SECRET_TOKEN']
8 |
--------------------------------------------------------------------------------
/config/initializers/silence_worker_status_logger.rb:
--------------------------------------------------------------------------------
1 | Rails::Rack::Logger.class_eval do
2 | def call_with_silence_worker_status(env)
3 | previous_level = Rails.logger.level
4 | Rails.logger.level = Logger::ERROR if env['PATH_INFO'] =~ %r{^/worker_status}
5 | call_without_silence_worker_status(env)
6 | ensure
7 | Rails.logger.level = previous_level
8 | end
9 | alias_method_chain :call, :silence_worker_status
10 | end
11 |
--------------------------------------------------------------------------------
/lib/json_with_indifferent_access.rb:
--------------------------------------------------------------------------------
1 | class JSONWithIndifferentAccess
2 | def self.load(json)
3 | ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(json || '{}'))
4 | rescue JSON::ParserError
5 | Rails.logger.error "Unparsable JSON in JSONWithIndifferentAccess: #{json}"
6 | { 'error' => 'unparsable json detected during de-serialization' }
7 | end
8 |
9 | def self.dump(hash)
10 | JSON.dump(hash)
11 | end
12 | end
--------------------------------------------------------------------------------
/vendor/assets/javascripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | //= require bootstrap/affix
2 | //= require bootstrap/alert
3 | //= require bootstrap/button
4 | //= require bootstrap/carousel
5 | //= require bootstrap/collapse
6 | //= require bootstrap/dropdown
7 | //= require bootstrap/tab
8 | //= require bootstrap/transition
9 | //= require bootstrap/scrollspy
10 | //= require bootstrap/modal
11 | //= require bootstrap/tooltip
12 | //= require bootstrap/popover
13 |
--------------------------------------------------------------------------------
/db/migrate/20150507153436_update_keep_events_for_to_be_in_seconds.rb:
--------------------------------------------------------------------------------
1 | class UpdateKeepEventsForToBeInSeconds < ActiveRecord::Migration
2 | class Agent < ActiveRecord::Base; end
3 |
4 | SECONDS_IN_DAY = 60 * 60 * 24
5 |
6 | def up
7 | Agent.update_all ['keep_events_for = keep_events_for * ?', SECONDS_IN_DAY]
8 | end
9 |
10 | def down
11 | Agent.update_all ['keep_events_for = keep_events_for / ?', SECONDS_IN_DAY]
12 | end
13 | end
--------------------------------------------------------------------------------
/spec/data_fixtures/stubhub_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "response":{
3 | "docs":[
4 | {
5 | "url": "http://www.stubhub.com/event/name-1-1-2014-12345",
6 | "seo_description_en_US": "name",
7 | "event_date_local": "2014-01-01",
8 | "maxPrice": "100",
9 | "minPrice": "50",
10 | "totalPostings": "100",
11 | "totalTickets": "200",
12 | "venue_name": "Venue Name"
13 | }
14 | ]
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/db/migrate/20140901143732_add_control_links.rb:
--------------------------------------------------------------------------------
1 | class AddControlLinks < ActiveRecord::Migration
2 | def change
3 | create_table :control_links do |t|
4 | t.integer :controller_id, null: false
5 | t.integer :control_target_id, null: false
6 |
7 | t.timestamps
8 | end
9 |
10 | add_index :control_links, [:controller_id, :control_target_id], unique: true
11 | add_index :control_links, :control_target_id
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password. You can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/db/migrate/20140121075418_create_user_credentials.rb:
--------------------------------------------------------------------------------
1 | class CreateUserCredentials < ActiveRecord::Migration
2 | def change
3 | create_table :user_credentials do |t|
4 | t.integer :user_id, :null => false
5 | t.string :credential_name, :null => false
6 | t.text :credential_value, :null => false
7 |
8 | t.timestamps
9 | end
10 | add_index :user_credentials, [:user_id, :credential_name], :unique => true
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20160224120316_add_mode_option_to_ftpsite_agents.rb:
--------------------------------------------------------------------------------
1 | class AddModeOptionToFtpsiteAgents < ActiveRecord::Migration
2 | def up
3 | Agents::FtpsiteAgent.find_each do |agent|
4 | agent.options['mode'] = 'read'
5 | agent.save!(validate: false)
6 | end
7 | end
8 |
9 | def down
10 | Agents::FtpsiteAgent.find_each do |agent|
11 | agent.options.delete 'mode'
12 | agent.save!(validate: false)
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20160405072512_post_agent_set_event_header_style.rb:
--------------------------------------------------------------------------------
1 | class PostAgentSetEventHeaderStyle < ActiveRecord::Migration
2 | def up
3 | Agent.of_type("Agents::PostAgent").each do |post_agent|
4 | if post_agent.send(:boolify, post_agent.options['emit_events']) &&
5 | !post_agent.options.key?('event_headers_style')
6 | post_agent.options['event_headers_style'] = 'raw'
7 | post_agent.save!
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.rbc
2 | *.sassc
3 | .sass-cache
4 | capybara-*.html
5 | .rspec
6 | /.bundle
7 | /vendor/bundle
8 | /log/*
9 | /tmp/*
10 | !/tmp/.gitkeep
11 | /db/*.sqlite3
12 | /public/system/*
13 | /coverage/
14 | /spec/tmp/*
15 | **.orig
16 | rerun.txt
17 | pickle-email-*.html
18 | .idea/
19 | .DS_Store
20 | .env
21 | deployment/tmp
22 | deployment/cookbooks
23 | .vagrant
24 | .*un~
25 | .ruby-gemset
26 | .ruby-version
27 | manifest.yml
28 | config/unicorn.rb
29 | db/schema.rb
30 |
--------------------------------------------------------------------------------
/.openshift/cron/weekly/jobs.allow:
--------------------------------------------------------------------------------
1 | #
2 | # Script or job files listed in here (one entry per line) will be
3 | # executed on a weekly-basis.
4 | #
5 | # Example: The chronograph script will be executed weekly but the README
6 | # and chrono.dat files in this directory will be ignored.
7 | #
8 | # The README file is actually ignored due to the entry in the
9 | # jobs.deny which is checked before jobs.allow (this file).
10 | #
11 | chronograph
12 |
13 |
--------------------------------------------------------------------------------
/app/views/layouts/_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if flash.keys.length > 0 %>
2 |
3 | <% flash.each do |name, msg| %>
4 |
alert-dismissable">
5 | ×
6 | <%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
7 |
8 | <% end %>
9 |
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/app/controllers/diagrams_controller.rb:
--------------------------------------------------------------------------------
1 | class DiagramsController < ApplicationController
2 | def show
3 | if params[:scenario_id].present?
4 | @scenario = current_user.scenarios.find(params[:scenario_id])
5 | agents = @scenario.agents
6 | else
7 | agents = current_user.agents
8 | end
9 | @disabled_agents = agents.inactive
10 | agents = agents.active if params[:exclude_disabled].present?
11 | @agents = agents.includes(:receivers)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/rdbms_functions.rb:
--------------------------------------------------------------------------------
1 | module RDBMSFunctions
2 | def rdbms_date_add(source, unit, amount)
3 | adapter_type = ActiveRecord::Base.connection.adapter_name.downcase.to_sym
4 | case adapter_type
5 | when :mysql, :mysql2
6 | "DATE_ADD(`#{source}`, INTERVAL #{amount} #{unit})"
7 | when :postgresql
8 | "(#{source} + INTERVAL '#{amount} #{unit}')"
9 | else
10 | raise NotImplementedError, "Unknown adapter type '#{adapter_type}'"
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/pages/scenario-form-page.js.coffee:
--------------------------------------------------------------------------------
1 | class @ScenarioFormPage
2 | constructor:() ->
3 | @enabledSelect2()
4 |
5 | format: (icon) ->
6 | originalOption = icon.element
7 | ' ' + icon.text
8 |
9 | enabledSelect2: () ->
10 | $('.select2-fountawesome-icon').select2
11 | width: '100%'
12 | formatResult: @format
13 |
14 | $ ->
15 | Utils.registerPage(ScenarioFormPage, forPathsMatching: /^scenarios/)
--------------------------------------------------------------------------------
/db/migrate/20121216025930_create_events.rb:
--------------------------------------------------------------------------------
1 | class CreateEvents < ActiveRecord::Migration
2 | def change
3 | create_table :events do |t|
4 | t.integer :user_id
5 | t.integer :agent_id
6 | t.decimal :lat, :precision => 15, :scale => 10
7 | t.decimal :lng, :precision => 15, :scale => 10
8 | t.text :payload
9 |
10 | t.timestamps
11 | end
12 |
13 | add_index :events, [:user_id, :created_at]
14 | add_index :events, [:agent_id, :created_at]
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/bin/decrypt_backup.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # If you're using the backup gem, described on the Huginn wiki and at doc/deployment/backup, then you can use this
4 | # utility to decrypt backups.
5 |
6 | in_file = ARGV.shift
7 | out_file = ARGV.shift || "decrypted_backup.tar"
8 |
9 | puts "About to decrypt #{in_file} and write it to #{out_file}."
10 |
11 | cmd = "bundle exec backup decrypt --encryptor openssl --base64 --salt --in #{in_file} --out #{out_file}"
12 | puts "Executing: #{cmd}"
13 | puts `#{cmd}`
14 |
--------------------------------------------------------------------------------
/spec/support/spec_helpers.rb:
--------------------------------------------------------------------------------
1 | module SpecHelpers
2 | def build_events(options = {})
3 | options[:values].map.with_index do |tuple, index|
4 | event = Event.new
5 | event.agent = agents(:bob_weather_agent)
6 | event.payload = (options[:pattern] || {}).dup.merge((options[:keys].zip(tuple)).inject({}) { |memo, (key, value)| memo[key] = value; memo })
7 | event.created_at = (100 - index).hours.ago
8 | event.updated_at = (100 - index).hours.ago
9 | event
10 | end
11 | end
12 | end
--------------------------------------------------------------------------------
/app/assets/stylesheets/diagram.css.scss:
--------------------------------------------------------------------------------
1 | .agent-diagram {
2 | position: relative;
3 | z-index: auto;
4 |
5 | svg.diagram {
6 | position: absolute;
7 | z-index: 1;
8 | }
9 |
10 | .overlay-container {
11 | z-index: auto;
12 |
13 | .overlay {
14 | z-index: auto;
15 | width: 100%;
16 | height: 100%;
17 |
18 | .badge {
19 | position: absolute;
20 | display: none;
21 | color: white !important;
22 | z-index: 2;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require "rubygems"
8 | require "bundler"
9 |
10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
11 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq }
12 | gem "spring", match[1]
13 | require "spring/binstub"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/fixtures/services.yml:
--------------------------------------------------------------------------------
1 | generic:
2 | token: 1234token
3 | secret: 56789secret
4 | refresh_token: refresh12345
5 | provider: testprovider
6 | name: test
7 | expires_at: <%= Time.parse("2015-01-01 00:00:00") %>
8 | options: <%= { user_id: 12345 }.to_yaml.inspect %>
9 | user: bob
10 | global:
11 | token: 1234token
12 | provider: testprovider
13 | name: test
14 | expires_at: <%= Time.parse("2015-01-01 00:00:00") %>
15 | options: <%= { user_id: 12345 }.to_yaml.inspect %>
16 | user: jane
17 | global: true
--------------------------------------------------------------------------------
/spec/fixtures/user_credentials.yml:
--------------------------------------------------------------------------------
1 | bob_aws_key:
2 | user: bob
3 | credential_name: aws_key
4 | credential_value: 2222222222-bob
5 | mode: text
6 | bob_aws_secret:
7 | user: bob
8 | credential_name: aws_secret
9 | credential_value: 1111111111-bob
10 | mode: text
11 | jane_aws_key:
12 | user: jane
13 | credential_name: aws_key
14 | credential_value: 2222222222-jane
15 | mode: text
16 | jane_aws_secret:
17 | user: jane
18 | credential_name: aws_secret
19 | credential_value: 1111111111-jabe
20 | mode: text
--------------------------------------------------------------------------------
/app/helpers/users_helper.rb:
--------------------------------------------------------------------------------
1 | module UsersHelper
2 | def user_account_state(user)
3 | if !user.active?
4 | content_tag :span, 'inactive', class: 'label label-danger'
5 | elsif user.access_locked?
6 | content_tag :span, 'locked', class: 'label label-danger'
7 | elsif ENV['REQUIRE_CONFIRMED_EMAIL'] == 'true' && !user.confirmed?
8 | content_tag :span, 'unconfirmed', class: 'label label-warning'
9 | else
10 | content_tag :span, 'active', class: 'label label-success'
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/jobs/agent_check_job.rb:
--------------------------------------------------------------------------------
1 | class AgentCheckJob < ActiveJob::Base
2 | # Given an Agent id, load the Agent, call #check on it, and then save it with an updated `last_check_at` timestamp.
3 | def perform(agent_id)
4 | agent = Agent.find(agent_id)
5 | begin
6 | return if agent.unavailable?
7 | agent.check
8 | agent.last_check_at = Time.now
9 | agent.save!
10 | rescue => e
11 | agent.error "Exception during check. #{e.message}: #{e.backtrace.join("\n")}"
12 | raise
13 | end
14 | end
15 | end
--------------------------------------------------------------------------------
/app/views/agents/agent_views/data_output_agent/_show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Data for this Agent is available at these URLs:
3 |
4 |
5 |
6 |
7 | <% @agent.options['secrets'].each do |secret| %>
8 | <% url = lambda { |format| web_requests_url(:agent_id => @agent.id, :user_id => current_user.id, :secret => secret, :format => format) } %>
9 | <%= link_to url.call(:json), url.call(:json), :target => :blank %>
10 | <%= link_to url.call(:xml), url.call(:xml), :target => :blank %>
11 | <% end %>
12 |
13 |
--------------------------------------------------------------------------------
/db/migrate/20120919061122_enable_lockable_strategy_for_devise.rb:
--------------------------------------------------------------------------------
1 | class EnableLockableStrategyForDevise < ActiveRecord::Migration
2 | def up
3 | add_column :users, :failed_attempts, :integer, :default => 0
4 | add_column :users, :unlock_token, :string
5 | add_column :users, :locked_at, :datetime
6 |
7 | add_index :users, :unlock_token, :unique => true
8 | end
9 |
10 | def down
11 | remove_column :users, :failed_attempts
12 | remove_column :users, :unlock_token
13 | remove_column :users, :locked_at
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20120919063304_add_username_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddUsernameToUsers < ActiveRecord::Migration
2 | class User < ActiveRecord::Base
3 | end
4 |
5 | def up
6 | add_column :users, :username, :string
7 |
8 | User.find_each do |user|
9 | user.update_attribute :username, user.email.gsub(/@.*$/, '')
10 | end
11 |
12 | change_column :users, :username, :string, :null => false
13 | add_index :users, :username, :unique => true
14 | end
15 |
16 | def down
17 | remove_column :users, :username
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20140127164931_change_handler_to_medium_text.rb:
--------------------------------------------------------------------------------
1 | # Increase handler size to 16MB (consistent with events.payload)
2 |
3 | class ChangeHandlerToMediumText < ActiveRecord::Migration
4 | def up
5 | if mysql?
6 | change_column :delayed_jobs, :handler, :text, :limit => 16777215
7 | end
8 | end
9 |
10 | def down
11 | if mysql?
12 | change_column :delayed_jobs, :handler, :text, :limit => 65535
13 | end
14 | end
15 |
16 | def mysql?
17 | ActiveRecord::Base.connection.adapter_name =~ /mysql/i
18 | end
19 | end
--------------------------------------------------------------------------------
/app/views/scenarios/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | <%= render 'form' %>
11 |
12 |
13 |
14 |
15 |
16 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, scenarios_path, class: "btn btn-default" %>
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/views/scenarios/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | <%= render 'form' %>
11 |
12 |
13 |
14 |
15 |
16 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, scenarios_path, class: "btn btn-default" %>
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/models/link.rb:
--------------------------------------------------------------------------------
1 | # A Link connects Agents in a directed Event flow from the `source` to the `receiver`.
2 | class Link < ActiveRecord::Base
3 | attr_accessible :source_id, :receiver_id
4 |
5 | belongs_to :source, :class_name => "Agent", :inverse_of => :links_as_source
6 | belongs_to :receiver, :class_name => "Agent", :inverse_of => :links_as_receiver
7 |
8 | before_create :store_event_id_at_creation
9 |
10 | def store_event_id_at_creation
11 | self.event_id_at_creation = source.events.limit(1).reorder("id desc").pluck(:id).first || 0
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/doc/heroku/update.md:
--------------------------------------------------------------------------------
1 | ## Update an existing Heroku deployment
2 |
3 | Once you've run `bin/setup_heroku`, you should have 'cantino/huginn' as a remote in git. (Check with `git remote -v`.) Now, you can update your Heroku installation with the following commands:
4 |
5 | ```sh
6 | git fetch origin
7 | git merge origin/master
8 | git push -f heroku master # note: this will wipe out any code changes that only exist on Heroku!
9 | heroku run rake db:migrate # this will migrate the database to the latest state (not needed for every update, but always safe to run)
10 | ```
11 |
--------------------------------------------------------------------------------
/spec/fixtures/agent_logs.yml:
--------------------------------------------------------------------------------
1 | log_for_jane_website_agent:
2 | agent: jane_website_agent
3 | outbound_event: jane_website_agent_event
4 | message: "fetching some website data"
5 |
6 | log_for_bob_website_agent:
7 | agent: bob_website_agent
8 | outbound_event: bob_website_agent_event
9 | message: "fetching some other website data"
10 |
11 | first_log_for_bob_weather_agent:
12 | agent: bob_weather_agent
13 | message: "checking the weather"
14 |
15 | second_log_for_bob_weather_agent:
16 | agent: bob_weather_agent
17 | message: "checking the weather again"
18 |
--------------------------------------------------------------------------------
/spec/concerns/inheritance_tracking_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 | require 'inheritance_tracking'
3 |
4 | describe InheritanceTracking do
5 | class Class1
6 | include InheritanceTracking
7 | end
8 |
9 | class Class2 < Class1; end
10 | class Class3 < Class1; end
11 |
12 | it "tracks subclasses" do
13 | expect(Class1.subclasses).to eq([Class2, Class3])
14 | end
15 |
16 | it "can be temporarily overridden with #with_subclasses" do
17 | Class1.with_subclasses(Class2) do
18 | expect(Class1.subclasses).to eq([Class2])
19 | end
20 | end
21 | end
--------------------------------------------------------------------------------
/spec/fixtures/scenarios.yml:
--------------------------------------------------------------------------------
1 | jane_weather:
2 | name: Jane's weather alert Scenario
3 | user: jane
4 | description: Jane's weather alert system
5 | public: false
6 | guid: random-guid-generated-by-bob
7 |
8 | jane_weather_duplicate:
9 | name: Jane's duplicated, incomplete weather alert Scenario
10 | user: jane
11 | public: false
12 | guid: random-guid-generated-by-jane2
13 |
14 | bob_weather:
15 | name: Bob's weather alert Scenario
16 | user: bob
17 | description: Bob's weather alert system
18 | public: false
19 | guid: random-guid-generated-by-jane
20 |
--------------------------------------------------------------------------------
/app/concerns/inheritance_tracking.rb:
--------------------------------------------------------------------------------
1 | module InheritanceTracking
2 | extend ActiveSupport::Concern
3 |
4 | module ClassMethods
5 | def inherited(subclass)
6 | @subclasses ||= []
7 | @subclasses << subclass
8 | @subclasses.uniq!
9 | super
10 | end
11 |
12 | def subclasses
13 | @subclasses
14 | end
15 |
16 | def with_subclasses(*subclasses)
17 | original_subclasses = @subclasses
18 | @subclasses = subclasses.flatten
19 | yield
20 | ensure
21 | @subclasses = original_subclasses
22 | end
23 | end
24 | end
--------------------------------------------------------------------------------
/app/controllers/scenario_imports_controller.rb:
--------------------------------------------------------------------------------
1 | class ScenarioImportsController < ApplicationController
2 | def new
3 | @scenario_import = ScenarioImport.new(:url => params[:url])
4 | end
5 |
6 | def create
7 | @scenario_import = ScenarioImport.new(params[:scenario_import])
8 | @scenario_import.set_user(current_user)
9 |
10 | if @scenario_import.valid? && @scenario_import.import_confirmed? && @scenario_import.import
11 | redirect_to @scenario_import.scenario, notice: "Import successful!"
12 | else
13 | render action: "new"
14 | end
15 | end
16 | end
--------------------------------------------------------------------------------
/app/views/user_credentials/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | <%= render 'form' %>
11 |
12 |
13 |
14 |
15 |
16 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, user_credentials_path, class: "btn btn-default" %>
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/views/user_credentials/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | <%= render 'form' %>
11 |
12 |
13 |
14 |
15 |
16 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, user_credentials_path, class: "btn btn-default" %>
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/db/migrate/20160423163416_add_xml_namespace_option_to_data_output_agents.rb:
--------------------------------------------------------------------------------
1 | class AddXmlNamespaceOptionToDataOutputAgents < ActiveRecord::Migration
2 | def up
3 | Agents::DataOutputAgent.find_each do |agent|
4 | agent.options['ns_media'] = 'true'
5 | agent.options['ns_itunes'] = 'true'
6 | agent.save!(validate: false)
7 | end
8 | end
9 |
10 | def down
11 | Agents::DataOutputAgent.find_each do |agent|
12 | agent.options.delete 'ns_media'
13 | agent.options.delete 'ns_itunes'
14 | agent.save!(validate: false)
15 | end
16 | end
17 | end
--------------------------------------------------------------------------------
/spec/data_fixtures/adioso_parse.json:
--------------------------------------------------------------------------------
1 | {
2 | "cost_max": null,
3 | "currency": "USD",
4 | "date_end": 1403740799,
5 | "date_start": 1372032000,
6 | "dest_codes": [
7 | "CGX",
8 | "MDW",
9 | "ORD",
10 | "GYY"
11 | ],
12 | "duration_max": null,
13 | "origin_codes": [
14 | "PDX",
15 | "SLE"
16 | ],
17 | "search_url": "http://api.adioso.com/v2/search/fares?currency=USD&date_end=1403740799&date_start=1372032000&dest_codes=CGX%2CMDW%2CORD%2CGYY&origin_codes=PDX%2CSLE",
18 | "segments_max": null
19 | }
20 |
--------------------------------------------------------------------------------
/db/migrate/20150219213604_add_type_option_attribute_to_pushbullet_agents.rb:
--------------------------------------------------------------------------------
1 | class AddTypeOptionAttributeToPushbulletAgents < ActiveRecord::Migration
2 | def up
3 | Agents::PushbulletAgent.find_each do |agent|
4 | if agent.options['type'].nil?
5 | agent.options['type'] = 'note'
6 | agent.save!
7 | end
8 | end
9 | end
10 |
11 | def down
12 | Agents::PushbulletAgent.find_each do |agent|
13 | if agent.options['type'].present?
14 | agent.options.delete 'type'
15 | agent.save(validate: false)
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140515211100_create_services.rb:
--------------------------------------------------------------------------------
1 | class CreateServices < ActiveRecord::Migration
2 | def change
3 | create_table :services do |t|
4 | t.integer :user_id, null: false
5 | t.string :provider, null: false
6 | t.string :name, null: false
7 | t.text :token, null: false
8 | t.text :secret
9 | t.text :refresh_token
10 | t.datetime :expires_at
11 | t.boolean :global, default: false
12 | t.text :options
13 | t.timestamps
14 | end
15 | add_index :services, :user_id
16 | add_index :services, [:user_id, :global]
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/env.test:
--------------------------------------------------------------------------------
1 | APP_SECRET_TOKEN=notarealappsecrettoken
2 | TWITTER_OAUTH_KEY=twitteroauthkey
3 | TWITTER_OAUTH_SECRET=twitteroauthsecret
4 | TUMBLR_OAUTH_KEY=tumblroauthsecret
5 | TUMBLR_OAUTH_SECRET=tumblroauthsecret
6 | THIRTY_SEVEN_SIGNALS_OAUTH_KEY=TESTKEY
7 | THIRTY_SEVEN_SIGNALS_OAUTH_SECRET=TESTSECRET
8 | DROPBOX_OAUTH_KEY=dropboxoauthkey
9 | DROPBOX_OAUTH_SECRET=dropboxoauthsecret
10 | WUNDERLIST_OAUTH_KEY=wunderoauthkey
11 | EVERNOTE_OAUTH_KEY=evernoteoauthkey
12 | EVERNOTE_OAUTH_SECRET=evernoteoauthsecret
13 | FAILED_JOBS_TO_KEEP=2
14 | REQUIRE_CONFIRMED_EMAIL=false
15 | ENABLE_INSECURE_AGENTS=true
16 |
--------------------------------------------------------------------------------
/app/models/contact.rb:
--------------------------------------------------------------------------------
1 | # Contacts are used only for the contact form on the Huginn website. If you host a public Huginn instance, you can use
2 | # these to receive messages from visitors.
3 |
4 | class Contact < ActiveRecord::Base
5 | attr_accessible :email, :message, :name
6 |
7 | validates_format_of :email, :with => /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)\Z/i
8 | validates_presence_of :name, :message
9 |
10 | after_create :send_contact
11 |
12 | def send_contact
13 | ContactMailer.send_contact(self).deliver
14 | end
15 | end
--------------------------------------------------------------------------------
/db/migrate/20121220053905_create_agents.rb:
--------------------------------------------------------------------------------
1 | class CreateAgents < ActiveRecord::Migration
2 | def change
3 | create_table :agents do |t|
4 | t.integer :user_id
5 | t.text :options
6 | t.string :type
7 | t.string :name
8 | t.string :schedule
9 | t.integer :events_count
10 | t.datetime :last_check_at
11 | t.datetime :last_receive_at
12 | t.integer :last_checked_event_id
13 |
14 | t.timestamps
15 | end
16 |
17 | add_index :agents, [:user_id, :created_at]
18 | add_index :agents, :type
19 | add_index :agents, :schedule
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/assets/javascripts/pages/scenario-show-page.js.coffee:
--------------------------------------------------------------------------------
1 | class @ScenarioShowPage
2 | constructor:() ->
3 | @changeModalText()
4 |
5 | changeModalText: () ->
6 | $('#disable-all').click ->
7 | $('#enable-disable-agents .modal-body').text 'Would you like to disable all agents?'
8 | $('#scenario-disabled-value').val 'true'
9 | $('#enable-all').click ->
10 | $('#enable-disable-agents .modal-body').text 'Would you like to enable all agents?'
11 | $('#scenario-disabled-value').val 'false'
12 |
13 | $ ->
14 | Utils.registerPage(ScenarioShowPage, forPathsMatching: /^scenarios/)
15 |
16 |
--------------------------------------------------------------------------------
/lib/time_tracker.rb:
--------------------------------------------------------------------------------
1 | class TimeTracker
2 | attr_accessor :elapsed_time, :result
3 |
4 | def self.track
5 | start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
6 | result = yield
7 | new(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start, result)
8 | end
9 |
10 | def initialize(elapsed_time, result)
11 | @elapsed_time = elapsed_time
12 | @result = result
13 | end
14 |
15 | def method_missing(method_sym, *arguments, &block)
16 | if @result.respond_to?(method_sym)
17 | @result.send(method_sym, *arguments, &block)
18 | else
19 | super
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/features/edit_an_agent_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe "Editing an agent", js: true do
4 | it "creates an alert if a agent with invalid json is submitted" do
5 | login_as(users(:bob))
6 | visit("/agents/#{agents(:bob_website_agent).id}/edit")
7 | click_on("Toggle View")
8 |
9 | fill_in(:agent_options, with: '{
10 | "expected_receive_period_in_days": "2"
11 | "keep_event": "false"
12 | }')
13 | expect(get_alert_text_from { click_on "Save" }).to have_text("Sorry, there appears to be an error in your JSON input. Please fix it before continuing.")
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/fixtures/scenario_memberships.yml:
--------------------------------------------------------------------------------
1 | jane_weather_agent_scenario_membership:
2 | agent: jane_weather_agent
3 | scenario: jane_weather
4 |
5 | jane_rain_notifier_agent_scenario_membership:
6 | agent: jane_rain_notifier_agent
7 | scenario: jane_weather
8 |
9 | jane_rain_notifier_agent_scenario_membership_duplicate:
10 | agent: jane_weather_agent
11 | scenario: jane_weather_duplicate
12 |
13 | bob_weather_agent_scenario_membership:
14 | agent: bob_weather_agent
15 | scenario: bob_weather
16 |
17 | bob_rain_notifier_agent_scenario_membership:
18 | agent: bob_rain_notifier_agent
19 | scenario: bob_weather
20 |
--------------------------------------------------------------------------------
/deployment/logrotate/huginn:
--------------------------------------------------------------------------------
1 | /home/huginn/huginn/log/*.log {
2 | daily
3 | missingok
4 | rotate 180
5 | # must use with delaycompress below
6 | compress
7 | dateext
8 |
9 | # this is important if using "compress" since we need to call
10 | # the "lastaction" script below before compressing:
11 | delaycompress
12 |
13 | # note the lack of the evil "copytruncate" option in this
14 | # config. Unicorn supports the USR1 signal and we send it
15 | # as our "lastaction" action:
16 | lastaction
17 | pid=/home/huginn/huginn/tmp/pids/unicorn.pid
18 | test -s $pid && kill -USR1 "$(cat $pid)"
19 | endscript
20 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | tmp
3 | log
4 | doc
5 | spec
6 | media
7 | .openshift
8 | .bundle
9 | vendor/bundle
10 | db/*.sqlite3
11 | public/system/*
12 | coverage
13 | spec/tmp/*
14 | .travis.yml
15 | build_docker_image.sh
16 |
17 | # Copied from .gitignore
18 | *.rbc
19 | *.sassc
20 | .sass-cache
21 | capybara-*.html
22 | .rspec
23 | !/tmp/.gitkeep
24 | **.orig
25 | rerun.txt
26 | pickle-email-*.html
27 | .idea/
28 | .DS_Store
29 | .env
30 | deployment/tmp
31 | deployment/cookbooks
32 | .vagrant
33 | .*un~
34 | .ruby-gemset
35 | .ruby-version
36 | manifest.yml
37 | config/unicorn.rb
38 | db/schema.rb
39 | nitrous-post-create.sh
40 | nitrous.json
41 |
--------------------------------------------------------------------------------
/app/assets/javascripts/components/json-editor.js.coffee.erb:
--------------------------------------------------------------------------------
1 | window.setupJsonEditor = ($editors = $(".live-json-editor")) ->
2 | JSONEditor.prototype.ADD_IMG = '<%= image_path 'json-editor/add.png' %>'
3 | JSONEditor.prototype.DELETE_IMG = '<%= image_path 'json-editor/delete.png' %>'
4 | editors = []
5 | $editors.each ->
6 | $editor = $(this)
7 | jsonEditor = new JSONEditor($editor, $editor.data('width') || 400, $editor.data('height') || 500)
8 | jsonEditor.doTruncation true
9 | jsonEditor.showFunctionButtons()
10 | editors.push jsonEditor
11 | return editors
12 |
13 | $ ->
14 | window.jsonEditor = setupJsonEditor()[0]
15 |
--------------------------------------------------------------------------------
/app/controllers/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class OmniauthCallbacksController < Devise::OmniauthCallbacksController
2 | def action_missing(name)
3 | case name.to_sym
4 | when *Devise.omniauth_providers
5 | service = current_user.services.initialize_or_update_via_omniauth(request.env['omniauth.auth'])
6 | if service && service.save
7 | redirect_to services_path, notice: "The service was successfully created."
8 | else
9 | redirect_to services_path, error: "Error creating the service."
10 | end
11 | else
12 | raise ActionController::RoutingError, 'not found'
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/jobs/agent_receive_job.rb:
--------------------------------------------------------------------------------
1 | class AgentReceiveJob < ActiveJob::Base
2 | # Given an Agent id and an array of Event ids, load the Agent, call #receive on it with the Event objects, and then
3 | # save it with an updated `last_receive_at` timestamp.
4 | def perform(agent_id, event_ids)
5 | agent = Agent.find(agent_id)
6 | begin
7 | return if agent.unavailable?
8 | agent.receive(Event.where(:id => event_ids).order(:id))
9 | agent.last_receive_at = Time.now
10 | agent.save!
11 | rescue => e
12 | agent.error "Exception during receive. #{e.message}: #{e.backtrace.join("\n")}"
13 | raise
14 | end
15 | end
16 | end
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/environment.rb:
--------------------------------------------------------------------------------
1 | module Dotenv
2 | # This class inherits from Hash and represents the environemnt into which
3 | # Dotenv will load key value pairs from a file.
4 | class Environment < Hash
5 | attr_reader :filename
6 |
7 | def initialize(filename)
8 | @filename = filename
9 | load
10 | end
11 |
12 | def load
13 | update Parser.call(read)
14 | end
15 |
16 | def read
17 | File.read(@filename)
18 | end
19 |
20 | def apply
21 | each { |k, v| ENV[k] ||= v }
22 | end
23 |
24 | def apply!
25 | each { |k, v| ENV[k] = v }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/jobs/agent_propagate_job.rb:
--------------------------------------------------------------------------------
1 | class AgentPropagateJob < ActiveJob::Base
2 | queue_as :propagation
3 |
4 | def perform
5 | Agent.receive!
6 | end
7 |
8 | def self.can_enqueue?
9 | if Rails.configuration.active_job.queue_adapter == :delayed_job &&
10 | Delayed::Job.where(failed_at: nil, queue: 'propagation').count > 0
11 | return false
12 | elsif Rails.configuration.active_job.queue_adapter == :resque &&
13 | (Resque.size('propagation') > 0 ||
14 | Resque.workers.select { |w| w.job && w.job['queue'] && w.job['queue']['propagation'] }.count > 0)
15 | return false
16 | end
17 | true
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/config/initializers/mysqlpls.rb:
--------------------------------------------------------------------------------
1 | # see https://github.com/rails/rails/issues/9855#issuecomment-28874587
2 | # circumvents the default InnoDB limitation for index prefix bytes maximum when using proper 4byte UTF8 (utf8mb4)
3 | # (for server-side workaround see http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix)
4 | if ENV['ON_HEROKU'].nil?
5 | require 'active_record/connection_adapters/abstract_mysql_adapter'
6 |
7 | module ActiveRecord
8 | module ConnectionAdapters
9 | class AbstractMysqlAdapter
10 | NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 }
11 | end
12 | end
13 | end
14 | end
--------------------------------------------------------------------------------
/spec/data_fixtures/witai.json:
--------------------------------------------------------------------------------
1 | {
2 | "msg_id":"x",
3 | "_text":"x",
4 | "outcomes": [
5 | {
6 | "_text": "set the temperature to 22 degrees at 7 PM",
7 | "intent": "get_temparature",
8 | "entities": {
9 | "datetime": [
10 | {
11 | "grain": "hour",
12 | "type": "value",
13 | "value": "2015-03-27T21:00:00.000-07:00"
14 | }
15 | ],
16 | "temperature": [
17 | {
18 | "type": "value",
19 | "value": 34,
20 | "unit": "degree"
21 | }
22 | ]
23 | },
24 | "confidence": 0.554
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/spec/fixtures/user_credentials.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 23,
4 | "user_id": 30,
5 | "credential_name": "Google_api_key",
6 | "credential_value": "gcperfxrtqymqmluvskxzyiyxxfnjduzncoukyqehkrkamofwz",
7 | "created_at": "2016-04-01 10:50:59 -0700",
8 | "updated_at": "2016-04-01 10:50:59 -0700",
9 | "mode": "text"
10 | },
11 | {
12 | "id": 24,
13 | "user_id": 30,
14 | "credential_name": "twitter_secret_key",
15 | "credential_value": "jhpswiebwhbrnabgkbvczrwcyxblxtyvvlvkhuoudjalcqmlwz",
16 | "created_at": "2016-04-01 10:50:59 -0700",
17 | "updated_at": "2016-04-01 10:50:59 -0700",
18 | "mode": "text"
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/app/helpers/scenario_helper.rb:
--------------------------------------------------------------------------------
1 | module ScenarioHelper
2 |
3 | def style_colors(scenario)
4 | colors = {
5 | color: scenario.tag_fg_color || default_scenario_fg_color,
6 | background_color: scenario.tag_bg_color || default_scenario_bg_color
7 | }.map { |key, value| "#{key.to_s.dasherize}:#{value}" }.join(';')
8 | end
9 |
10 | def scenario_label(scenario, text = nil)
11 | text ||= scenario.name
12 | content_tag :span, text, class: 'label scenario', style: style_colors(scenario)
13 | end
14 |
15 | def default_scenario_bg_color
16 | '#5BC0DE'
17 | end
18 |
19 | def default_scenario_fg_color
20 | '#FFFFFF'
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/db/migrate/20160301113717_add_confirmable_attributes_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddConfirmableAttributesToUsers < ActiveRecord::Migration
2 | def change
3 | change_table(:users) do |t|
4 | ## Confirmable
5 | t.string :confirmation_token
6 | t.datetime :confirmed_at
7 | t.datetime :confirmation_sent_at
8 | t.string :unconfirmed_email # Only if using reconfirmable
9 | end
10 |
11 | add_index :users, :confirmation_token, unique: true
12 |
13 | if ENV['REQUIRE_CONFIRMED_EMAIL'] != 'true' && ActiveRecord::Base.connection.column_exists?(:users, :confirmed_at)
14 | User.update_all('confirmed_at = NOW()')
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/db/migrate/20131227000021_add_cached_dates_to_agent.rb:
--------------------------------------------------------------------------------
1 | class AddCachedDatesToAgent < ActiveRecord::Migration
2 | def up
3 | add_column :agents, :last_event_at, :datetime
4 | execute "UPDATE agents SET last_event_at = (SELECT created_at FROM events WHERE events.agent_id = agents.id ORDER BY id DESC LIMIT 1)"
5 |
6 | add_column :agents, :last_error_log_at, :datetime
7 | execute "UPDATE agents SET last_error_log_at = (SELECT created_at FROM agent_logs WHERE agent_logs.agent_id = agents.id AND agent_logs.level >= 4 ORDER BY id DESC LIMIT 1)"
8 | end
9 |
10 | def down
11 | remove_column :agents, :last_event_at
12 | remove_column :agents, :last_error_log_at
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/data_fixtures/imap2.eml:
--------------------------------------------------------------------------------
1 | From: John
2 | Date: Fri, 9 May 2014 17:00:00 +0900
3 | Message-ID:
4 | Subject: Re: some subject
5 | To: Jane , Nanashi
6 | MIME-Version: 1.0
7 | Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
8 |
9 | --d8c92622e09101e4bc833685557b
10 | Content-Type: text/plain; charset=UTF-8
11 |
12 | Some reply
13 |
14 | --d8c92622e09101e4bc833685557b
15 | Content-Type: text/html; charset=UTF-8
16 | Content-Transfer-Encoding: quoted-printable
17 |
18 | Some HTML reply
19 |
20 | --d8c92622e09101e4bc833685557b--
21 |
--------------------------------------------------------------------------------
/spec/features/toggle_visibility_of_disabled_agents.rb:
--------------------------------------------------------------------------------
1 | require 'capybara_helper'
2 |
3 | describe "Toggling the visibility of an agent", js: true do
4 | it "hides them if they are disabled" do
5 | login_as(users(:bob))
6 | visit("/agents")
7 |
8 | expect {
9 | click_on("Show/Hide Disabled Agents")
10 | }.to change{ find_all(".table-striped tr").count }.by(-1)
11 | end
12 |
13 | it "shows them when they are hidden" do
14 | login_as(users(:bob))
15 | visit("/agents")
16 | click_on("Show/Hide Disabled Agents")
17 |
18 | expect {
19 | click_on("Show/Hide Disabled Agents")
20 | }.to change{ find_all(".table-striped tr").count }.by(1)
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_wells.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Wells
3 | // --------------------------------------------------
4 |
5 |
6 | // Base class
7 | .well {
8 | min-height: 20px;
9 | padding: 19px;
10 | margin-bottom: 20px;
11 | background-color: $well-bg;
12 | border: 1px solid $well-border;
13 | border-radius: $border-radius-base;
14 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
15 | blockquote {
16 | border-color: #ddd;
17 | border-color: rgba(0,0,0,.15);
18 | }
19 | }
20 |
21 | // Sizes
22 | .well-lg {
23 | padding: 24px;
24 | border-radius: $border-radius-large;
25 | }
26 | .well-sm {
27 | padding: 9px;
28 | border-radius: $border-radius-small;
29 | }
30 |
--------------------------------------------------------------------------------
/app/views/agents/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 | <%= render 'form' %>
13 |
14 |
15 |
16 |
17 |
18 |
19 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, agents_path, class: "btn btn-default" %>
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_component-animations.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Component animations
3 | // --------------------------------------------------
4 |
5 | // Heads up!
6 | //
7 | // We don't use the `.opacity()` mixin here since it causes a bug with text
8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.
9 |
10 | .fade {
11 | opacity: 0;
12 | @include transition(opacity .15s linear);
13 | &.in {
14 | opacity: 1;
15 | }
16 | }
17 |
18 | .collapse {
19 | display: none;
20 | &.in {
21 | display: block;
22 | }
23 | }
24 | .collapsing {
25 | position: relative;
26 | height: 0;
27 | overflow: hidden;
28 | @include transition(height .35s ease);
29 | }
30 |
--------------------------------------------------------------------------------
/spec/models/agents/weather_agent_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Agents::WeatherAgent do
4 | let(:agent) do
5 | Agents::WeatherAgent.create(
6 | name: 'weather',
7 | options: {
8 | :location => 94103,
9 | :lat => 37.779329,
10 | :lng => -122.41915,
11 | :api_key => 'test'
12 | }
13 | ).tap do |agent|
14 | agent.user = users(:bob)
15 | agent.save!
16 | end
17 | end
18 |
19 | it "creates a valid agent" do
20 | expect(agent).to be_valid
21 | end
22 |
23 | describe "#service" do
24 | it "doesn't have a Service object attached" do
25 | expect(agent.service).to be_nil
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/importers/default_scenario_importer.rb:
--------------------------------------------------------------------------------
1 | require 'open-uri'
2 | class DefaultScenarioImporter
3 | def self.import(user)
4 | return unless ENV['IMPORT_DEFAULT_SCENARIO_FOR_ALL_USERS'] == 'true'
5 | seed(user)
6 | end
7 |
8 | def self.seed(user)
9 | scenario_import = ScenarioImport.new()
10 | scenario_import.set_user(user)
11 | scenario_file = ENV['DEFAULT_SCENARIO_FILE'].presence || File.join(Rails.root, "data", "default_scenario.json")
12 | begin
13 | scenario_import.file = open(scenario_file)
14 | raise "Import failed" unless scenario_import.valid? && scenario_import.import
15 | ensure
16 | scenario_import.file.close
17 | end
18 | return true
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/lib/time_tracker_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe TimeTracker do
4 | describe "#track" do
5 | it "tracks execution time" do
6 | tracked_result = TimeTracker.track { sleep(0.01) }
7 | expect(tracked_result.elapsed_time).to satisfy {|v| v > 0.01 && v < 0.1}
8 | end
9 |
10 | it "returns the proc return value" do
11 | tracked_result = TimeTracker.track { 42 }
12 | expect(tracked_result.result).to eq(42)
13 | end
14 |
15 | it "returns an object that behaves like the proc result" do
16 | tracked_result = TimeTracker.track { 42 }
17 | expect(tracked_result.to_i).to eq(42)
18 | expect(tracked_result + 1).to eq(43)
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/db/migrate/20130126080736_change_memory_to_long_text.rb:
--------------------------------------------------------------------------------
1 | # PG allows arbitrarily long text fields but MySQL has default limits. Make those limits larger if we're using MySQL.
2 |
3 | class ChangeMemoryToLongText < ActiveRecord::Migration
4 | def up
5 | if mysql?
6 | change_column :agents, :memory, :text, :limit => 4294967295
7 | change_column :events, :payload, :text, :limit => 16777215
8 | end
9 | end
10 |
11 | def down
12 | if mysql?
13 | change_column :agents, :memory, :text, :limit => 65535
14 | change_column :events, :payload, :text, :limit => 65535
15 | end
16 | end
17 |
18 | def mysql?
19 | ActiveRecord::Base.connection.adapter_name =~ /mysql/i
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/app/mailers/system_mailer.rb:
--------------------------------------------------------------------------------
1 | class SystemMailer < ActionMailer::Base
2 | default :from => ENV['EMAIL_FROM_ADDRESS'].presence || 'you@example.com'
3 |
4 | def send_message(options)
5 | @groups = options[:groups]
6 | @headline = options[:headline]
7 | @body = options[:body]
8 |
9 | mail_options = { to: options[:to], subject: options[:subject] }
10 | mail_options[:from] = options[:from] if options[:from].present?
11 | if options[:content_type].present?
12 | mail(mail_options) do |format|
13 | format.text if options[:content_type] == "text/plain"
14 | format.html if options[:content_type] == "text/html"
15 | end
16 | else
17 | mail(mail_options)
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/doc/README.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | ## Installation and upgrading
4 |
5 | ### Docker (not recommended for production)
6 |
7 | - [Check out Huginn with Docker](docker/install.md) Run a local Huginn installation using Docker
8 |
9 | ### Manual installation
10 |
11 | Manual installation instructions which will guide through the steps to install Huginn on any Ubuntu 12.04/14.04 or Debian 6/7 server.
12 |
13 | - [Install](manual/README.md) Requirements, directory structures and installation from source.
14 | - [Update](manual/update.md) Update your installation.
15 | - Deploy updates via [Capistrano](manual/capistrano.md).
16 |
17 | ### Heroku
18 |
19 | - [Deploy to Heroku](heroku/install.md)
20 | - [Update](heroku/update.md) an existing Heroku deployment
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Breadcrumbs
3 | // --------------------------------------------------
4 |
5 |
6 | .breadcrumb {
7 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal;
8 | margin-bottom: $line-height-computed;
9 | list-style: none;
10 | background-color: $breadcrumb-bg;
11 | border-radius: $border-radius-base;
12 |
13 | > li {
14 | display: inline-block;
15 |
16 | + li:before {
17 | content: "#{$breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
18 | padding: 0 5px;
19 | color: $breadcrumb-color;
20 | }
21 | }
22 |
23 | > .active {
24 | color: $breadcrumb-active-color;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/dotenv-rails.gemspec:
--------------------------------------------------------------------------------
1 | require File.expand_path("../lib/dotenv/version", __FILE__)
2 | require "English"
3 |
4 | Gem::Specification.new "dotenv-rails", Dotenv::VERSION do |gem|
5 | gem.authors = ["Brandon Keepers"]
6 | gem.email = ["brandon@opensoul.org"]
7 | gem.description = gem.summary = "Autoload dotenv in Rails."
8 | gem.homepage = "https://github.com/bkeepers/dotenv"
9 | gem.license = "MIT"
10 | gem.files = `git ls-files lib | grep rails`
11 | .split($OUTPUT_RECORD_SEPARATOR) + ["README.md", "LICENSE"]
12 |
13 | gem.add_dependency "dotenv", Dotenv::VERSION
14 |
15 | gem.add_development_dependency "spring"
16 | gem.add_development_dependency "railties", "~>4.0"
17 | end
18 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/data_fixtures/imap1.eml:
--------------------------------------------------------------------------------
1 | From: Nanashi
2 | Date: Fri, 9 May 2014 16:00:00 +0900
3 | Message-ID:
4 | Subject: some subject
5 | To: Jane , John
6 | MIME-Version: 1.0
7 | Content-Type: multipart/alternative; boundary=d8c92622e09101e4bc833685557b
8 |
9 | --d8c92622e09101e4bc833685557b
10 | Content-Type: text/plain; charset=UTF-8
11 |
12 | Some plain text
13 | Some second line
14 |
15 | --d8c92622e09101e4bc833685557b
16 | Content-Type: text/html; charset=UTF-8
17 | Content-Transfer-Encoding: quoted-printable
18 |
19 | Some HTML document
20 | Some second line of HTML
21 |
22 | --d8c92622e09101e4bc833685557b--
23 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/post_stop_ruby-2.0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED*
4 | # immediately before (re)starting or stopping the specified cartridge.
5 | # They are able to make any desired environment variable changes as
6 | # well as other adjustments to the application environment.
7 |
8 | # The post_start_cartridge and post_stop_cartridge hooks are executed
9 | # immediately after (re)starting or stopping the specified cartridge.
10 |
11 | # Exercise caution when adding commands to these hooks. They can
12 | # prevent your application from stopping cleanly or starting at all.
13 | # Application start and stop is subject to different timeouts
14 | # throughout the system.
15 |
16 | echo "-> Post stop ruby step"
--------------------------------------------------------------------------------
/.openshift/action_hooks/pre_stop_ruby-2.0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED*
4 | # immediately before (re)starting or stopping the specified cartridge.
5 | # They are able to make any desired environment variable changes as
6 | # well as other adjustments to the application environment.
7 |
8 | # The post_start_cartridge and post_stop_cartridge hooks are executed
9 | # immediately after (re)starting or stopping the specified cartridge.
10 |
11 | # Exercise caution when adding commands to these hooks. They can
12 | # prevent your application from stopping cleanly or starting at all.
13 | # Application start and stop is subject to different timeouts
14 | # throughout the system.
15 |
16 | echo "-> Pre stop ruby step"
--------------------------------------------------------------------------------
/app/concerns/working_helpers.rb:
--------------------------------------------------------------------------------
1 | module WorkingHelpers
2 | extend ActiveSupport::Concern
3 |
4 | def event_created_within?(days)
5 | last_event_at && last_event_at > days.to_i.days.ago
6 | end
7 |
8 | def recent_error_logs?
9 | last_event_at && last_error_log_at && last_error_log_at > (last_event_at - 2.minutes)
10 | end
11 |
12 | def received_event_without_error?
13 | (last_receive_at.present? && last_error_log_at.blank?) || (last_receive_at.present? && last_error_log_at.present? && last_receive_at > last_error_log_at)
14 | end
15 |
16 | def checked_without_error?
17 | (last_check_at.present? && last_error_log_at.nil?) || (last_check_at.present? && last_error_log_at.present? && last_check_at > last_error_log_at)
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/models/user_credential.rb:
--------------------------------------------------------------------------------
1 | class UserCredential < ActiveRecord::Base
2 | MODES = %w[text java_script]
3 |
4 | attr_accessible :credential_name, :credential_value, :mode
5 |
6 | belongs_to :user
7 |
8 | validates_presence_of :credential_name
9 | validates_presence_of :credential_value
10 | validates_inclusion_of :mode, :in => MODES
11 | validates_presence_of :user_id
12 | validates_uniqueness_of :credential_name, :scope => :user_id
13 |
14 | before_validation :default_mode_to_text
15 | before_save :trim_fields
16 |
17 | protected
18 |
19 | def trim_fields
20 | credential_name.strip!
21 | credential_value.strip!
22 | end
23 |
24 | def default_mode_to_text
25 | self.mode = 'text' unless mode.present?
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/post_start_ruby-2.0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED*
4 | # immediately before (re)starting or stopping the specified cartridge.
5 | # They are able to make any desired environment variable changes as
6 | # well as other adjustments to the application environment.
7 |
8 | # The post_start_cartridge and post_stop_cartridge hooks are executed
9 | # immediately after (re)starting or stopping the specified cartridge.
10 |
11 | # Exercise caution when adding commands to these hooks. They can
12 | # prevent your application from stopping cleanly or starting at all.
13 | # Application start and stop is subject to different timeouts
14 | # throughout the system.
15 |
16 | echo "-> Post start ruby step"
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Resend confirmation instructions
6 |
7 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'form-horizontal' }) do |f| %>
8 | <%= devise_error_messages! %>
9 |
10 |
11 | <%= f.label :email %>
12 | <%= f.email_field :email, autofocus: true, class: 'form-control' %>
13 |
14 |
15 | <%= f.submit "Resend confirmation instructions", class: "btn btn-primary" %>
16 | <% end %>
17 |
18 | <%= render "devise/shared/links" %>
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/views/home/_signed_out_index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Your agents are standing by
7 |
Huginn monitors the world and acts on your behalf.
8 |
9 | <%= link_to "Login", new_user_session_path, :class => "btn btn-large" %>
10 | <%= link_to "Signup", new_user_registration_path, :class => "btn btn-primary btn-large" %>
11 |
12 |
13 |
14 | <%= image_tag 'odin.jpg', :class => 'img-responsive img-rounded', :title => "Wägner, Wilhelm. 1882. Nordisch-germanische Götter und Helden. Otto Spamer, Leipzig & Berlin. Page 7." %>
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docker/single-process/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 | MAINTAINER Dominik Sander
3 |
4 | ADD docker/scripts/prepare /scripts/prepare
5 | RUN /scripts/prepare
6 |
7 | WORKDIR /app
8 |
9 | ADD ["Gemfile", "Gemfile.lock", "/app/"]
10 | ADD lib/gemfile_helper.rb /app/lib/
11 | ADD vendor/gems /app/vendor/gems
12 |
13 | RUN chown -R huginn:huginn /app && \
14 | sudo -u huginn -H echo "gem 'sqlite3', '~> 1.3.11'" >> /app/Gemfile && \
15 | sudo -u huginn -H LC_ALL=en_US.UTF-8 RAILS_ENV=production ON_HEROKU=true bundle install --without test development --path vendor/bundle -j 4
16 | COPY . /app
17 |
18 | ADD ["docker/scripts/setup", "docker/single-process/scripts/init", "/scripts/"]
19 |
20 | RUN /scripts/setup
21 |
22 | EXPOSE 3000
23 |
24 | CMD ["/scripts/init"]
25 |
--------------------------------------------------------------------------------
/spec/support/shared_examples/file_handling_consumer.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | shared_examples_for 'FileHandlingConsumer' do
4 | it 'returns a file pointer' do
5 | expect(@checker.get_file_pointer('testfile')).to eq(file_pointer: { file: "testfile", agent_id: @checker.id})
6 | end
7 |
8 | it 'get_io raises an exception when trying to access an agent of a different user' do
9 | @checker2 = @checker.dup
10 | @checker2.user = users(:bob)
11 | @checker2.save!
12 | expect(@checker2.user.id).not_to eq(@checker.user.id)
13 | event = Event.new(user: @checker.user, payload: {'file_pointer' => {'file' => 'test', 'agent_id' => @checker2.id}})
14 | expect { @checker.get_io(event) }.to raise_error(ActiveRecord::RecordNotFound)
15 | end
16 | end
--------------------------------------------------------------------------------
/app/views/system_mailer/send_message.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <% if @body.present? %>
8 | <%= sanitize @body %>
9 | <% else %>
10 | <% if @headline %>
11 | <%= sanitize @headline %>
12 | <% end %>
13 | <% @groups.each do |group| %>
14 |
15 |
<%= sanitize group[:title] %>
16 | <% group[:entries].each do |entry| %>
17 |
18 | <%= sanitize entry %>
19 |
20 | <% end %>
21 |
22 | <% end %>
23 | <% end %>
24 |
25 |
--------------------------------------------------------------------------------
/spec/lib/delayed_job_worker_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe DelayedJobWorker do
4 | before do
5 | @djw = DelayedJobWorker.new
6 | end
7 |
8 | it "should run" do
9 | mock.instance_of(Delayed::Worker).start
10 | @djw.run
11 | end
12 |
13 | it "should stop" do
14 | mock.instance_of(Delayed::Worker).start
15 | mock.instance_of(Delayed::Worker).stop
16 | @djw.run
17 | @djw.stop
18 | end
19 |
20 | context "#setup_worker" do
21 | it "should return an array with an instance of itself" do
22 | workers = DelayedJobWorker.setup_worker
23 | expect(workers).to be_a(Array)
24 | expect(workers.first).to be_a(DelayedJobWorker)
25 | expect(workers.first.id).to eq('DelayedJobWorker')
26 | end
27 | end
28 | end
--------------------------------------------------------------------------------
/db/migrate/20140722131220_convert_efa_skip_agent.rb:
--------------------------------------------------------------------------------
1 | class ConvertEfaSkipAgent < ActiveRecord::Migration
2 | def up
3 | Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
4 | agent.options_will_change!
5 | unless agent.options.delete('skip_agent').to_s == 'true'
6 | agent.options['instructions'] = {
7 | 'agent' => '{{agent.type}}'
8 | }.update(agent.options['instructions'] || {})
9 | end
10 | agent.save!
11 | end
12 | end
13 |
14 | def down
15 | Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
16 | agent.options_will_change!
17 | agent.options['skip_agent'] = (agent.options['instructions'] || {})['agent'] == '{{agent.type}}'
18 | agent.save!
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/concerns/oauthable.rb:
--------------------------------------------------------------------------------
1 | module Oauthable
2 | extend ActiveSupport::Concern
3 |
4 | included do |base|
5 | @valid_oauth_providers = :all
6 | attr_accessible :service_id
7 | validates_presence_of :service_id
8 | end
9 |
10 | def oauthable?
11 | true
12 | end
13 |
14 | def valid_services_for(user)
15 | if valid_oauth_providers == :all
16 | user.available_services
17 | else
18 | user.available_services.where(provider: valid_oauth_providers)
19 | end
20 | end
21 |
22 | def valid_oauth_providers
23 | self.class.valid_oauth_providers
24 | end
25 |
26 | module ClassMethods
27 | def valid_oauth_providers(*providers)
28 | return @valid_oauth_providers if providers == []
29 | @valid_oauth_providers = providers
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/db/migrate/20140730005210_convert_efa_skip_created_at.rb:
--------------------------------------------------------------------------------
1 | class ConvertEfaSkipCreatedAt < ActiveRecord::Migration
2 | def up
3 | Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
4 | agent.options_will_change!
5 | unless agent.options.delete('skip_created_at').to_s == 'true'
6 | agent.options['instructions'] = {
7 | 'created_at' => '{{created_at}}'
8 | }.update(agent.options['instructions'] || {})
9 | end
10 | agent.save!
11 | end
12 | end
13 |
14 | def down
15 | Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
16 | agent.options_will_change!
17 | agent.options['skip_created_at'] = (agent.options['instructions'] || {})['created_at'] == '{{created_at}}'
18 | agent.save!
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/docker/single-process/docker-compose.yml:
--------------------------------------------------------------------------------
1 | mysqldata:
2 | image: mysql:5.7
3 | command: /bin/true
4 |
5 | mysql:
6 | image: mysql:5.7
7 | volumes_from:
8 | - mysqldata
9 | environment:
10 | MYSQL_ROOT_PASSWORD: myrootpassword
11 | MYSQL_DATABASE: huginn
12 | MYSQL_USER: huginn
13 | MYSQL_PASSWORD: myhuginnpassword
14 |
15 | huginn_web:
16 | image: cantino/huginn-single-process
17 | restart: always
18 | extends:
19 | file: environment.yml
20 | service: huginn_base
21 | ports:
22 | - 3000:3000
23 | links:
24 | - mysql
25 |
26 | huginn_threaded:
27 | image: cantino/huginn-single-process
28 | restart: always
29 | extends:
30 | file: environment.yml
31 | service: huginn_base
32 | links:
33 | - mysql
34 | command: /scripts/init bin/threaded.rb
35 |
36 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/dotenv.gemspec:
--------------------------------------------------------------------------------
1 | require File.expand_path("../lib/dotenv/version", __FILE__)
2 | require "English"
3 |
4 | Gem::Specification.new "dotenv", Dotenv::VERSION do |gem|
5 | gem.authors = ["Brandon Keepers"]
6 | gem.email = ["brandon@opensoul.org"]
7 | gem.description = gem.summary = "Loads environment variables from `.env`."
8 | gem.homepage = "https://github.com/bkeepers/dotenv"
9 | gem.license = "MIT"
10 |
11 | gem.files = `git ls-files README.md LICENSE lib bin | grep -v rails`
12 | .split($OUTPUT_RECORD_SEPARATOR)
13 | gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) }
14 |
15 | gem.add_development_dependency "rake"
16 | gem.add_development_dependency "rspec"
17 | gem.add_development_dependency "rubocop"
18 | end
19 |
--------------------------------------------------------------------------------
/app/views/agents/dry_runs/index.html.erb:
--------------------------------------------------------------------------------
1 | <% if @events && @events.length > 0 %>
2 | Recently received events:
3 | <% @events.each do |event| %>
4 | <%= link_to '#', class: 'dry-run-event-sample', 'data-payload' => event.payload.to_json do %>
5 | <%= truncate event.payload.to_json, :length => 90, :omission => "" %>
6 | <% end %>
7 | <% end %>
8 | <% end %>
9 |
10 | Event to send<%= params[:with_event_mode] == 'maybe' ? ' (Optional)' : '' %>
11 |
21 |
--------------------------------------------------------------------------------
/spec/jobs/agent_propagate_job_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe AgentPropagateJob do
4 | it "calls Agent.receive! when run" do
5 | mock(Agent).receive!
6 | AgentPropagateJob.new.perform
7 | end
8 |
9 | context "#can_enqueue?" do
10 | it "is truthy when no propagation job is queued" do
11 | expect(AgentPropagateJob.can_enqueue?).to be_truthy
12 | end
13 |
14 | it "is falsy when a progation job is queued" do
15 | Delayed::Job.create!(queue: 'propagation')
16 | expect(AgentPropagateJob.can_enqueue?).to be_falsy
17 | end
18 |
19 | it "is truthy when a enqueued progation job failed" do
20 | Delayed::Job.create!(queue: 'propagation', failed_at: Time.now - 1.minute)
21 | expect(AgentPropagateJob.can_enqueue?).to be_truthy
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/.openshift/cron/weekly/README:
--------------------------------------------------------------------------------
1 | Run scripts or jobs on a weekly basis
2 | =====================================
3 | Any scripts or jobs added to this directory will be run on a scheduled basis
4 | (weekly) using run-parts.
5 |
6 | run-parts ignores any files that are hidden or dotfiles (.*) or backup
7 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} and handles
8 | the files named jobs.deny and jobs.allow specially.
9 |
10 | In this specific example, the chronograph script is the only script or job file
11 | executed on a weekly basis (due to white-listing it in jobs.allow). And the
12 | README and chrono.dat file are ignored either as a result of being black-listed
13 | in jobs.deny or because they are NOT white-listed in the jobs.allow file.
14 |
15 | For more details, please see ../README.cron file.
16 |
17 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spec/models/agents/pdf_agent_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Agents::PdfInfoAgent do
4 | let(:agent) do
5 | _agent = Agents::PdfInfoAgent.new(name: "PDF Info Agent")
6 | _agent.user = users(:bob)
7 | _agent.sources << agents(:bob_website_agent)
8 | _agent.save!
9 | _agent
10 | end
11 |
12 | describe "#receive" do
13 | before do
14 | @event = Event.new(payload: {'url' => 'http://mypdf.com'})
15 | end
16 |
17 | it "should call HyPDF" do
18 | expect {
19 | mock(agent).open('http://mypdf.com') { "data" }
20 | mock(HyPDF).pdfinfo('data') { {title: "Huginn"} }
21 | agent.receive([@event])
22 | }.to change { Event.count }.by(1)
23 | event = Event.last
24 | expect(event.payload[:title]).to eq('Huginn')
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/views/agents/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 | <%= render 'form' %>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back', agents_path, class: "btn btn-default" %>
22 | <%= link_to icon_tag('glyphicon-asterisk') + ' Show', agent_path(@agent), class: "btn btn-default" %>
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spec/db/seeds/admin_and_default_scenario_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 | require_relative '../../../db/seeds/seeder'
3 |
4 | describe Seeder do
5 | before do
6 | stub_puts_to_prevent_spew_in_spec_output
7 | end
8 |
9 | describe '.seed' do
10 | it 'imports a default scenario' do
11 | expect { Seeder.seed }.to change(Agent, :count).by(7)
12 | end
13 |
14 | it 'creates an admin' do
15 | expect { Seeder.seed }.to change(User, :count).by(1)
16 | expect(User.last).to be_admin
17 | end
18 |
19 | it 'can be run multiple times and exit normally' do
20 | Seeder.seed
21 | expect { Seeder.seed }.to raise_error(SystemExit)
22 | end
23 | end
24 |
25 | def stub_puts_to_prevent_spew_in_spec_output
26 | stub(Seeder).puts(anything)
27 | stub(Seeder).puts
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/concerns/tumblr_concern.rb:
--------------------------------------------------------------------------------
1 | module TumblrConcern
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | include Oauthable
6 |
7 | valid_oauth_providers :tumblr
8 | end
9 |
10 | def tumblr_consumer_key
11 | ENV['TUMBLR_OAUTH_KEY']
12 | end
13 |
14 | def tumblr_consumer_secret
15 | ENV['TUMBLR_OAUTH_SECRET']
16 | end
17 |
18 | def tumblr_oauth_token
19 | service.token
20 | end
21 |
22 | def tumblr_oauth_token_secret
23 | service.secret
24 | end
25 |
26 | def tumblr
27 | Tumblr.configure do |config|
28 | config.consumer_key = tumblr_consumer_key
29 | config.consumer_secret = tumblr_consumer_secret
30 | config.oauth_token = tumblr_oauth_token
31 | config.oauth_token_secret = tumblr_oauth_token_secret
32 | end
33 |
34 | Tumblr::Client.new
35 | end
36 | end
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/views/admin/users/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for([:admin, @user], html: { class: 'form-horizontal' }) do |f| %>
2 | <%= devise_error_messages! %>
3 | <%= render partial: '/devise/registrations/common_registration_fields', locals: { f: f } %>
4 |
5 |
12 |
13 |
18 | <% end %>
19 |
20 |
21 |
22 |
23 |
24 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, admin_users_path, class: "btn btn-default" %>
25 |
26 |
27 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.assets.enabled = true
4 | Rails.application.config.assets.initialize_on_precompile = false
5 |
6 | # Version of your assets, change this if you want to expire all your assets.
7 | Rails.application.config.assets.version = '1.0'
8 |
9 | # Add additional assets to the asset load path
10 | # Rails.application.config.assets.paths << Emoji.images_path
11 |
12 | # Precompile additional assets (application.js.coffee.erb, application.css, and all non-JS/CSS are already added)
13 | Rails.application.config.assets.precompile += %w( diagram.js graphing.js map_marker.js ace.js )
14 |
15 | Rails.application.config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
16 | Rails.application.config.assets.precompile += %w(*.woff *.eot *.svg *.ttf) # Bootstrap fonts
17 |
--------------------------------------------------------------------------------
/db/migrate/20140603104211_rename_digest_email_to_email_digest.rb:
--------------------------------------------------------------------------------
1 | class RenameDigestEmailToEmailDigest < ActiveRecord::Migration
2 | def up
3 | sql = <<-SQL
4 | UPDATE #{ActiveRecord::Base.connection.quote_table_name('agents')}
5 | SET #{ActiveRecord::Base.connection.quote_column_name('type')} = 'Agents::EmailDigestAgent'
6 | WHERE #{ActiveRecord::Base.connection.quote_column_name('type')} = 'Agents::DigestEmailAgent'
7 | SQL
8 |
9 | execute sql
10 | end
11 |
12 | def down
13 | sql = <<-SQL
14 | UPDATE #{ActiveRecord::Base.connection.quote_table_name('agents')}
15 | SET #{ActiveRecord::Base.connection.quote_column_name('type')} = 'Agents::DigestEmailAgent'
16 | WHERE #{ActiveRecord::Base.connection.quote_column_name('type')} = 'Agents::EmailDigestAgent'
17 | SQL
18 |
19 | execute sql
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/pre_start_ruby-2.0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # The pre_start_cartridge and pre_stop_cartridge hooks are *SOURCED*
4 | # immediately before (re)starting or stopping the specified cartridge.
5 | # They are able to make any desired environment variable changes as
6 | # well as other adjustments to the application environment.
7 |
8 | # The post_start_cartridge and post_stop_cartridge hooks are executed
9 | # immediately after (re)starting or stopping the specified cartridge.
10 |
11 | # Exercise caution when adding commands to these hooks. They can
12 | # prevent your application from stopping cleanly or starting at all.
13 | # Application start and stop is subject to different timeouts
14 | # throughout the system.
15 |
16 | echo "-> Pro start ruby step"
17 |
18 | if [ -f ${OPENSHIFT_REPO_DIR}/.env ]
19 | then
20 | source ${OPENSHIFT_REPO_DIR}/.env
21 | fi
22 |
--------------------------------------------------------------------------------
/spec/controllers/admin/users_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Admin::UsersController do
4 | describe 'POST #create' do
5 | context 'with valid user params' do
6 | it 'imports the default scenario for the new user' do
7 | mock(DefaultScenarioImporter).import(is_a(User))
8 | sign_in users(:jane)
9 | post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
10 | password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false }
11 | end
12 | end
13 |
14 | context 'with invalid user params' do
15 | it 'does not import the default scenario' do
16 | stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
17 | sign_in users(:jane)
18 | post :create, :user => {}
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/concerns/liquid_droppable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe LiquidDroppable do
4 | before do
5 | class DroppableTest
6 | include LiquidDroppable
7 |
8 | def initialize(value)
9 | @value = value
10 | end
11 |
12 | attr_reader :value
13 |
14 | def to_s
15 | "[value:#{value}]"
16 | end
17 | end
18 |
19 | class DroppableTestDrop
20 | def value
21 | @object.value
22 | end
23 | end
24 | end
25 |
26 | describe 'test class' do
27 | it 'should be droppable' do
28 | five = DroppableTest.new(5)
29 | expect(five.to_liquid.class).to eq(DroppableTestDrop)
30 | expect(Liquid::Template.parse('{{ x.value | plus:3 }}').render('x' => five)).to eq('8')
31 | expect(Liquid::Template.parse('{{ x }}').render('x' => five)).to eq('[value:5]')
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/views/agents/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 | <%= render 'agents/table' %>
9 |
10 |
11 |
12 |
13 | <%= link_to icon_tag('glyphicon-plus') + ' New Agent', new_agent_path, class: "btn btn-default" %>
14 | <%= link_to icon_tag('glyphicon-refresh') + ' Run event propagation', propagate_agents_path, method: 'post', class: "btn btn-default" %>
15 | <%= link_to icon_tag('glyphicon-random') + ' View diagram', diagram_path, class: "btn btn-default" %>
16 | <%= link_to icon_tag('glyphicon-adjust') + toggle_disabled_text, toggle_visibility_agents_path, method: :put, class: "btn btn-default visibility-enabler" %>
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spec/controllers/scenario_imports_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe ScenarioImportsController do
4 | before do
5 | sign_in users(:bob)
6 | end
7 |
8 | describe "GET new" do
9 | it "initializes a new ScenarioImport and renders new" do
10 | get :new
11 | expect(assigns(:scenario_import)).to be_a(ScenarioImport)
12 | expect(response).to render_template(:new)
13 | end
14 | end
15 |
16 | describe "POST create" do
17 | it "initializes a ScenarioImport for current_user, passing in params" do
18 | post :create, :scenario_import => { :url => "bad url" }
19 | expect(assigns(:scenario_import).user).to eq(users(:bob))
20 | expect(assigns(:scenario_import).url).to eq("bad url")
21 | expect(assigns(:scenario_import)).not_to be_valid
22 | expect(response).to render_template(:new)
23 | end
24 | end
25 | end
26 |
27 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/cli.rb:
--------------------------------------------------------------------------------
1 | require "dotenv"
2 |
3 | module Dotenv
4 | # The CLI is a class responsible of handling all the command line interface
5 | # logic.
6 | class CLI
7 | attr_reader :argv
8 |
9 | def initialize(argv = [])
10 | @argv = argv.dup
11 | end
12 |
13 | def run
14 | filenames = parse_filenames || []
15 | begin
16 | Dotenv.load!(*filenames)
17 | rescue Errno::ENOENT => e
18 | abort e.message
19 | else
20 | exec(*argv) unless argv.empty?
21 | end
22 | end
23 |
24 | private
25 |
26 | def parse_filenames
27 | pos = argv.index("-f")
28 | return nil unless pos
29 | # drop the -f
30 | argv.delete_at pos
31 | # parse one or more comma-separated .env files
32 | require "csv"
33 | CSV.parse_line argv.delete_at(pos)
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/app/concerns/weibo_concern.rb:
--------------------------------------------------------------------------------
1 | module WeiboConcern
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | gem_dependency_check { defined?(WeiboOAuth2) }
6 |
7 | self.validate :validate_weibo_options
8 | end
9 |
10 | def validate_weibo_options
11 | unless options['app_key'].present? &&
12 | options['app_secret'].present? &&
13 | options['access_token'].present?
14 | errors.add(:base, "app_key, app_secret and access_token are required")
15 | end
16 | end
17 |
18 | def weibo_client
19 | unless @weibo_client
20 | WeiboOAuth2::Config.api_key = options['app_key'] # WEIBO_APP_KEY
21 | WeiboOAuth2::Config.api_secret = options['app_secret'] # WEIBO_APP_SECRET
22 | @weibo_client = WeiboOAuth2::Client.new
23 | @weibo_client.get_token_from_hash :access_token => options['access_token']
24 | end
25 | @weibo_client
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | Huginn Docker images
2 | ====================
3 |
4 | Huginn is packaged in two docker images.
5 |
6 | #### [cantino/huginn](multi-process/README.md) multiple process image
7 |
8 | This image runs all processes needed by Huginn in one container, when the database is not linked it will also start MySQL internally. This is great to try huginn without having to set up anything, however maintenance and backups can be difficult.
9 |
10 | #### [cantino/huginn-single-process](single-process/README.md) multiple container image
11 |
12 | This image runs just one process per container and thus needs at least two container to be started, one for the Huginn application server and one for the threaded background worker. It is also possible to every background worker in a separate container to improve the performance. See [the PostgreSQL docker-compose configuration](single-process/postgresql.yml) for an example.
13 |
--------------------------------------------------------------------------------
/spec/data_fixtures/basecamp.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "creator": {
4 | "fullsize_avatar_url": "https://dge9rmgqjs8m1.cloudfront.net/global/dfsdfsdfdsf/original.gif?r=3",
5 | "avatar_url": "http://dge9rmgqjs8m1.cloudfront.net/global/dfsdfsdfdsf/avatar.gif?r=3",
6 | "name": "Dominik Sander",
7 | "id": 123456
8 | },
9 | "attachments": [],
10 | "raw_excerpt": "test test",
11 | "excerpt": "test test",
12 | "id": 6454342343,
13 | "created_at": "2014-04-17T10:25:31.000+02:00",
14 | "updated_at": "2014-04-17T10:25:31.000+02:00",
15 | "summary": "commented on whaat",
16 | "action": "commented on",
17 | "target": "whaat",
18 | "url": "https://basecamp.com/12456/api/v1/projects/5476464-explore-basecamp/messages/24598238-whaat.json",
19 | "html_url": "https://basecamp.com/12456/projects/5476464-explore-basecamp/messages/24598238-whaat#comment_150756301"
20 | }
21 | ]
22 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/tables.css.scss:
--------------------------------------------------------------------------------
1 | // Sortable table headers
2 | .table th a.selected {
3 | position: relative;
4 | text-decoration: underline;
5 |
6 | &.asc:after, &.desc:after {
7 | text-decoration: none;
8 | position: absolute;
9 | top: 0;
10 | right: -1em;
11 | font-family: FontAwesome;
12 | }
13 |
14 | &.asc:after {
15 | content: '\f0de'; //fa-sort-asc
16 | }
17 |
18 | &.desc:after {
19 | content: '\f0dd'; //fa-sort-desc
20 | }
21 | }
22 |
23 | .table-striped > tbody > tr.hl {
24 | &:nth-child(odd) {
25 | > td, > th {
26 | background-color: #ffeecc;
27 | }
28 | }
29 |
30 | &:nth-child(even) {
31 | > td, > th {
32 | background-color: #f9e8c6;
33 | }
34 | }
35 | }
36 |
37 | table.events {
38 | .payload {
39 | color: #999;
40 | font-size: 12px;
41 | //text-align: center;
42 | font-family: monospace;
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/spec/models/agents/weibo_user_agent_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'rails_helper'
3 |
4 | describe Agents::WeiboUserAgent do
5 | before do
6 | # intercept the twitter API request for @tectonic's user profile
7 | stub_request(:any, /api.weibo.com/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/one_weibo.json")), :status => 200)
8 |
9 | @opts = {
10 | :uid => "123456",
11 | :expected_update_period_in_days => "2",
12 | :app_key => "asdfe",
13 | :app_secret => "asdfe",
14 | :access_token => "asdfe"
15 | }
16 |
17 | @checker = Agents::WeiboUserAgent.new(:name => "123456 fetcher", :options => @opts)
18 | @checker.user = users(:bob)
19 | @checker.save!
20 | end
21 |
22 | describe "#check" do
23 | it "should check for changes" do
24 | expect { @checker.check }.to change { Event.count }.by(1)
25 | end
26 | end
27 |
28 | end
29 |
--------------------------------------------------------------------------------
/docker/multi-process/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:14.04
2 | MAINTAINER Andrew Cantino
3 |
4 | ADD docker/scripts/prepare /scripts/prepare
5 | RUN /scripts/prepare
6 |
7 | ADD docker/multi-process/scripts/standalone-packages /scripts/standalone-packages
8 | RUN /scripts/standalone-packages
9 |
10 | WORKDIR /app
11 |
12 | ADD ["Gemfile", "Gemfile.lock", "/app/"]
13 | ADD lib/gemfile_helper.rb /app/lib/
14 | ADD vendor/gems /app/vendor/gems
15 |
16 | RUN chown -R huginn:huginn /app && \
17 | sudo -u huginn -H echo "gem 'sqlite3', '~> 1.3.11'" >> /app/Gemfile && \
18 | sudo -u huginn -H LC_ALL=en_US.UTF-8 RAILS_ENV=production ON_HEROKU=true bundle install --without test development --path vendor/bundle -j 4
19 | COPY . /app
20 |
21 | ADD ["docker/scripts/setup", "docker/multi-process/scripts/init", "/scripts/"]
22 | RUN /scripts/setup
23 |
24 | VOLUME /var/lib/mysql
25 |
26 | EXPOSE 3000
27 |
28 | CMD ["/scripts/init"]
29 |
30 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_close.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Close icons
3 | // --------------------------------------------------
4 |
5 |
6 | .close {
7 | float: right;
8 | font-size: ($font-size-base * 1.5);
9 | font-weight: $close-font-weight;
10 | line-height: 1;
11 | color: $close-color;
12 | text-shadow: $close-text-shadow;
13 | @include opacity(.2);
14 |
15 | &:hover,
16 | &:focus {
17 | color: $close-color;
18 | text-decoration: none;
19 | cursor: pointer;
20 | @include opacity(.5);
21 | }
22 |
23 | // [converter] extracted button& to button.close
24 | }
25 |
26 | // Additional properties for button version
27 | // iOS requires the button element instead of an anchor tag.
28 | // If you want the anchor version, it requires `href="#"`.
29 | button.close {
30 | padding: 0;
31 | cursor: pointer;
32 | background: transparent;
33 | border: 0;
34 | -webkit-appearance: none;
35 | }
36 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/pre_build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This is a simple script and will be executed on your CI system if
3 | # available. Otherwise it will execute while your application is stopped
4 | # before the build step. This script gets executed directly, so it
5 | # could be python, php, ruby, etc.
6 |
7 | echo "-> Pre-build step"
8 |
9 | STORED_ASSETS="${OPENSHIFT_DATA_DIR}/assets"
10 | LIVE_ASSETS="${OPENSHIFT_REPO_DIR}/public/assets"
11 |
12 | # Ensure our stored assets directory exists
13 | if [ ! -d "${STORED_ASSETS}" ]; then
14 | echo " Creating permanent assets directory"
15 | mkdir "${STORED_ASSETS}"
16 | fi
17 |
18 | # Create symlink to stored assets unless we're uploading our own assets
19 | if [ -d "${LIVE_ASSETS}" ]; then
20 | echo " WARNING: Assets included in git repository, not using stored assets"
21 | else
22 | echo " Restoring stored assets"
23 | ln -s "${STORED_ASSETS}" "${LIVE_ASSETS}"
24 | fi
25 |
--------------------------------------------------------------------------------
/app/assets/javascripts/diagram.js.coffee:
--------------------------------------------------------------------------------
1 | # This is not included in the core application.js bundle.
2 |
3 | $ ->
4 | svg = document.querySelector('.agent-diagram svg.diagram')
5 | overlay = document.querySelector('.agent-diagram .overlay')
6 | $(overlay).width($(svg).width()).height($(svg).height())
7 | getTopLeft = (node) ->
8 | bbox = node.getBBox()
9 | point = svg.createSVGPoint()
10 | point.x = bbox.x + bbox.width
11 | point.y = bbox.y
12 | point.matrixTransform(node.getCTM())
13 | $(svg).find('g.node[data-badge-id]').each ->
14 | tl = getTopLeft(this)
15 | $('#' + this.getAttribute('data-badge-id'), overlay).each ->
16 | badge = $(this)
17 | badge.css
18 | left: tl.x - badge.outerWidth() * (2/3)
19 | top: tl.y - badge.outerHeight() * (1/3)
20 | 'background-color': badge.find('.label').css('background-color')
21 | .show()
22 | return
23 | return
24 |
--------------------------------------------------------------------------------
/db/migrate/20160307085545_warn_about_duplicate_usernames.rb:
--------------------------------------------------------------------------------
1 | class WarnAboutDuplicateUsernames < ActiveRecord::Migration
2 | def up
3 | names = User.group('LOWER(username)').having('count(*) > 1').pluck('LOWER(username)')
4 | if names.length > 0
5 | puts "-----------------------------------------------------"
6 | puts "--------------------- WARNiNG -----------------------"
7 | puts "-------- Found users with duplicate usernames -------"
8 | puts "-----------------------------------------------------"
9 | puts "For the users to log in using their username they have to change it to a unique name"
10 | names.each do |name|
11 | puts
12 | puts "'#{name}' is used multiple times:"
13 | User.where(['LOWER(username) = ?', name]).each do |u|
14 | puts "#{u.id}\t#{u.email}"
15 | end
16 | end
17 | puts
18 | puts
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/doc/deployment/unicorn/production.rb:
--------------------------------------------------------------------------------
1 | app_path = "/home/you/app/current"
2 |
3 | worker_processes 2
4 | preload_app true
5 | timeout 180
6 | listen '/home/you/app/shared/pids/unicorn.socket'
7 |
8 | working_directory app_path
9 |
10 | rails_env = ENV['RAILS_ENV'] || 'production'
11 |
12 | # Log everything to one file
13 | stderr_path "log/unicorn.log"
14 | stdout_path "log/unicorn.log"
15 |
16 | # Set master PID location
17 | pid '/home/you/app/shared/pids/unicorn.pid'
18 |
19 | before_fork do |server, worker|
20 | ActiveRecord::Base.connection.disconnect!
21 | old_pid = "#{server.config[:pid]}.oldbin"
22 | if File.exist?(old_pid) && server.pid != old_pid
23 | begin
24 | Process.kill("QUIT", File.read(old_pid).to_i)
25 | rescue Errno::ENOENT, Errno::ESRCH
26 | # someone else did our job for us
27 | end
28 | end
29 | end
30 |
31 | after_fork do |server, worker|
32 | ActiveRecord::Base.establish_connection
33 | end
34 |
--------------------------------------------------------------------------------
/app/assets/javascripts/pages/user-credential-page.js.coffee:
--------------------------------------------------------------------------------
1 | class @UserCredentialPage
2 | constructor: ->
3 | editor = ace.edit("ace-credential-value")
4 | editor.getSession().setTabSize(2)
5 | editor.getSession().setUseSoftTabs(true)
6 | editor.getSession().setUseWrapMode(false)
7 |
8 | setMode = ->
9 | mode = $("#user_credential_mode").val()
10 | if mode == 'java_script'
11 | editor.getSession().setMode("ace/mode/javascript")
12 | else
13 | editor.getSession().setMode("ace/mode/text")
14 |
15 | setMode()
16 | $("#user_credential_mode").on 'change', setMode
17 |
18 | $textarea = $('#user_credential_credential_value').hide()
19 | editor.getSession().setValue($textarea.val())
20 |
21 | $textarea.closest('form').on 'submit', ->
22 | $textarea.val(editor.getSession().getValue())
23 |
24 | $ ->
25 | Utils.registerPage(UserCredentialPage, forPathsMatching: /^user_credentials\/\d+/)
26 |
--------------------------------------------------------------------------------
/spec/data_fixtures/urlTest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | test
5 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/config/unicorn.rb.example:
--------------------------------------------------------------------------------
1 | wd = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2 |
3 | app_path = wd
4 |
5 | worker_processes 2
6 | preload_app true
7 | timeout 180
8 | listen "#{wd}/tmp/sockets/unicorn.socket"
9 |
10 | working_directory app_path
11 |
12 | rails_env = ENV['RAILS_ENV'] || 'production'
13 |
14 | # Log everything to one file
15 | stderr_path "log/unicorn.log"
16 | stdout_path "log/unicorn.log"
17 |
18 | # Set master PID location
19 | pid "#{wd}/tmp/pids/unicorn.pid"
20 |
21 | before_fork do |server, worker|
22 | ActiveRecord::Base.connection.disconnect!
23 | old_pid = "#{server.config[:pid]}.oldbin"
24 | if File.exist?(old_pid) && server.pid != old_pid
25 | begin
26 | Process.kill("QUIT", File.read(old_pid).to_i)
27 | rescue Errno::ENOENT, Errno::ESRCH
28 | # someone else did our job for us
29 | end
30 | end
31 | end
32 |
33 | after_fork do |server, worker|
34 | ActiveRecord::Base.establish_connection
35 | end
36 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/lib/dotenv/substitutions/variable.rb:
--------------------------------------------------------------------------------
1 | require "English"
2 |
3 | module Dotenv
4 | module Substitutions
5 | # Substitute variables in a value.
6 | #
7 | # HOST=example.com
8 | # URL="https://$HOST"
9 | #
10 | module Variable
11 | class << self
12 | VARIABLE = /
13 | (\\)? # is it escaped with a backslash?
14 | (\$) # literal $
15 | \{? # allow brace wrapping
16 | ([A-Z0-9_]+) # match the variable
17 | \}? # closing brace
18 | /xi
19 |
20 | def call(value, env)
21 | value.gsub(VARIABLE) do |variable|
22 | match = $LAST_MATCH_INFO
23 |
24 | if match[1] == '\\'
25 | variable[1..-1]
26 | else
27 | env.fetch(match[3]) { ENV[match[3]] }
28 | end
29 | end
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/controllers/omniauth_callbacks_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe OmniauthCallbacksController do
4 | before do
5 | sign_in users(:bob)
6 | OmniAuth.config.test_mode = true
7 | request.env["devise.mapping"] = Devise.mappings[:user]
8 | request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/twitter.json')))
9 | end
10 |
11 | describe "accepting a callback url" do
12 | it "should update the user's credentials" do
13 | expect {
14 | get :twitter
15 | }.to change { users(:bob).services.count }.by(1)
16 | end
17 |
18 | # it "should work with an unknown provider (for now)" do
19 | # request.env["omniauth.auth"]['provider'] = 'unknown'
20 | # expect {
21 | # get :unknown
22 | # }.to change { users(:bob).services.count }.by(1)
23 | # expect(users(:bob).services.first.provider).to eq('unknown')
24 | # end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/db/seeds/seeder.rb:
--------------------------------------------------------------------------------
1 | class Seeder
2 | def self.seed
3 | user = User.find_or_initialize_by(:email => ENV['SEED_EMAIL'] || "admin@example.com")
4 | if user.persisted?
5 | puts "User with email '#{user.email}' already exists, not seeding."
6 | exit
7 | end
8 |
9 | user.username = ENV['SEED_USERNAME'] || "admin"
10 | user.password = ENV['SEED_PASSWORD'] || "password"
11 | user.password_confirmation = ENV['SEED_PASSWORD'] || "password"
12 | user.invitation_code = User::INVITATION_CODES.first
13 | user.admin = true
14 | user.save!
15 |
16 | if DefaultScenarioImporter.seed(user)
17 | puts "NOTE: The example 'SF Weather Agent' will not work until you edit it and put in a free API key from http://www.wunderground.com/weather/api/"
18 | puts "See the Huginn Wiki for more Agent examples! https://github.com/cantino/huginn/wiki"
19 | else
20 | raise('Unable to import the default scenario')
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Huginn",
3 | "description": "Build agents that monitor and act on your behalf. Your agents are standing by!",
4 | "website": "https://github.com/cantino/huginn",
5 | "repository": "https://github.com/cantino/huginn",
6 | "logo": "https://raw.githubusercontent.com/cantino/huginn/master/media/huginn-icon-64.png",
7 | "env": {
8 | "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-multi.git",
9 | "APP_SECRET_TOKEN": {
10 | "generator": "secret"
11 | },
12 | "PROCFILE_PATH": "deployment/heroku/Procfile.heroku",
13 | "ON_HEROKU": "true",
14 | "FORCE_SSL": "true",
15 | "INVITATION_CODE": {
16 | "generator": "secret"
17 | },
18 | "USE_GRAPHVIZ_DOT": "dot"
19 | },
20 | "scripts": {
21 | "postdeploy": "bundle exec rake db:migrate"
22 | },
23 | "addons": ["heroku-postgresql"],
24 | "success_url": "/users/sign_up"
25 | }
26 |
--------------------------------------------------------------------------------
/app/concerns/dropbox_concern.rb:
--------------------------------------------------------------------------------
1 | module DropboxConcern
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | include Oauthable
6 | valid_oauth_providers :dropbox
7 | gem_dependency_check { defined?(Dropbox) && Devise.omniauth_providers.include?(:dropbox) }
8 | end
9 |
10 | def dropbox
11 | Dropbox::API::Config.app_key = consumer_key
12 | Dropbox::API::Config.app_secret = consumer_secret
13 | Dropbox::API::Config.mode = 'dropbox'
14 | Dropbox::API::Client.new(token: oauth_token, secret: oauth_token_secret)
15 | end
16 |
17 | private
18 |
19 | def consumer_key
20 | (config = Devise.omniauth_configs[:dropbox]) && config.strategy.consumer_key
21 | end
22 |
23 | def consumer_secret
24 | (config = Devise.omniauth_configs[:dropbox]) && config.strategy.consumer_secret
25 | end
26 |
27 | def oauth_token
28 | service && service.token
29 | end
30 |
31 | def oauth_token_secret
32 | service && service.secret
33 | end
34 |
35 | end
--------------------------------------------------------------------------------
/app/views/devise/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end -%>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%>
8 |
9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%>
12 |
13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%>
16 |
17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%>
20 |
--------------------------------------------------------------------------------
/spec/models/concerns/oauthable.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | module Agents
4 | class OauthableTestAgent < Agent
5 | include Oauthable
6 | end
7 | end
8 |
9 | shared_examples_for Oauthable do
10 | before(:each) do
11 | @agent = described_class.new(:name => "somename")
12 | @agent.user = users(:jane)
13 | end
14 |
15 | it "should be oauthable" do
16 | expect(@agent.oauthable?).to eq(true)
17 | end
18 |
19 | describe "valid_services_for" do
20 | it "should return all available services without specifying valid_oauth_providers" do
21 | @agent = Agents::OauthableTestAgent.new
22 | expect(@agent.valid_services_for(users(:bob)).collect(&:id).sort).to eq([services(:generic), services(:global)].collect(&:id).sort)
23 | end
24 |
25 | it "should filter the services based on the agent defaults" do
26 | expect(@agent.valid_services_for(users(:bob)).to_a).to eq(Service.where(provider: @agent.valid_oauth_providers))
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/db/migrate/20140213053001_add_event_id_at_creation_to_links.rb:
--------------------------------------------------------------------------------
1 | class AddEventIdAtCreationToLinks < ActiveRecord::Migration
2 | class Link < ActiveRecord::Base; end
3 | class Event < ActiveRecord::Base; end
4 |
5 | def up
6 | add_column :links, :event_id_at_creation, :integer, :null => false, :default => 0
7 |
8 | Link.all.find_each do |link|
9 | last_event_id = execute(
10 | <<-SQL
11 | SELECT #{ActiveRecord::Base.connection.quote_column_name('id')}
12 | FROM #{ActiveRecord::Base.connection.quote_table_name('events')}
13 | WHERE events.agent_id = #{link.source_id} ORDER BY events.id DESC limit 1
14 | SQL
15 | ).first.to_a.first
16 | if last_event_id.nil?
17 | link.event_id_at_creation = Event.last.id
18 | else
19 | link.event_id_at_creation = last_event_id
20 | end
21 | link.save
22 | end
23 | end
24 |
25 | def down
26 | remove_column :links, :event_id_at_creation
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/db/migrate/20140723110551_adopt_xpath_in_website_agent.rb:
--------------------------------------------------------------------------------
1 | class AdoptXpathInWebsiteAgent < ActiveRecord::Migration
2 | class Agent < ActiveRecord::Base
3 | include JSONSerializedField
4 | json_serialize :options
5 | end
6 |
7 | def up
8 | Agent.where(type: 'Agents::WebsiteAgent').each do |agent|
9 | extract = agent.options['extract']
10 | next unless extract.is_a?(Hash) && extract.all? { |name, detail|
11 | detail.key?('xpath') || detail.key?('css')
12 | }
13 |
14 | agent.options_will_change!
15 | agent.options['extract'].each { |name, extraction|
16 | case
17 | when extraction.delete('text')
18 | extraction['value'] = './/text()'
19 | when attr = extraction.delete('attr')
20 | extraction['value'] = "@#{attr}"
21 | end
22 | }
23 | agent.save!
24 | end
25 | end
26 |
27 | def down
28 | raise ActiveRecord::IrreversibleMigration, "Cannot revert this migration"
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Resend unlock instructions
5 |
6 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'form-horizontal' }) do |f| %>
7 | <%= devise_error_messages! %>
8 |
9 |
15 |
16 |
21 | <% end %>
22 |
23 |
24 |
25 | <%= render "devise/shared/links" %>
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Huginn",
3 | "icons": [
4 | {
5 | "src": "/android-chrome-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "/android-chrome-72x72.png",
12 | "sizes": "72x72",
13 | "type": "image/png",
14 | "density": "1.5"
15 | },
16 | {
17 | "src": "/android-chrome-144x144.png",
18 | "sizes": "144x144",
19 | "type": "image/png",
20 | "density": "3.0"
21 | },
22 | {
23 | "src": "/android-chrome-48x48.png",
24 | "sizes": "48x48",
25 | "type": "image/png",
26 | "density": "1.0"
27 | },
28 | {
29 | "src": "/android-chrome-96x96.png",
30 | "sizes": "96x96",
31 | "type": "image/png",
32 | "density": "2.0"
33 | },
34 | {
35 | "src": "/android-chrome-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Forgot your password?
5 |
6 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'form-horizontal' }) do |f| %>
7 | <%= devise_error_messages! %>
8 |
9 |
15 |
16 |
21 | <% end %>
22 |
23 |
24 |
25 | <%= render "devise/shared/links" %>
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/spec/models/agents/commander_agent_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Agents::CommanderAgent do
4 | let(:valid_params) {
5 | {
6 | name: 'Example',
7 | schedule: 'every_1h',
8 | options: {
9 | 'action' => 'run',
10 | },
11 | }
12 | }
13 |
14 | let(:agent) {
15 | described_class.create!(valid_params) { |agent|
16 | agent.user = users(:bob)
17 | }
18 | }
19 |
20 | it_behaves_like AgentControllerConcern
21 |
22 | describe "check" do
23 | it "should command targets" do
24 | stub(agent).control!.once { nil }
25 | agent.check
26 | end
27 | end
28 |
29 | describe "receive_events" do
30 | it "should command targets" do
31 | stub(agent).control!.once { nil }
32 |
33 | event = Event.new
34 | event.agent = agents(:bob_rain_notifier_agent)
35 | event.payload = {
36 | 'url' => 'http://xkcd.com',
37 | 'link' => 'Random',
38 | }
39 | agent.receive([event])
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/docker/multi-process/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # This needs at least compose 1.6.0
2 | version: '2'
3 |
4 | services:
5 | mysqldata:
6 | image: mysql:5.7
7 | command: /bin/true
8 |
9 | mysql:
10 | image: mysql:5.7
11 | volumes_from:
12 | - mysqldata
13 | ports:
14 | - "3306:3306"
15 | environment:
16 | MYSQL_ROOT_PASSWORD: myrootpassword
17 | MYSQL_DATABASE: huginn
18 | MYSQL_USER: huginn
19 | MYSQL_PASSWORD: myhuginnpassword
20 |
21 | huginn:
22 | build:
23 | context: ../../
24 | dockerfile: docker/multi-process/Dockerfile
25 | restart: always
26 | environment:
27 | HUGINN_DATABASE_NAME: huginn
28 | HUGINN_DATABASE_USERNAME: root
29 | HUGINN_DATABASE_PASSWORD: myrootpassword
30 | INTENTIONALLY_SLEEP: 10
31 | #DATABASE_INITIAL_CONNECT_MAX_RETRIES: 5
32 | PORT: 3000
33 | MYSQL_PORT_3306_TCP_ADDR: mysql
34 | MYSQL_PORT_3306_TCP_PORT: 3306
35 | ports:
36 | - 3000:3000
37 | links:
38 | - mysql
39 |
--------------------------------------------------------------------------------
/spec/helpers/jobs_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe JobsHelper do
4 | let(:job) { Delayed::Job.new }
5 |
6 | describe '#status' do
7 | it "works for failed jobs" do
8 | job.failed_at = Time.now
9 | expect(status(job)).to eq('failed ')
10 | end
11 |
12 | it "works for running jobs" do
13 | job.locked_at = Time.now
14 | job.locked_by = 'test'
15 | expect(status(job)).to eq('running ')
16 | end
17 |
18 | it "works for queued jobs" do
19 | expect(status(job)).to eq('queued ')
20 | end
21 | end
22 |
23 | describe '#relative_distance_of_time_in_words' do
24 | it "in the past" do
25 | expect(relative_distance_of_time_in_words(Time.now-5.minutes)).to eq('5m ago')
26 | end
27 |
28 | it "in the future" do
29 | expect(relative_distance_of_time_in_words(Time.now+5.minutes)).to eq('in 5m')
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/app/views/scenarios/_enable_agents_modal.html.erb:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/app/controllers/services_controller.rb:
--------------------------------------------------------------------------------
1 | class ServicesController < ApplicationController
2 | include SortableTable
3 |
4 | before_action :upgrade_warning, only: :index
5 |
6 | def index
7 | set_table_sort sorts: %w[provider name global], default: { provider: :asc }
8 |
9 | @services = current_user.services.reorder(table_sort).page(params[:page])
10 |
11 | respond_to do |format|
12 | format.html
13 | format.json { render json: @services }
14 | end
15 | end
16 |
17 | def destroy
18 | @services = current_user.services.find(params[:id])
19 | @services.destroy
20 |
21 | respond_to do |format|
22 | format.html { redirect_to services_path }
23 | format.json { head :no_content }
24 | end
25 | end
26 |
27 | def toggle_availability
28 | @service = current_user.services.find(params[:id])
29 | @service.toggle_availability!
30 |
31 | respond_to do |format|
32 | format.html { redirect_to services_path }
33 | format.json { render json: @service }
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/capybara_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 | require 'capybara/rails'
3 | require 'capybara/poltergeist'
4 | require 'capybara-select2'
5 |
6 | CAPYBARA_TIMEOUT = ENV['CI'] == 'true' ? 60 : 5
7 |
8 | Capybara.register_driver :poltergeist do |app|
9 | Capybara::Poltergeist::Driver.new(app, timeout: CAPYBARA_TIMEOUT)
10 | end
11 |
12 | Capybara.javascript_driver = :poltergeist
13 | Capybara.default_max_wait_time = CAPYBARA_TIMEOUT
14 |
15 | RSpec.configure do |config|
16 | config.include Warden::Test::Helpers
17 | config.include AlertConfirmer, type: :feature
18 |
19 | config.before(:suite) do
20 | Warden.test_mode!
21 | DatabaseCleaner.clean_with(:truncation)
22 | end
23 |
24 | config.before(:each) do |example|
25 | DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
26 | DatabaseCleaner.start
27 | end
28 |
29 | config.after(:each) do
30 | DatabaseCleaner.clean
31 | Warden.test_reset!
32 | end
33 | end
34 |
35 | VCR.configure do |config|
36 | config.ignore_localhost = true
37 | end
38 |
--------------------------------------------------------------------------------
/app/views/scenario_imports/_step_one.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
You can import Scenarios, either from a .json file, or via a public
11 | Scenario URL. When you import a Scenario, Huginn will keep track of where it came from and
12 | later let you update it.
13 |
14 |
15 |
16 |
17 |
18 | <%= f.label :url, 'Option 1: Provide a Public Scenario URL' %>
19 | <%= f.text_field :url, :class => 'form-control', :placeholder => "Public Scenario URL" %>
20 |
21 |
22 |
23 | <%= f.label :file, 'Option 2: Upload a Scenario JSON File' %>
24 | <%= f.file_field :file, :class => 'form-control' %>
25 |
26 |
27 |
28 | <%= f.submit "Start Import", :class => "btn btn-primary" %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/concerns/markdown_class_attributes.rb:
--------------------------------------------------------------------------------
1 | module MarkdownClassAttributes
2 | extend ActiveSupport::Concern
3 |
4 | module ClassMethods
5 | def markdown_class_attributes(*attributes)
6 | attributes.each do |attribute|
7 | class_eval <<-RUBY
8 | def html_#{attribute}
9 | Kramdown::Document.new(#{attribute}, :auto_ids => false).to_html.html_safe
10 | end
11 |
12 | def #{attribute}
13 | if self.class.#{attribute}.is_a?(Proc)
14 | Utils.unindent(self.instance_eval(&self.class.#{attribute}) || "No #{attribute} has been set.")
15 | else
16 | Utils.unindent(self.class.#{attribute} || "No #{attribute} has been set.")
17 | end
18 | end
19 |
20 | def self.#{attribute}(value = nil, &block)
21 | if block
22 | @#{attribute} = block
23 | elsif value
24 | @#{attribute} = value
25 | end
26 | @#{attribute}
27 | end
28 | RUBY
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/spec/models/user_credential_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe UserCredential do
4 | describe "validation" do
5 | it { should validate_uniqueness_of(:credential_name).scoped_to(:user_id) }
6 | it { should validate_presence_of(:credential_name) }
7 | it { should validate_presence_of(:credential_value) }
8 | it { should validate_presence_of(:user_id) }
9 | end
10 |
11 | describe "mass assignment" do
12 | it { should allow_mass_assignment_of :credential_name }
13 |
14 | it { should allow_mass_assignment_of :credential_value }
15 |
16 | it { should_not allow_mass_assignment_of :user_id }
17 | end
18 |
19 | describe "cleaning fields" do
20 | it "should trim whitespace" do
21 | user_credential = user_credentials(:bob_aws_key)
22 | user_credential.credential_name = " new name "
23 | user_credential.credential_value = " new value "
24 | user_credential.save!
25 | expect(user_credential.credential_name).to eq("new name")
26 | expect(user_credential.credential_value).to eq("new value")
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/.openshift/action_hooks/deploy:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This deploy hook gets executed after dependencies are resolved and the
3 | # build hook has been run but before the application has been started back
4 | # up again. This script gets executed directly, so it could be python, php,
5 | # ruby, etc.
6 |
7 | echo "-> Deploy step"
8 |
9 | pushd ${OPENSHIFT_REPO_DIR} > /dev/null
10 |
11 | echo "DATABASE_NAME=${OPENSHIFT_APP_NAME}" > ${OPENSHIFT_REPO_DIR}/.env
12 | echo "DATABASE_USERNAME=${OPENSHIFT_MYSQL_DB_USERNAME}" >> ${OPENSHIFT_REPO_DIR}/.env
13 | echo "DATABASE_PASSWORD=${OPENSHIFT_MYSQL_DB_PASSWORD}" >> ${OPENSHIFT_REPO_DIR}/.env
14 | echo "DATABASE_HOST=${OPENSHIFT_MYSQL_DB_HOST}" >> ${OPENSHIFT_REPO_DIR}/.env
15 | echo "DATABASE_PORT=${OPENSHIFT_MYSQL_DB_PORT}" >> ${OPENSHIFT_REPO_DIR}/.env
16 | echo "DATABASE_SOCKET=${OPENSHIFT_MYSQL_DB_SOCKET}" >> ${OPENSHIFT_REPO_DIR}/.env
17 |
18 | chmod ugo+r ${OPENSHIFT_REPO_DIR}/.env
19 |
20 | source ${OPENSHIFT_REPO_DIR}/.env
21 |
22 | gem install bundler
23 | echo "Migrating"
24 | RAILS_ENV="production" bundle exec rake db:migrate
25 | popd > /dev/null
26 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_thumbnails.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Thumbnails
3 | // --------------------------------------------------
4 |
5 |
6 | // Mixin and adjust the regular image class
7 | .thumbnail {
8 | display: block;
9 | padding: $thumbnail-padding;
10 | margin-bottom: $line-height-computed;
11 | line-height: $line-height-base;
12 | background-color: $thumbnail-bg;
13 | border: 1px solid $thumbnail-border;
14 | border-radius: $thumbnail-border-radius;
15 | @include transition(all .2s ease-in-out);
16 |
17 | > img,
18 | a > img {
19 | @include img-responsive();
20 | margin-left: auto;
21 | margin-right: auto;
22 | }
23 |
24 | // [converter] extracted a&:hover, a&:focus, a&.active to a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active
25 |
26 | // Image captions
27 | .caption {
28 | padding: $thumbnail-caption-padding;
29 | color: $thumbnail-caption-color;
30 | }
31 | }
32 |
33 | // Add a hover state for linked versions only
34 | a.thumbnail:hover,
35 | a.thumbnail:focus,
36 | a.thumbnail.active {
37 | border-color: $link-color;
38 | }
39 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_utilities.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Utility classes
3 | // --------------------------------------------------
4 |
5 |
6 | // Floats
7 | // -------------------------
8 |
9 | .clearfix {
10 | @include clearfix();
11 | }
12 | .center-block {
13 | @include center-block();
14 | }
15 | .pull-right {
16 | float: right !important;
17 | }
18 | .pull-left {
19 | float: left !important;
20 | }
21 |
22 |
23 | // Toggling content
24 | // -------------------------
25 |
26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1
27 | .hide {
28 | display: none !important;
29 | }
30 | .show {
31 | display: block !important;
32 | }
33 | .invisible {
34 | visibility: hidden;
35 | }
36 | .text-hide {
37 | @include text-hide();
38 | }
39 |
40 |
41 | // Hide from screenreaders and browsers
42 | //
43 | // Credit: HTML5 Boilerplate
44 |
45 | .hidden {
46 | display: none !important;
47 | visibility: hidden !important;
48 | }
49 |
50 |
51 | // For Affix plugin
52 | // -------------------------
53 |
54 | .affix {
55 | position: fixed;
56 | }
57 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/jquery.json-editor.css:
--------------------------------------------------------------------------------
1 | .json-editor {
2 | background-color: #FFF;
3 | position: relative; }
4 | .json-editor textarea {
5 | width: 100%;
6 | font-family: monospace; }
7 | .json-editor .builder {
8 | background-color: white;
9 | overflow: auto;
10 | font-size: 0.9em; }
11 | .json-editor .builder .key {
12 | font-weight: bold; }
13 | .json-editor .builder .key .edit_field {
14 | width: 150px; }
15 | .json-editor .builder .val .edit_field {
16 | width: 200px; }
17 | .json-editor blockquote {
18 | margin: 0;
19 | padding: 0;
20 | clear: both;
21 | padding-left: 7px; }
22 | .json-editor div {
23 | background-color: #cfc;
24 | margin: 1px;
25 | padding: 2px; }
26 | .json-editor .val {
27 | font-style: italic; }
28 | .json-editor .key a, .json-editor .val a {
29 | color: black;
30 | text-decoration: none; }
31 | .json-editor .icon {
32 | display: block;
33 | float: right;
34 | text-decoration: none;
35 | padding-left: 5px;
36 | border: 0;
37 | color: blue; }
38 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/_common_registration_fields.html.erb:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
15 |
22 |
23 |
29 |
--------------------------------------------------------------------------------
/spec/controllers/users/registrations_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | module Users
4 | describe RegistrationsController do
5 | include Devise::TestHelpers
6 |
7 | describe "POST create" do
8 | context 'with valid params' do
9 | it "imports the default scenario for the new user" do
10 | mock(DefaultScenarioImporter).import(is_a(User))
11 |
12 | @request.env["devise.mapping"] = Devise.mappings[:user]
13 | post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
14 | password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false, invitation_code: 'try-huginn'}
15 | end
16 | end
17 |
18 | context 'with invalid params' do
19 | it "does not import the default scenario" do
20 | stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
21 |
22 | @request.env["devise.mapping"] = Devise.mappings[:user]
23 | setup_controller_for_warden
24 | post :create, :user => {}
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/views/scenario_imports/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <% if @scenario_import.errors.any? %>
5 |
6 |
<%= pluralize(@scenario_import.errors.count, "error") %> prohibited this Scenario from being imported:
7 | <% @scenario_import.errors.full_messages.each do |msg| %>
8 |
<%= msg %>
9 | <% end %>
10 |
11 | <% end %>
12 |
13 |
14 |
15 | <%= form_for @scenario_import, :multipart => true do |f| %>
16 | <%= f.hidden_field :data %>
17 |
18 | <% if @scenario_import.step_one? %>
19 | <%= render 'step_one', :f => f %>
20 | <% elsif @scenario_import.step_two? %>
21 | <%= render 'step_two', :f => f %>
22 | <% end %>
23 | <% end %>
24 |
25 |
26 |
27 |
28 |
29 | <%= link_to icon_tag('glyphicon-chevron-left') + ' Back'.html_safe, scenarios_path, class: "btn btn-default" %>
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_jumbotron.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Jumbotron
3 | // --------------------------------------------------
4 |
5 |
6 | .jumbotron {
7 | padding: $jumbotron-padding;
8 | margin-bottom: $jumbotron-padding;
9 | color: $jumbotron-color;
10 | background-color: $jumbotron-bg;
11 |
12 | h1,
13 | .h1 {
14 | color: $jumbotron-heading-color;
15 | }
16 | p {
17 | margin-bottom: ($jumbotron-padding / 2);
18 | font-size: $jumbotron-font-size;
19 | font-weight: 200;
20 | }
21 |
22 | .container & {
23 | border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container
24 | }
25 |
26 | .container {
27 | max-width: 100%;
28 | }
29 |
30 | @media screen and (min-width: $screen-sm-min) {
31 | padding-top: ($jumbotron-padding * 1.6);
32 | padding-bottom: ($jumbotron-padding * 1.6);
33 |
34 | .container & {
35 | padding-left: ($jumbotron-padding * 2);
36 | padding-right: ($jumbotron-padding * 2);
37 | }
38 |
39 | h1,
40 | .h1 {
41 | font-size: ($font-size-base * 4.5);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_media.scss:
--------------------------------------------------------------------------------
1 | // Media objects
2 | // Source: http://stubbornella.org/content/?p=497
3 | // --------------------------------------------------
4 |
5 |
6 | // Common styles
7 | // -------------------------
8 |
9 | // Clear the floats
10 | .media,
11 | .media-body {
12 | overflow: hidden;
13 | zoom: 1;
14 | }
15 |
16 | // Proper spacing between instances of .media
17 | .media,
18 | .media .media {
19 | margin-top: 15px;
20 | }
21 | .media:first-child {
22 | margin-top: 0;
23 | }
24 |
25 | // For images and videos, set to block
26 | .media-object {
27 | display: block;
28 | }
29 |
30 | // Reset margins on headings for tighter default spacing
31 | .media-heading {
32 | margin: 0 0 5px;
33 | }
34 |
35 |
36 | // Media image alignment
37 | // -------------------------
38 |
39 | .media {
40 | > .pull-left {
41 | margin-right: 10px;
42 | }
43 | > .pull-right {
44 | margin-left: 10px;
45 | }
46 | }
47 |
48 |
49 | // Media list variation
50 | // -------------------------
51 |
52 | // Undo default ul/ol styles
53 | .media-list {
54 | padding-left: 0;
55 | list-style: none;
56 | }
57 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/_pager.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Pager pagination
3 | // --------------------------------------------------
4 |
5 |
6 | .pager {
7 | padding-left: 0;
8 | margin: $line-height-computed 0;
9 | list-style: none;
10 | text-align: center;
11 | @include clearfix();
12 | li {
13 | display: inline;
14 | > a,
15 | > span {
16 | display: inline-block;
17 | padding: 5px 14px;
18 | background-color: $pager-bg;
19 | border: 1px solid $pager-border;
20 | border-radius: $pager-border-radius;
21 | }
22 |
23 | > a:hover,
24 | > a:focus {
25 | text-decoration: none;
26 | background-color: $pager-hover-bg;
27 | }
28 | }
29 |
30 | .next {
31 | > a,
32 | > span {
33 | float: right;
34 | }
35 | }
36 |
37 | .previous {
38 | > a,
39 | > span {
40 | float: left;
41 | }
42 | }
43 |
44 | .disabled {
45 | > a,
46 | > a:hover,
47 | > a:focus,
48 | > span {
49 | color: $pager-disabled-color;
50 | background-color: $pager-bg;
51 | cursor: not-allowed;
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/bootstrap/bootstrap.scss:
--------------------------------------------------------------------------------
1 | // Core variables and mixins
2 | @import "variables";
3 | @import "mixins";
4 |
5 | // Reset
6 | @import "normalize";
7 | @import "print";
8 |
9 | // Core CSS
10 | @import "scaffolding";
11 | @import "type";
12 | @import "code";
13 | @import "grid";
14 | @import "tables";
15 | @import "forms";
16 | @import "buttons";
17 |
18 | // Components
19 | @import "component-animations";
20 | @import "glyphicons";
21 | @import "dropdowns";
22 | @import "button-groups";
23 | @import "input-groups";
24 | @import "navs";
25 | @import "navbar";
26 | @import "breadcrumbs";
27 | @import "pagination";
28 | @import "pager";
29 | @import "labels";
30 | @import "badges";
31 | @import "jumbotron";
32 | @import "thumbnails";
33 | @import "alerts";
34 | @import "progress-bars";
35 | @import "media";
36 | @import "list-group";
37 | @import "panels";
38 | @import "wells";
39 | @import "close";
40 |
41 | // Components w/ JavaScript
42 | @import "modals";
43 | @import "tooltip";
44 | @import "popovers";
45 | @import "carousel";
46 |
47 | // Utility classes
48 | @import "utilities";
49 | @import "responsive-utilities";
50 |
--------------------------------------------------------------------------------
/app/controllers/worker_status_controller.rb:
--------------------------------------------------------------------------------
1 | class WorkerStatusController < ApplicationController
2 | def show
3 | start = Time.now
4 | events = current_user.events
5 |
6 | if params[:since_id].present?
7 | since_id = params[:since_id].to_i
8 | events = events.where('id > ?', since_id)
9 | end
10 |
11 | result = events.select('COUNT(id) AS count', 'MIN(id) AS min_id', 'MAX(id) AS max_id').reorder('min(created_at)').first
12 | count, min_id, max_id = result.count, result.min_id, result.max_id
13 |
14 | case max_id
15 | when nil
16 | when min_id
17 | events_url = events_path(hl: max_id)
18 | else
19 | events_url = events_path(hl: "#{min_id}-#{max_id}")
20 | end
21 |
22 | render json: {
23 | pending: Delayed::Job.pending.where("run_at <= ?", start).count,
24 | awaiting_retry: Delayed::Job.awaiting_retry.count,
25 | recent_failures: Delayed::Job.failed.where('failed_at > ?', 5.days.ago).count,
26 | event_count: count,
27 | max_id: max_id || 0,
28 | events_url: events_url,
29 | compute_time: Time.now - start
30 | }
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/spec/data_fixtures/services/37signals.json:
--------------------------------------------------------------------------------
1 | {
2 | "provider": "37signals",
3 | "uid": 12345,
4 | "info": {
5 | "email": "basecamp@none.de",
6 | "first_name": "Dominik",
7 | "last_name": "Sander",
8 | "name": "Dominik Sander"
9 | },
10 | "credentials": {
11 | "token": "abcde",
12 | "refresh_token": "fghrefresh",
13 | "expires_at": 1401554352,
14 | "expires": true
15 | },
16 | "extra": {
17 | "accounts": [
18 | {
19 | "product": "bcx",
20 | "name": "Dominik Sander's Basecamp",
21 | "id": 12345,
22 | "href": "https://basecamp.com/12345/api/v1"
23 | }
24 | ],
25 | "raw_info": {
26 | "expires_at": "2014-05-31T16:39:12Z",
27 | "identity": {
28 | "first_name": "Dominik",
29 | "last_name": "Sander",
30 | "email_address": "basecamp@none.de",
31 | "id": 12345
32 | },
33 | "accounts": [
34 | {
35 | "product": "bcx",
36 | "name": "Dominik Sander's Basecamp",
37 | "id": 12345,
38 | "href": "https://basecamp.com/12345/api/v1"
39 | }
40 | ]
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013, Andrew Cantino (Iteration Labs, LLC)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/initializers/delayed_job.rb:
--------------------------------------------------------------------------------
1 | Delayed::Worker.destroy_failed_jobs = false
2 | Delayed::Worker.max_attempts = 5
3 | Delayed::Worker.max_run_time = (ENV['DELAYED_JOB_MAX_RUNTIME'].presence || 2).to_i.minutes
4 | Delayed::Worker.read_ahead = 5
5 | Delayed::Worker.default_priority = 10
6 | Delayed::Worker.delay_jobs = !Rails.env.test?
7 | Delayed::Worker.sleep_delay = (ENV['DELAYED_JOB_SLEEP_DELAY'].presence || 10).to_f
8 | Delayed::Worker.logger = Rails.logger
9 |
10 | # Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log'))
11 | # Delayed::Worker.logger.level = Logger::DEBUG
12 |
13 | class Delayed::Job
14 | scope :pending, -> { where("locked_at IS NULL AND attempts = 0") }
15 | scope :awaiting_retry, -> { where("failed_at IS NULL AND attempts > 0 AND locked_at IS NULL") }
16 | scope :failed, -> { where("failed_at IS NOT NULL") }
17 | end
18 |
19 | def database_deadlocks_when_using_optimized_strategy?
20 | ENV["DATABASE_ADAPTER"] == "mysql2"
21 | end
22 |
23 | if database_deadlocks_when_using_optimized_strategy?
24 | Delayed::Backend::ActiveRecord.configure do |config|
25 | config.reserve_sql_strategy = :default_sql
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/vendor/gems/dotenv-2.0.1/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Brandon Keepers
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/app/assets/javascripts/components/search.js.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | $agentNavigate = $('#agent-navigate')
3 |
4 | # initialize typeahead listener
5 | $agentNavigate.bind "typeahead:selected", (event, object, name) ->
6 | item = object['value']
7 | $agentNavigate.typeahead('val', '')
8 | if window.agentPaths[item]
9 | $(".spinner").show()
10 | navigationData = window.agentPaths[item]
11 | if !(navigationData instanceof Object) || !navigationData.method || navigationData.method == 'GET'
12 | window.location = navigationData.url || navigationData
13 | else
14 | $(" ").appendTo($("body")).click()
15 |
16 | # substring matcher for typeahead
17 | substringMatcher = (strings) ->
18 | findMatches = (query, callback) ->
19 | matches = []
20 | substrRegex = new RegExp(query, "i")
21 | $.each strings, (i, str) ->
22 | matches.push value: str if substrRegex.test(str)
23 | callback(matches.slice(0,6))
24 |
25 | $agentNavigate.typeahead
26 | minLength: 1,
27 | highlight: true,
28 | ,
29 | source: substringMatcher(window.agentNames)
30 |
--------------------------------------------------------------------------------
/app/controllers/events_controller.rb:
--------------------------------------------------------------------------------
1 | class EventsController < ApplicationController
2 | before_action :load_event, except: :index
3 |
4 | def index
5 | if params[:agent_id]
6 | @agent = current_user.agents.find(params[:agent_id])
7 | @events = @agent.events.page(params[:page])
8 | else
9 | @events = current_user.events.preload(:agent).page(params[:page])
10 | end
11 |
12 | respond_to do |format|
13 | format.html
14 | format.json { render json: @events }
15 | end
16 | end
17 |
18 | def show
19 | respond_to do |format|
20 | format.html
21 | format.json { render json: @event }
22 | end
23 | end
24 |
25 | def reemit
26 | @event.reemit!
27 | respond_to do |format|
28 | format.html { redirect_back event_path(@event), notice: 'Event re-emitted.' }
29 | end
30 | end
31 |
32 | def destroy
33 | @event.destroy
34 |
35 | respond_to do |format|
36 | format.html { redirect_back events_path, notice: 'Event deleted.' }
37 | format.json { head :no_content }
38 | end
39 | end
40 |
41 | private
42 |
43 | def load_event
44 | @event = current_user.events.find(params[:id])
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/.openshift/cron/README.cron:
--------------------------------------------------------------------------------
1 | Run scripts or jobs on a periodic basis
2 | =======================================
3 | Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly
4 | directories will be run on a scheduled basis (frequency is as indicated by the
5 | name of the directory) using run-parts.
6 |
7 | run-parts ignores any files that are hidden or dotfiles (.*) or backup
8 | files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved}
9 |
10 | The presence of two specially named files jobs.deny and jobs.allow controls
11 | how run-parts executes your scripts/jobs.
12 | jobs.deny ===> Prevents specific scripts or jobs from being executed.
13 | jobs.allow ===> Only execute the named scripts or jobs (all other/non-named
14 | scripts that exist in this directory are ignored).
15 |
16 | The principles of jobs.deny and jobs.allow are the same as those of cron.deny
17 | and cron.allow and are described in detail at:
18 | http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access
19 |
20 | See: man crontab or above link for more details and see the the weekly/
21 | directory for an example.
22 |
23 |
--------------------------------------------------------------------------------
/db/migrate/20160108221620_website_agent_does_not_use_event_url.rb:
--------------------------------------------------------------------------------
1 | class WebsiteAgentDoesNotUseEventUrl < ActiveRecord::Migration
2 | def up
3 | # Until this migration, if a WebsiteAgent received Events and did not have a `url_from_event` option set,
4 | # it would use the `url` from the Event's payload. If the Event did not have a `url` in its payload, the
5 | # WebsiteAgent would do nothing. This migration assumes that if someone has wired a WebsiteAgent to receive Events
6 | # and has not set `url_from_event` or `data_from_event`, they were trying to use the Event's `url` payload, so we
7 | # set `url_from_event` to `{{ url }}` for them.
8 | Agents::WebsiteAgent.find_each do |agent|
9 | next unless agent.sources.count > 0
10 |
11 | if !agent.options['data_from_event'].present? && !agent.options['url_from_event'].present?
12 | agent.options['url_from_event'] = '{{ url }}'
13 | agent.save!
14 | puts ">> Setting `url_from_event` on WebsiteAgent##{agent.id} to {{ url }} because it is wired"
15 | puts ">> to receive Events, and the WebsiteAgent no longer uses the Event's `url` value directly."
16 | end
17 | end
18 | end
19 |
20 | def down
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/assets/javascripts/map_marker.js.coffee:
--------------------------------------------------------------------------------
1 | window.map_marker = (map, options = {}) ->
2 | pos = new google.maps.LatLng(options.lat, options.lng)
3 |
4 | if options.radius > 0
5 | marker = new google.maps.Circle
6 | map: map
7 | strokeColor: '#FF0000'
8 | strokeOpacity: 0.8
9 | strokeWeight: 2
10 | fillColor: '#FF0000'
11 | fillOpacity: 0.35
12 | center: pos
13 | radius: options.radius
14 | return marker
15 | else
16 | marker = new google.maps.Marker
17 | map: map
18 | position: pos
19 | title: 'Recorded Location'
20 | return marker
21 |
22 | if options.course
23 | p1 = new LatLon(pos.lat(), pos.lng())
24 | speed = options.speed ? 1
25 | p2 = p1.destinationPoint(options.course, Math.max(0.2, speed) * 0.1)
26 |
27 | lineCoordinates = [
28 | pos
29 | new google.maps.LatLng(p2.lat(), p2.lon())
30 | ]
31 |
32 | lineSymbol =
33 | path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW
34 |
35 | arrow = new google.maps.Polyline
36 | map: map
37 | path: lineCoordinates
38 | icons: [
39 | {
40 | icon: lineSymbol
41 | offset: '100%'
42 | }
43 | ]
44 |
45 | return arrow
46 |
--------------------------------------------------------------------------------
/app/views/agents/dry_runs/create.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
<%= Utils.pretty_print(@results[:events], false) %>
11 |
12 |
13 |
<%= @results[:log] %>
14 |
15 |
16 |
<%= Utils.pretty_print(@results[:memory], false) %>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/spec/helpers/scenario_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe ScenarioHelper do
4 | let(:scenario) { users(:bob).scenarios.build(name: 'Scene', tag_fg_color: '#AAAAAA', tag_bg_color: '#000000') }
5 |
6 | describe '#style_colors' do
7 | it 'returns a css style-formated version of the scenario foreground and background colors' do
8 | expect(style_colors(scenario)).to eq("color:#AAAAAA;background-color:#000000")
9 | end
10 |
11 | it 'defauls foreground and background colors' do
12 | scenario.tag_fg_color = nil
13 | scenario.tag_bg_color = nil
14 | expect(style_colors(scenario)).to eq("color:#FFFFFF;background-color:#5BC0DE")
15 | end
16 | end
17 |
18 | describe '#scenario_label' do
19 | it 'creates a scenario label with the scenario name' do
20 | expect(scenario_label(scenario)).to eq(
21 | 'Scene '
22 | )
23 | end
24 |
25 | it 'creates a scenario label with the given text' do
26 | expect(scenario_label(scenario, 'Other')).to eq(
27 | 'Other '
28 | )
29 | end
30 | end
31 |
32 | end
33 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 |
2 | guard 'livereload' do
3 | watch(%r{app/views/.+\.(erb|haml|slim)$})
4 | watch(%r{app/helpers/.+\.rb})
5 | watch(%r{public/.+\.(css|js|html)})
6 | watch(%r{config/locales/.+\.yml})
7 | # Rails Assets Pipeline
8 | watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" }
9 | end
10 |
11 | guard :rspec, cmd: ENV['SPRING'] ? 'bundle exec spring rspec' : 'bundle exec rspec' do
12 | watch(%r{^spec/.+_spec\.rb$})
13 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
14 | watch('spec/spec_helper.rb') { "spec" }
15 |
16 | # Rails example
17 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
18 | watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
19 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
20 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
21 | watch('config/routes.rb') { "spec/routing" }
22 | watch('app/controllers/application_controller.rb') { "spec/controllers" }
23 | watch('spec/rails_helper.rb') { "spec" }
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/jquery.serializeObject.js:
--------------------------------------------------------------------------------
1 | //
2 | // Use internal $.serializeArray to get list of form elements which is
3 | // consistent with $.serialize
4 | //
5 | // From version 2.0.0, $.serializeObject will stop converting [name] values
6 | // to camelCase format. This is *consistent* with other serialize methods:
7 | //
8 | // - $.serialize
9 | // - $.serializeArray
10 | //
11 | // If you require camel casing, you can either download version 1.0.4 or map
12 | // them yourself.
13 | //
14 |
15 | (function($){
16 | $.fn.serializeObject = function () {
17 | "use strict";
18 |
19 | var result = {};
20 | var extend = function (i, element) {
21 | var node = result[element.name];
22 |
23 | // If node with same name exists already, need to convert it to an array as it
24 | // is a multi-value field (i.e., checkboxes)
25 |
26 | if ('undefined' !== typeof node && node !== null) {
27 | if ($.isArray(node)) {
28 | node.push(element.value);
29 | } else {
30 | result[element.name] = [node, element.value];
31 | }
32 | } else {
33 | result[element.name] = element.value;
34 | }
35 | };
36 |
37 | $.each(this.serializeArray(), extend);
38 | return result;
39 | };
40 | })(jQuery);
41 |
--------------------------------------------------------------------------------
/spec/fixtures/multiple_user_credentials.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 23,
4 | "user_id": 30,
5 | "credential_name": "Google_api_key",
6 | "credential_value": "gcperfxrtqymqmluvskxzyiyxxfnjduzncoukyqehkrkamofwz",
7 | "created_at": "2016-04-01 10:50:59 -0700",
8 | "updated_at": "2016-04-01 10:50:59 -0700",
9 | "mode": "text"
10 | },
11 | {
12 | "id": 24,
13 | "user_id": 30,
14 | "credential_name": "twitter_secret_key",
15 | "credential_value": "jhpswiebwhbrnabgkbvczrwcyxblxtyvvlvkhuoudjalcqmlwz",
16 | "created_at": "2016-04-01 10:50:59 -0700",
17 | "updated_at": "2016-04-01 10:50:59 -0700",
18 | "mode": "text"
19 | },
20 | {
21 | "id": 23,
22 | "user_id": 30,
23 | "credential_name": "Google_api_key",
24 | "credential_value": "gcperfxrtqymqmluvskxzyiyxxfnjduzncoukyqehkrkamofwz",
25 | "created_at": "2016-04-01 10:50:59 -0700",
26 | "updated_at": "2016-04-01 10:50:59 -0700",
27 | "mode": "text"
28 | },
29 | {
30 | "id": 24,
31 | "user_id": 30,
32 | "credential_name": "twitter_secret_key",
33 | "credential_value": "jhpswiebwhbrnabgkbvczrwcyxblxtyvvlvkhuoudjalcqmlwz",
34 | "created_at": "2016-04-01 10:50:59 -0700",
35 | "updated_at": "2016-04-01 10:50:59 -0700",
36 | "mode": "text"
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/spec/controllers/services_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe ServicesController do
4 | before do
5 | sign_in users(:bob)
6 | end
7 |
8 | describe "GET index" do
9 | it "only returns sevices of the current user" do
10 | get :index
11 | expect(assigns(:services).all? {|i| expect(i.user).to eq(users(:bob)) }).to eq(true)
12 | end
13 | end
14 |
15 | describe "POST toggle_availability" do
16 | it "should work for service of the user" do
17 | post :toggle_availability, :id => services(:generic).to_param
18 | expect(assigns(:service)).to eq(services(:generic))
19 | redirect_to(services_path)
20 | end
21 |
22 | it "should not work for a service of another user" do
23 | expect {
24 | post :toggle_availability, :id => services(:global).to_param
25 | }.to raise_error(ActiveRecord::RecordNotFound)
26 | end
27 | end
28 |
29 | describe "DELETE destroy" do
30 | it "destroys only services owned by the current user" do
31 | expect {
32 | delete :destroy, :id => services(:generic).to_param
33 | }.to change(Service, :count).by(-1)
34 |
35 | expect {
36 | delete :destroy, :id => services(:global).to_param
37 | }.to raise_error(ActiveRecord::RecordNotFound)
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/app/assets/javascripts/components/core.js.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | # Flash
3 | if $(".flash").length
4 | setTimeout((-> $(".flash").slideUp(-> $(".flash").remove())), 5000)
5 |
6 | # Help popovers
7 | $('.hover-help').popover(trigger: 'hover', html: true)
8 |
9 | # Pressing '/' selects the search box.
10 | $("body").on "keypress", (e) ->
11 | if e.keyCode == 47 # The '/' key
12 | if e.target.nodeName == "BODY"
13 | e.preventDefault()
14 | $agentNavigate.focus()
15 |
16 | # Select2 Selects
17 | $(".select2").select2(width: 'resolve')
18 |
19 | $(".select2-linked-tags").select2(
20 | width: 'resolve',
21 | formatSelection: (obj) ->
22 | "#{Utils.escape(obj.text)} "
23 | )
24 |
25 | # Helper for selecting text when clicked
26 | $('.selectable-text').each ->
27 | $(this).click ->
28 | range = document.createRange()
29 | range.setStartBefore(this.firstChild)
30 | range.setEndAfter(this.lastChild)
31 | sel = window.getSelection()
32 | sel.removeAllRanges();
33 | sel.addRange(range)
34 |
35 | # Agent navbar dropdown
36 | $('.navbar .dropdown.dropdown-hover').hover (-> $(this).addClass('open')), (-> $(this).removeClass('open'))
--------------------------------------------------------------------------------
/app/models/agents/read_file_agent.rb:
--------------------------------------------------------------------------------
1 | module Agents
2 | class ReadFileAgent < Agent
3 | include FormConfigurable
4 | include FileHandling
5 |
6 | cannot_be_scheduled!
7 | consumes_file_pointer!
8 |
9 | def default_options
10 | {
11 | 'data_key' => 'data'
12 | }
13 | end
14 |
15 | description do
16 | <<-MD
17 | The ReadFileAgent takes events from `FileHandling` agents, reads the file, and emits the contents as a string.
18 |
19 | `data_key` specifies the key of the emitted event which contains the file contents.
20 |
21 | #{receiving_file_handling_agent_description}
22 | MD
23 | end
24 |
25 | event_description <<-MD
26 | {
27 | "data" => '...'
28 | }
29 | MD
30 |
31 | form_configurable :data_key, type: :string
32 |
33 | def validate_options
34 | if options['data_key'].blank?
35 | errors.add(:base, "The 'data_key' options is required.")
36 | end
37 | end
38 |
39 | def working?
40 | received_event_without_error?
41 | end
42 |
43 | def receive(incoming_events)
44 | incoming_events.each do |event|
45 | next unless io = get_io(event)
46 | create_event payload: { interpolated['data_key'] => io.read }
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | devise:
6 | failure:
7 | deactivated_account: "Your account has been deactivated by an administrator."
8 | datetime:
9 | distance_in_words:
10 | half_a_minute: "half a minute"
11 | less_than_x_seconds:
12 | one: "<1s"
13 | other: "<%{count}s"
14 | x_seconds:
15 | one: "1s"
16 | other: "%{count}s"
17 | less_than_x_minutes:
18 | one: "<1m"
19 | other: "<%{count}m"
20 | x_minutes:
21 | one: "1m"
22 | other: "%{count}m"
23 | about_x_hours:
24 | one: "~1h"
25 | other: "~%{count}h"
26 | x_days:
27 | one: "1d"
28 | other: "%{count}d"
29 | about_x_months:
30 | one: "~1mo"
31 | other: "~%{count}mo"
32 | x_months:
33 | one: "1mo"
34 | other: "%{count}mo"
35 | about_x_years:
36 | one: "~1yr"
37 | other: "~%{count}yr"
38 | over_x_years:
39 | one: ">1yr"
40 | other: ">%{count}yr"
41 | almost_x_years:
42 | one: "~1yr"
43 | other: "~%{count}yr"
44 |
--------------------------------------------------------------------------------