Permalink
Browse files

Merge pull request #156 from cantino/user_credentials

Add UserCredentials and use them in the twitter Agents
  • Loading branch information...
2 parents b16f105 + ff758cc commit a7935d159067328f5e0faef80575b6a53969d832 @cantino cantino committed Feb 5, 2014
@@ -2,28 +2,41 @@ module TwitterConcern
extend ActiveSupport::Concern
included do
- self.validate :validate_twitter_options
- self.after_initialize :configure_twitter
+ validate :validate_twitter_options
+ after_initialize :configure_twitter
end
def validate_twitter_options
- unless options['consumer_key'].present? &&
- options['consumer_secret'].present? &&
- options['oauth_token'].present? &&
- options['oauth_token_secret'].present?
- errors.add(:base, "consumer_key, consumer_secret, oauth_token and oauth_token_secret are required to authenticate with the Twitter API")
+ unless twitter_consumer_key.present? &&
+ twitter_consumer_secret.present? &&
+ twitter_oauth_token.present? &&
+ twitter_oauth_token_secret.present?
+ errors.add(:base, "Twitter consumer_key, consumer_secret, oauth_token, and oauth_token_secret are required to authenticate with the Twitter API. You can provide these as options to this Agent, or as Credentials with the same names, but starting with 'twitter_'.")
end
end
+ def twitter_consumer_key
+ options['consumer_key'].presence || credential('twitter_consumer_key')
+ end
+
+ def twitter_consumer_secret
+ options['consumer_secret'].presence || credential('twitter_consumer_secret')
+ end
+
+ def twitter_oauth_token
+ options['oauth_token'].presence || options['access_key'].presence || credential('twitter_oauth_token')
+ end
+
+ def twitter_oauth_token_secret
+ options['oauth_token_secret'].presence || options['access_secret'].presence || credential('twitter_oauth_token_secret')
+ end
+
def configure_twitter
Twitter.configure do |config|
- config.consumer_key = options['consumer_key']
- config.consumer_secret = options['consumer_secret']
- config.oauth_token = options['oauth_token'] || options['access_key']
- config.oauth_token_secret = options['oauth_token_secret'] || options['access_secret']
+ config.consumer_key = twitter_consumer_key
+ config.consumer_secret = twitter_consumer_secret
+ config.oauth_token = twitter_oauth_token
+ config.oauth_token_secret = twitter_oauth_token_secret
end
end
-
- module ClassMethods
- end
end
@@ -0,0 +1,61 @@
+class UserCredentialsController < ApplicationController
+ def index
+ @user_credentials = current_user.user_credentials.page(params[:page])
+
+ respond_to do |format|
+ format.html
+ format.json { render json: @user_credentials }
+ end
+ end
+
+ def new
+ @user_credential = current_user.user_credentials.build
+
+ respond_to do |format|
+ format.html
+ format.json { render json: @user_credential }
+ end
+ end
+
+ def edit
+ @user_credential = current_user.user_credentials.find(params[:id])
+ end
+
+ def create
+ @user_credential = current_user.user_credentials.build(params[:user_credential])
+
+ respond_to do |format|
+ if @user_credential.save
+ format.html { redirect_to user_credentials_path, notice: 'Your credential was successfully created.' }
+ format.json { render json: @user_credential, status: :created, location: @user_credential }
+ else
+ format.html { render action: "new" }
+ format.json { render json: @user_credential.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ def update
+ @user_credential = current_user.user_credentials.find(params[:id])
+
+ respond_to do |format|
+ if @user_credential.update_attributes(params[:user_credential])
+ format.html { redirect_to user_credentials_path, notice: 'Your credential was successfully updated.' }
+ format.json { head :no_content }
+ else
+ format.html { render action: "edit" }
+ format.json { render json: @user_credential.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ def destroy
+ @user_credential = current_user.user_credentials.find(params[:id])
+ @user_credential.destroy
+
+ respond_to do |format|
+ format.html { redirect_to user_credentials_path }
+ format.json { head :no_content }
+ end
+ end
+end
View
@@ -102,6 +102,20 @@ def create_event(attrs)
end
end
+ def credential(name)
+ @credential_cache ||= {}
+ if @credential_cache.has_key?(name)
+ @credential_cache[name]
+ else
+ @credential_cache[name] = user.user_credentials.where(:credential_name => name).first.try(:credential_value)
+ end
+ end
+
+ def reload
+ @credential_cache = {}
+ super
+ end
+
def new_event_expiration_date
keep_events_for > 0 ? keep_events_for.days.from_now : nil
end
@@ -8,21 +8,19 @@ class TwitterPublishAgent < Agent
description <<-MD
The TwitterPublishAgent publishes tweets from the events it receives.
- You [must set up a Twitter app](https://github.com/cantino/huginn/wiki/Getting-a-twitter-oauth-token) and provide it's `consumer_key`, `consumer_secret`, `oauth_token` and `oauth_token_secret`,
- (also knows as "Access token" on the Twitter developer's site), along with the `username` of the Twitter user to publish as.
+ Twitter credentials must be supplied as either [credentials](/user_credentials) called
+ `twitter_consumer_key`, `twitter_consumer_secret`, `twitter_oauth_token`, and `twitter_oauth_token_secret`,
+ or as options to this Agent called `consumer_key`, `consumer_secret`, `oauth_token`, and `oauth_token_secret`.
- The `oauth_token` and `oauth_token_secret` determine which user the tweet will be sent as.
+ To get oAuth credentials for Twitter, [follow these instructions](https://github.com/cantino/huginn/wiki/Getting-a-twitter-oauth-token).
You must also specify a `message_path` parameter: a [JSONPaths](http://goessner.net/articles/JsonPath/) to the value to tweet.
Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
MD
def validate_options
- unless options['username'].present? &&
- options['expected_update_period_in_days'].present?
- errors.add(:base, "username and expected_update_period_in_days are required")
- end
+ errors.add(:base, "expected_update_period_in_days is required") unless options['expected_update_period_in_days'].present?
end
def working?
@@ -31,12 +29,7 @@ def working?
def default_options
{
- 'username' => "",
'expected_update_period_in_days' => "10",
- 'consumer_key' => "---",
- 'consumer_secret' => "---",
- 'oauth_token' => "---",
- 'oauth_token_secret' => "---",
'message_path' => "text"
}
end
@@ -68,9 +61,8 @@ def receive(incoming_events)
end
end
- def publish_tweet text
+ def publish_tweet(text)
Twitter.update(text)
end
-
end
end
@@ -6,11 +6,13 @@ class TwitterStreamAgent < Agent
description <<-MD
The TwitterStreamAgent follows the Twitter stream in real time, watching for certain keywords, or filters, that you provide.
- You must provide an oAuth `consumer_key`, `consumer_secret`, `oauth_token`, and `oauth_token_secret`, as well as an array of `filters`. Multiple words in a filter
- must all show up in a tweet, but are independent of order.
-
+ To follow the Twitter stream, provide an array of `filters`. Multiple words in a filter must all show up in a tweet, but are independent of order.
If you provide an array instead of a filter, the first entry will be considered primary and any additional values will be treated as aliases.
+ Twitter credentials must be supplied as either [credentials](/user_credentials) called
+ `twitter_consumer_key`, `twitter_consumer_secret`, `twitter_oauth_token`, and `twitter_oauth_token_secret`,
+ or as options to this Agent called `consumer_key`, `consumer_secret`, `oauth_token`, and `oauth_token_secret`.
+
To get oAuth credentials for Twitter, [follow these instructions](https://github.com/cantino/huginn/wiki/Getting-a-twitter-oauth-token).
Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
@@ -67,13 +69,9 @@ def working?
def default_options
{
- 'consumer_key' => "---",
- 'consumer_secret' => "---",
- 'oauth_token' => "---",
- 'oauth_token_secret' => "---",
- 'filters' => %w[keyword1 keyword2],
- 'expected_update_period_in_days' => "2",
- 'generate' => "events"
+ 'filters' => %w[keyword1 keyword2],
+ 'expected_update_period_in_days' => "2",
+ 'generate' => "events"
}
end
@@ -9,7 +9,13 @@ class TwitterUserAgent < Agent
description <<-MD
The TwitterUserAgent follows the timeline of a specified Twitter user.
- You [must set up a Twitter app](https://github.com/cantino/huginn/wiki/Getting-a-twitter-oauth-token) and provide it's `consumer_key`, `consumer_secret`, `oauth_token` and `oauth_token_secret`, (Also shown as "Access token" on the Twitter developer's site.) along with the `username` of the Twitter user to monitor.
+ Twitter credentials must be supplied as either [credentials](/user_credentials) called
+ `twitter_consumer_key`, `twitter_consumer_secret`, `twitter_oauth_token`, and `twitter_oauth_token_secret`,
+ or as options to this Agent called `consumer_key`, `consumer_secret`, `oauth_token`, and `oauth_token_secret`.
+
+ To get oAuth credentials for Twitter, [follow these instructions](https://github.com/cantino/huginn/wiki/Getting-a-twitter-oauth-token).
+
+ You must also provide the `username` of the Twitter user to monitor.
Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
MD
@@ -53,12 +59,8 @@ def working?
def default_options
{
- 'username' => "tectonic",
- 'expected_update_period_in_days' => "2",
- 'consumer_key' => "---",
- 'consumer_secret' => "---",
- 'oauth_token' => "---",
- 'oauth_token_secret' => "---"
+ 'username' => "tectonic",
+ 'expected_update_period_in_days' => "2"
}
end
View
@@ -1,5 +1,6 @@
# Contacts are used only for the contact form on the Huginn website. If you host a public Huginn instance, you can use
# these to receive messages from visitors.
+
class Contact < ActiveRecord::Base
attr_accessible :email, :message, :name
View
@@ -22,6 +22,7 @@ class User < ActiveRecord::Base
validates_format_of :username, :with => /\A[a-zA-Z0-9_-]{3,15}\Z/, :message => "can only contain letters, numbers, underscores, and dashes, and must be between 3 and 15 characters in length."
validates_inclusion_of :invitation_code, :on => :create, :in => INVITATION_CODES, :message => "is not valid"
+ has_many :user_credentials, :dependent => :destroy, :inverse_of => :user
has_many :events, :order => "events.created_at desc", :dependent => :delete_all, :inverse_of => :user
has_many :agents, :order => "agents.created_at desc", :dependent => :destroy, :inverse_of => :user
has_many :logs, :through => :agents, :class_name => "AgentLog"
@@ -0,0 +1,19 @@
+class UserCredential < ActiveRecord::Base
+ attr_accessible :credential_name, :credential_value
+
+ belongs_to :user
+
+ validates_presence_of :credential_name
+ validates_presence_of :credential_value
+ validates_presence_of :user_id
+ validates_uniqueness_of :credential_name, :scope => :user_id
+
+ before_save :trim_fields
+
+ protected
+
+ def trim_fields
+ credential_name.strip!
+ credential_value.strip!
+ end
+end
@@ -58,4 +58,4 @@
</div>
</div>
</div>
-</div>
+</div>
@@ -4,6 +4,7 @@
<ul class='nav pull-left'>
<%= nav_link "Agents", agents_path %>
<%= nav_link "Events", events_path %>
+ <%= nav_link "Credentials", user_credentials_path %>
</ul>
<% end %>
@@ -0,0 +1,30 @@
+<%= form_for(@user_credential, :method => @user_credential.new_record? ? "POST" : "PUT") do |f| %>
+ <% if @user_credential.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@user_credential.errors.count, "error") %> prohibited this Credential from being saved:</h2>
+ <ul>
+ <% @user_credential.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div class="control-group">
+ <%= f.label :credential_name, :class => 'control-label' %>
+ <div class="controls">
+ <%= f.text_field :credential_name, :class => 'span4' %>
+ </div>
+ </div>
+
+ <div class="control-group">
+ <%= f.label :credential_value, :class => 'control-label' %>
+ <div class="controls">
+ <%= f.text_area :credential_value, :class => 'span8', :rows => 10 %>
+ </div>
+ </div>
+
+ <div class='form-actions' style='clear: both'>
+ <%= f.submit "Save Credential", :class => "btn btn-primary" %>
+ </div>
+<% end %>
@@ -0,0 +1,17 @@
+<div class='container'>
+ <div class='row'>
+ <div class='span12'>
+ <div class="page-header">
+ <h2>
+ Editing your Credential
+ </h2>
+ </div>
+
+ <%= render 'form' %>
+
+ <div class="btn-group">
+ <%= link_to '<i class="icon-chevron-left"></i> Back'.html_safe, user_credentials_path, class: "btn" %>
+ </div>
+ </div>
+ </div>
+</div>
Oops, something went wrong.

0 comments on commit a7935d1

Please sign in to comment.