Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit d3e80c0b72dff2d81e5c4978e4e8a4764ca046e8 0 parents
rsl authored
3  .gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+doc
+test/*.sqlite3
3  CHANGELOG
@@ -0,0 +1,3 @@
+= v 1.0
+
+Initial release
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Lucky Sneaks
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 README
@@ -0,0 +1,34 @@
+= ActsAsSnook
+
+ActsAsSnook is a simple and elegant method of handling comment spam that doesn't rely on CAPTCHAS or Javascript or external web services. It uses a point based system to detect common traits of comment spam. The details of this implementation can be found in the source code and tests as well as the original blog post I based this plugin on (see "Thanks and credits").
+
+== Setup
+
+ActsAsSnook assumes that you have the following fields on your comment model: <tt>author</tt> [string], <tt>email</tt> [string], <tt>url</tt> [string], <tt>body</tt> [text], and <tt>spam_status</tt> [string]. The name of these attributes can be changed as shown in the next section.
+
+== Usage
+
+ acts_as_snook
+
+That's it if you want to use the default attributes I mentioned above. If you need to customize them, just specify them as arguments to acts_as_snook:
+
+ acts_as_snook :author_field => :user, :body_field => :comment # Etc, etc
+
+You may also add your own keywords to the list of spam keywords that ActsAsSnook uses to detect spam by adding them to the arguments:
+
+ acts_as_snook :spam_words => %{hatespeech ignorantlanguage blathering}
+
+ActsAsSnook also provides some helper methods for determining the spam status of a comment.
+
+<tt>comment.ham?</tt>:: Returns true for comments not marked as spam. "Ham" comments are safe for displaying on your site as is. This is the method you should most likely be using to check an individual comment.
+<tt>comment.spam?</tt>:: Returns true for comments marked as spam. These are should not be shown on your site.
+<tt>comment.moderate?</tt>:: Returns true for comments that were not marked as actual spam but did not get enough points to be marked as "ham" either. The spam_status field for these will contain "moderate".
+<tt>Comment#ham</tt>:: Shortcut for <tt>Comment#find(:all, :conditions => {:spam_status => "ham"})</tt>
+<tt>Comment#spam</tt>:: Shortcut for <tt>Comment#find(:all, :conditions => {:spam_status => "spam"})</tt>
+<tt>Comment#moderate</tt>:: Shortcut for <tt>Comment#find(:all, :conditions => {:spam_status => "moderate"})</tt>
+
+== Credits and Thanks
+
+Much love to Jonathan Snook, web developer extraordinaire. Most of the algorithms of ActsAsSnook [as well as the name, obviously] come from his blog post which you can find here[http://snook.ca/archives/other/effective_blog_comment_spam_blocker] The actual implementation I myself wrote.
+
+Copyright (c) 2008 Lucky Sneaks, released under the MIT license
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the ActsAsSnook plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the ActsAsSnook plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'doc'
+ rdoc.title = 'ActsAsSnook'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
3  init.rb
@@ -0,0 +1,3 @@
+require "lucky_sneaks/acts_as_snook"
+
+ActiveRecord::Base.send :include, LuckySneaks::ActsAsSnook
223 lib/lucky_sneaks/acts_as_snook.rb
@@ -0,0 +1,223 @@
+module LuckySneaks
+ module ActsAsSnook
+ def self.included(base) # :nodoc:
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Sets up spam detection (via before_validation callback). Avaiable options are:
+ #
+ # +:author_field+:: Symbol or string specifying a new database field to use for the author attribute
+ # +:email_field+:: Symbol or string specifying a new database field to use for the email attribute
+ # +:url_field+:: Symbol or string specifying a new database field to use for the url attribute
+ # +:body_field+:: Symbol or string specifying a new database field to use for the body attribute
+ # +:author_field+:: Symbol or string specifying a new database field to use for the spam_status attribute
+ # +:spam_words+:: Array of strings which will be added to the list of words which are considered spam markers
+ def acts_as_snook(options = {})
+ cattr_accessor :spam_words
+ self.spam_words = %w{
+ -online 4u 4-u acne adipex advicer baccarrat blackjack bllogspot booker buy byob carisoprodol
+ casino chatroom cialis coolhu credit-card-debt cwas cyclen cyclobenzaprine
+ day-trading debt-consolidation discreetordering duty-free dutyfree equityloans fioricet
+ freenet gambling- hair-loss homefinance holdem incest jrcreations levitra macinstruct mortgagequotes
+ online-gambling ottawavalleyag ownsthis paxil penis pharmacy phentermine
+ poker poze pussy ringtones roulette shemale shoes -site slot-machine thorcarlson
+ tramadol trim-spa ultram valeofglamorganconservatives viagra vioxx xanax zolus
+ }
+ if additional = options.delete(:spam_words)
+ self.spam_words << additional
+ self.spam_words.flatten.uniq.compact!
+ end
+
+ cattr_accessor :fields_for_snooking
+ self.fields_for_snooking = {
+ # Defaults
+ :author_field => :author,
+ :email_field => :email,
+ :url_field => :url,
+ :body_field => :body,
+ :spam_status_field => :spam_status
+ }.merge(options)
+
+ before_validation :calculate_snook_score
+
+ attr_reader :snook_credits
+ end
+
+ # Returns all instances which have been marked as safe for display
+ def ham(options = {})
+ find :all, options.merge(:conditions => {fields_for_snooking[:spam_status_field] => "ham"})
+ end
+
+ # Returns all instances which have been marked as being spam
+ def spam(options = {})
+ find :all, options.merge(:conditions => {fields_for_snooking[:spam_status_field] => "spam"})
+ end
+
+ # Returns all instances which have been marked as neither obvious spam nor obviously safe
+ # for displaying. This should be a really small number of your comments as it is _very_
+ # hard to hit this spot.
+ def moderate(options = {})
+ find :all, options.merge(:conditions => {fields_for_snooking[:spam_status_field] => "moderate"})
+ end
+ end
+
+ # Returns true if marked as spam, false otherwise
+ def spam?
+ calculate_snook_score unless snook_credits
+ snook_spam_status == "spam"
+ end
+
+ # Returns true if marked as ham (safe to display), false if spam
+ def ham?
+ calculate_snook_score unless snook_credits
+ snook_spam_status == "ham"
+ end
+
+ # Returns true if marked as spam, false otherwise
+ def moderate?
+ calculate_snook_score unless snook_credits
+ snook_spam_status == "moderate"
+ end
+
+ private
+ def calculate_snook_for_body_links
+ link_count = snook_body.scan(/http:/).size
+ if link_count > 2
+ deduct_snook_credits link_count
+ else
+ add_snook_credits 2
+ end
+ end
+
+ def calculate_snook_for_body_length
+ if snook_body.length > 20
+ add_snook_credits(2) if snook_body.scan(/http:/).size.zero?
+ else
+ deduct_snook_credits 1
+ end
+ end
+
+ def calculate_snook_for_previous_comments
+ add_snook_credits previous_comment_count_for_snook_author("ham")
+ deduct_snook_credits previous_comment_count_for_snook_author("spam")
+ end
+
+ def calculate_snook_for_spam_words
+ spam_words.each do |word|
+ deduct_snook_credits(1) if snook_body =~ /#{word}/i
+ deduct_snook_credits(1) if snook_url =~ /#{word}/i
+ end
+ end
+
+ def calculate_snook_for_suspect_url
+ regex = /http:\/\/\S*(\.html|\.info|\?|&|free)/i
+ deduct_snook_credits(1 * snook_body.scan(regex).size)
+ deduct_snook_credits(1) if snook_url =~ regex
+ end
+
+ def calculate_snook_for_suspect_tld
+ regex = /http:\/\/\S*\.(de|pl|cn)/i
+ deduct_snook_credits(1 * snook_body.scan(regex).size)
+ deduct_snook_credits(1) if snook_url =~ regex
+ end
+
+ def calculate_snook_for_url_length
+ deduct_snook_credits(1) if snook_url.length > 30
+ end
+
+ def calculate_snook_for_lame_body_start
+ deduct_snook_credits(10) if snook_body =~ /^(interesting|sorry|nice|cool)\s/i
+ end
+
+ def calculate_snook_for_author_link
+ deduct_snook_credits(2 * snook_author.scan(/http:/).size)
+ end
+
+ def calculate_snook_for_matching_previous_body
+ deduct_snook_credits(1 * previous_comments_with_same_snook_body)
+ end
+
+ def calculate_snook_for_consonant_runs
+ [snook_author, snook_email, snook_url, snook_body].each do |snookable|
+ snookable.scan(/[bcdfghjklmnpqrstvwxz]{5,}/).each do |run|
+ deduct_snook_credits run.size - 4
+ end
+ end
+ end
+
+ def calculate_snook_for_bbcode
+ deduct_snook_credits(1 * snook_body.scan(/\[(url|img)/i).size)
+ end
+
+ def calculate_snook_score
+ @snook_credits = 0
+ calculate_snook_for_body_links
+ calculate_snook_for_body_length
+ calculate_snook_for_previous_comments
+ calculate_snook_for_spam_words
+ calculate_snook_for_suspect_url
+ calculate_snook_for_suspect_tld
+ calculate_snook_for_url_length
+ calculate_snook_for_lame_body_start
+ calculate_snook_for_author_link
+ calculate_snook_for_matching_previous_body
+ calculate_snook_for_consonant_runs
+ status = if @snook_credits > 0
+ "ham"
+ elsif @snook_credits == 0
+ "moderate"
+ else
+ "spam"
+ end
+ self.send("#{self.class.fields_for_snooking[:spam_status_field]}=", status)
+ end
+
+ def snook_author
+ @snook_author ||= self.send(self.class.fields_for_snooking[:author_field]) || ""
+ end
+
+ def snook_email
+ @snook_email ||= self.send(self.class.fields_for_snooking[:email_field]) || ""
+ end
+
+ def snook_url
+ @snook_url ||= self.send(self.class.fields_for_snooking[:url_field]) || ""
+ end
+
+ def snook_body
+ @snook_body ||= self.send(self.class.fields_for_snooking[:body_field]) || ""
+ end
+
+ def snook_spam_status
+ self.send(self.class.fields_for_snooking[:spam_status_field])
+ end
+
+ def add_snook_credits(addition)
+ @snook_credits = @snook_credits + addition
+ end
+
+ def deduct_snook_credits(deduction)
+ @snook_credits = @snook_credits - deduction
+ end
+
+ def previous_comment_count_for_snook_author(spam_value)
+ spam_field = self.class.fields_for_snooking[:spam_status_field]
+ email_field = self.class.fields_for_snooking[:email_field]
+ email_value = snook_email
+
+ conditions = ["#{spam_field} = ? AND #{email_field} = ?", spam_value, email_value]
+
+ self.class.count :all, :conditions => conditions
+ end
+
+ def previous_comments_with_same_snook_body
+ body_field = self.class.fields_for_snooking[:body_field]
+ body_value = snook_body
+
+ conditions = ["#{body_field} = ?", body_value]
+
+ self.class.count :all, :conditions => conditions
+ end
+ end
+end
4 tasks/acts_as_snook_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :acts_as_snook do
+# # Task goes here
+# end
66 test/acts_as_snook_interface_test.rb
@@ -0,0 +1,66 @@
+require "test/test_helper"
+
+class ActsAsSnookInterfaceTest < Test::Unit::TestCase
+ def test_marks_spam_as_spam
+ SPAM_COMMENTS.each do |comment_attributes|
+ @comment = Comment.new(comment_attributes)
+ @comment.valid?
+ assert_equal "spam", @comment.spam_status
+ end
+ end
+
+ def test_marks_ham_as_ham
+ HAM_COMMENTS.each do |comment_attributes|
+ @comment = Comment.new(comment_attributes)
+ @comment.valid?
+ assert_equal "ham", @comment.spam_status
+ end
+ end
+
+ def test_indicates_spam_status_of_spam
+ @comment = Comment.new(SPAM_COMMENTS.first)
+ assert @comment.spam?
+ end
+
+ def test_indicates_spam_status_of_ham
+ @comment = Comment.new(HAM_COMMENTS.first)
+ assert @comment.ham?
+ end
+
+ def test_indicates_spam_status_of_comments_needing_moderation
+ # This is a hard spot to hit!
+ @comment = Comment.new(
+ :author => "Mister Mxyzptlk",
+ :url => "http://superman.de",
+ :body => "I take viagra and cialis but I'm not selling it."
+ )
+ assert @comment.moderate?
+ end
+
+ def test_collates_spam
+ SPAM_COMMENTS.each{|attributes| Comment.create attributes}
+ HAM_COMMENTS.each{|attributes| Comment.create attributes}
+
+ assert Comment.spam.all?{|comment| comment.spam_status == "spam"}
+
+ Comment.delete_all
+ end
+
+ def test_collates_ham
+ SPAM_COMMENTS.each{|attributes| Comment.create attributes}
+ HAM_COMMENTS.each{|attributes| Comment.create attributes}
+
+ assert Comment.ham.all?{|comment| comment.spam_status == "ham"}
+
+ Comment.delete_all
+ end
+
+ def test_collates_comments_needing_moderation
+ SPAM_COMMENTS.each{|attributes| Comment.create attributes}
+ HAM_COMMENTS.each{|attributes| Comment.create attributes}
+
+ assert Comment.moderate.empty?
+
+ Comment.delete_all
+ end
+end
175 test/acts_as_snook_internals_test.rb
@@ -0,0 +1,175 @@
+require "test/test_helper"
+
+class ActsAsSnookInternalsTest < Test::Unit::TestCase
+ def setup
+ @comment = Comment.new
+ # This happens internally but needs to be manually done
+ # because these tests are being run individually here
+ @comment.instance_variable_set("@snook_credits", 0)
+ end
+
+ def test_comment_gains_credits_if_no_link_in_body
+ @comment.body = "no link"
+ @comment.send :calculate_snook_for_body_links
+ assert_equal +2, @comment.snook_credits
+ end
+
+ def test_comment_gains_credits_if_one_link_in_body
+ @comment.body = link
+ @comment.send :calculate_snook_for_body_links
+ assert_equal +2, @comment.snook_credits
+ end
+
+ def test_comment_gains_credits_if_two_links_in_body
+ @comment.body = link + link
+ @comment.send :calculate_snook_for_body_links
+ assert_equal +2, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_link_if_more_than_two_links_in_body
+ @comment.body = link + link + link
+ @comment.send :calculate_snook_for_body_links
+ assert_equal -3, @comment.snook_credits
+ end
+
+ def test_comment_gains_credits_if_body_length_over_20_characters_without_links
+ @comment.body = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi fringilla semper sem. Proin sed eros."
+ @comment.send :calculate_snook_for_body_length
+ assert_equal +2, @comment.snook_credits
+ end
+
+ def test_comment_credits_do_not_change_if_body_length_over_20_characters_with_links
+ @comment.body = "<a href='http://lipsum.com'>Lorem ipsum</a> dolor sit amet, consectetuer adipiscing elit. Morbi fringilla semper sem."
+ @comment.send :calculate_snook_for_body_length
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credits_if_body_length_under_20_characters
+ @comment.body = "Lorem ipsum."
+ @comment.send :calculate_snook_for_body_length
+ assert_equal -1, @comment.snook_credits
+ end
+
+ def test_comment_gains_credit_per_prior_approved_comment
+ setup_prior_comments
+ @comment = good_comment
+ @comment.instance_variable_set("@snook_credits", 0)
+ @comment.send :calculate_snook_for_previous_comments
+ assert_equal +2, @comment.snook_credits
+ Comment.delete_all
+ end
+
+ def test_comment_loses_credit_per_prior_spam_comment
+ setup_prior_comments
+ @comment = bad_comment
+ @comment.instance_variable_set("@snook_credits", 0)
+ @comment.send :calculate_snook_for_previous_comments
+ assert_equal -2, @comment.snook_credits
+ Comment.delete_all
+ end
+
+ def test_comment_loses_credit_for_each_spam_word_in_body
+ @comment.body = "viagra, casino, poker, cialis"
+ @comment.send :calculate_snook_for_spam_words
+ assert_equal -4, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_for_each_spam_word_in_url
+ @comment.url = "http://viagra-online.com"
+ @comment.send :calculate_snook_for_spam_words
+ assert_equal -2, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_suspect_url_in_body
+ @comment.body = "<a href='http://foo.com/page.html'>foo</a> <a href='http://bar.info/'>bar</a>"
+ @comment.send :calculate_snook_for_suspect_url
+ assert_equal -2, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_suspect_url_in_url
+ @comment.url = "http://foo.com/?bar=baz"
+ @comment.send :calculate_snook_for_suspect_url
+ assert_equal -1, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_suspect_tld_in_body
+ @comment.body = "<a href='http://foo.de'>foo</a> <a href='http://bar.pl/'>bar</a> <a href='http://baz.cn/quux'></a>"
+ @comment.send :calculate_snook_for_suspect_tld
+ assert_equal -3, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_suspect_tld_in_url
+ @comment.url = "http://foo.de"
+ @comment.send :calculate_snook_for_suspect_tld
+ assert_equal -1, @comment.snook_credits
+ end
+
+ def test_comment_credits_do_not_change_if_url_shorter_than_30_characters
+ @comment.url = "http://acceptable-length.com/"
+ @comment.send :calculate_snook_for_url_length
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_if_url_longer_than_30_characters
+ @comment.url = "http://i-have-a-long-url-because-i-am-spamming-keywords-in-it.com"
+ @comment.send :calculate_snook_for_url_length
+ assert_equal -1, @comment.snook_credits
+ end
+
+ def test_comment_does_not_lose_credit_if_body_does_not_start_with_lame_word
+ @comment.body = "I am an acceptable comment"
+ @comment.send :calculate_snook_for_lame_body_start
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_if_body_starts_with_lame_word
+ @comment.body = "Interesting post."
+ @comment.send :calculate_snook_for_lame_body_start
+ assert_equal -10, @comment.snook_credits
+ end
+
+ def test_comment_credits_do_not_change_if_no_http_in_author
+ @comment.author = "Jack Shepherd"
+ @comment.send :calculate_snook_for_author_link
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_http_in_author
+ @comment.author = "<a href='http://foo.com/'>http://foo.com/</a>"
+ @comment.send :calculate_snook_for_author_link
+ assert_equal -4, @comment.snook_credits
+ end
+
+ def test_comment_credits_do_not_change_if_body_does_not_match_previous_comment
+ @comment.body = "Unique by default."
+ @comment.send :calculate_snook_for_matching_previous_body
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_body_matching_previous_comment
+ setup_prior_comments
+ @comment = bad_comment
+ @comment.instance_variable_set("@snook_credits", 0)
+ @comment.send :calculate_snook_for_matching_previous_body
+ assert_equal -2, @comment.snook_credits
+ Comment.delete_all
+ end
+
+ def test_comment_loses_credit_per_run_of_5_consonants
+ @comment.body = "bvrrsd trnkdattlf"
+ @comment.send :calculate_snook_for_consonant_runs
+ assert_equal -3, @comment.snook_credits
+ end
+
+ def test_comment_credits_do_not_change_if_no_bbcode
+ @comment.body = "This is good text. It contains no harmful, hackful bbcode"
+ @comment.send :calculate_snook_for_bbcode
+ assert_equal 0, @comment.snook_credits
+ end
+
+ def test_comment_loses_credit_per_instance_of_bbcode
+ @comment.body = "[URL=http://foo.com]foo[/URL] and [URL=http://bar.com]bar[/URL]"
+ @comment.send :calculate_snook_for_bbcode
+ assert_equal -2, @comment.snook_credits
+ end
+end
3  test/comment.rb
@@ -0,0 +1,3 @@
+class Comment < ActiveRecord::Base
+ acts_as_snook # Defaults
+end
8 test/schema.rb
@@ -0,0 +1,8 @@
+ActiveRecord::Migration.verbose = false
+
+ActiveRecord::Schema.define(:version => 1) do
+ create_table :comments, :force => true do |t|
+ t.string :author, :email, :url, :spam_status
+ t.text :body
+ end
+end
160 test/test_helper.rb
@@ -0,0 +1,160 @@
+$:.unshift ""
+
+require 'test/unit'
+
+begin
+ # Load Rails environment if you can
+ require File.join(File.dirname(__FILE__), '/../../../config/environment')
+rescue LoadError
+ # Load gem ActiveRecord if you can't
+ require 'rubygems'
+ gem 'activerecord'
+ require 'active_record'
+
+ RAILS_ROOT = File.dirname(__FILE__)
+end
+
+ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => "acts_as_url.sqlite3")
+
+require File.join(File.dirname(__FILE__), '../init')
+require File.join(File.dirname(__FILE__), 'schema')
+require File.join(File.dirname(__FILE__), 'comment')
+
+class Test::Unit::TestCase # :nodoc:
+ def link
+ "<a href='http://lipsum.com/'>lorem ipsum</a>"
+ end
+
+ def good_comment(options = {})
+ Comment.new({
+ :author => "Jack Shephard",
+ :email => "jack@st-sebastian.org",
+ :body => "If we don't live together, we're gonna die alone."
+ }.merge(options))
+ end
+
+ def bad_comment(options = {})
+ Comment.new({
+ :author => "Benjamin Linus",
+ :email => "leader@otherton.net",
+ :body => "Nice job. We're the good ones."
+ }.merge(options))
+ end
+
+ def setup_prior_comments
+ good_comment.save
+ good_comment(:body => "Something different.").save
+ bad_comment.save
+ bad_comment.save
+ end
+
+ SPAM_COMMENTS = [
+ {
+ :author => 'osama187',
+ :email => 'osama394@BinLaden.us',
+ :url => 'www.mlx6.com',
+ :body => 'alalah; <a href="http://www.frankharmer10k.com/">spain slots online frankharmer10k.com</a>; <a href="http://www.rogerperez.com">phentermine</a>; <a href="http://www.inca-trail-to-machupicchu.com">blackjack</a>; <a href="http://www.concordbridge.org/">gambling amchine jackpotter</a>; <a href="http://www.mysinglelesbianlife.com">lexapro</a>; <a href="http://www.pawneebillswildwestshow.com">blackjack</a>; <a href="http://www.onlinepokerlabs.de">pokerraume</a>; <a href="http://www.thegregmooreshow.com">life insurance</a>; <a href="http://www.rtlgrossepointe.org">slots</a>; <a href="http://www.cityinamber.com/">cityinamber.com combined auto and home insurance</a>; <a href="http://www.racetothestars.com">casino slots</a>; <a href="http://www.be-ga.net/">be-ga.net stacker 2 blackjack</a>; <a href="http://www.goe2007.com/">goe2007.com blackjack boats</a>; <a href="http://www.mlx6.com">paxil</a>; <a href="http://www.aiccn.org/">aiccn.org horace mann auto insurance</a>; <a href="http://www.smmmsa.com">buy rimonabant</a>; <a href="http://www.electrummel.org/">electrummel.org blackjack forward slash</a>; <a href="http://www.tschofenig.com">viagra</a>; <a href="http://www.consciouscultureblog.com/">consciouscultureblog.com simslots casino com</a>; <a href="http://www.u-can-sell-it.com">cheap car insurance</a>; <a href="http://www.isiskali.com">internet black jack</a>; <a href="http://www.bloggersnepal.com/">inside blackjack casino</a>; <a href="http://www.blogswana.org/">blogswana.org auto insurance hybrid vehicle</a>; '
+ },
+ {
+ :author => 'assan90',
+ :email => 'assan8@hassoni8.com',
+ :url => 'buyaugmentinonline.blinklist.com/',
+ :body => %q{a , <a href="http://buy-propecia-worldwide.wikidot.com">buy propecia</a>, <a href="http://buy-cheap-cialis-online.wikidot.com">buy cialis online</a>, <a href="http://cialisss.blinklist.com/">cialis levitra viagra,</a>, <a href="http://viagra-online-worldwide.blinklist.com/">viagra for sale</a>, <a href="http://buy-generic-viagra-worldwide.blinklist.com/">buy generic viagra</a>, <a href="http://buyaugmentinonline.blinklist.com/">augmentin</a>, <a href="http://byzoloftonline.blinklist.com/">zoloft</a>, <a href="http://augmentin-online-pharmacy.wikidot.com">augmentin</a>, <a href="http://acompliauspharmacy.blinklist.com/">acomplia</a>, <a href="http://buy-soma-worldwide.wikidot.com">buy soma</a>, <a href="http://buycialisonlineworldwide.blinklist.com/">generic cialis online</a>, <a href="http://buy-ultram-online-worldwide.blinklist.com/">buy ultram online</a>, <a href="http://buy-tramado-online-worldwide.wikidot.com">buy tramadol online</a>, }
+ },
+ {
+ :author => 'cialis online',
+ :email => '',
+ :url => 'http://www.blogger.com/profile/11453893651597504089',
+ :body => %q{<a href="http://www.theusapills.com/cialis" rel="nofollow">Order Cialis From The #1 Online Pharmacy</a><br>THE LOWEST CIALIS PRICE GUARANTEED<br> Fast And Discreet Shipping Worldwide<br> Free Medical Consultation And More<br> <a href="http://www.theusapills.com/cialis" rel="nofollow">CLICK HERE TO ENTER<br> http://www.theusapills.com/cialis</a>}
+ },
+ {
+ :author => 'Charles Ford',
+ :email => 'Joshua@internet.com',
+ :url => 'http://www.av.com/',
+ :body => %q{Very informative site. Good job. thins that excited you at 14:<br>
+ <a target="_blank" class="ext" href="http://www.adobe.com" title="http://www.adobe.com">http://www.adobe.com</a> , <a href="http://www.yahoo.co.uk" rel="nofollow">thins<br>
+ that excited you at 14</a> , <a href="http://www.panasonic.com" rel="nofollow">black girls on their mission</a>
+ }
+ },
+ {
+ :author => 'Welekneenia',
+ :email => 'argenbrownou@yandex.ru',
+ :url => 'http://replica-handbag.handbagreplicawatch.net/index.html',
+ :body => %q{[URL=http://swiss-replica.handbagreplicawatch.net/swiss-replica-AND-canal.html]swiss replica AND canal[/URL] [URL=http://swiss-replica.handbagreplicawatch.net/swiss-replica-watch.html]swiss replica watch[/URL] [URL=http://replica-gucci-bag.handbagreplicawatch.net/gucci-messenger-bag-replica.html]gucci messenger bag replica[/URL]
+ Swiss Replica Rolex is made of the highest quality materials.
+ [URL=http://prada-replica-handbag.handbagreplicawatch.net/replica-prada-mens.html]replica prada mens[/URL] [URL=http://prada-replica-handbag.handbagreplicawatch.net/prada-replica-sport.html]prada replica sport[/URL]
+ The fact is that only a few people are able to afford it.
+ [URL=http://prada-replica-handbag.handbagreplicawatch.net/replica-prada-bags.html]replica prada bags[/URL] [URL=http://replica-oakley.handbagreplicawatch.net/replica-oakley-watch.html]replica oakley watch[/URL]
+ They also specialize on luxury necklaces and bag replicas.
+ [URL=http://tiffany-replica.handbagreplicawatch.net/replica-tiffany-and-co.html]replica tiffany and co[/URL] [URL=http://panerai-replica.handbagreplicawatch.net/panerai-radiomir-replica.html]panerai radiomir replica[/URL]
+ All purchased in the last six months or so.
+ [URL=http://replica-watch.handbagreplicawatch.net/chopard-replica-watch.html]chopard replica watch[/URL] [URL=http://replica-watch.handbagreplicawatch.net/buy-a-replica-watch.html]buy a replica watch[/URL] [URL=http://replica-bag.handbagreplicawatch.net/fendi-spy-bag-replica.html]fendi spy bag replica[/URL]
+ The styling of the watches is the same as the originals.
+ [URL=http://replica-bag.handbagreplicawatch.net/bag-diaper-kate-replica-spade.html]bag diaper kate replica spade[/URL] [URL=http://replica-watch.handbagreplicawatch.net/replica-citizen-watch.html]replica citizen watch[/URL]}
+ },
+ {
+ :author => "Fearacreext",
+ :email => "iledygoro@yandex.ua",
+ :url => "http://www.nemogs.com/e/",
+ :body => %q{Hey sweetie, you wanna attract chick at the club? Try Ultra Allure pheromones!
+ -Attract women of all ages
+ -Excite women before even talking to them
+ -Make women want to sleep with you immediately
+ -Millions of men are already using them!
+ -Proven to work!
+ [URL=http://www.nemogs.com/r/]They are having a huge sale right now, check out the site for all the info.[/URL]
+ [URL=http://www.nemogs.com/r/]Everybody I know has got a couple bottles of this stuff, it simply works! Don't be the only one left behind![/URL]
+ Product here - http://nemogs.com/r/index.php}
+ }
+ ]
+
+ HAM_COMMENTS = [
+ {
+ :author => 'Dan Cedarholm',
+ :email => '',
+ :url => 'http://www.simplebits.com/',
+ :body => %q{Nicely said, Simon. I have a bit of a knee-jerk reaction to comments -- when I'm flooded I curse them, but like you, I feel the comments add far more value than my posts.
+ So I feel more optimistic after reading your thoughts on the state of things. Something that didn't register until now is that MT's variation on your excellent redirect solution completely hides the URL, killing the "signature" aspect of a comment. I'm thinking your /redirect/?http://www.site.com is a nicer solution -- it's certainly a good way of verifying a frequent poster.}
+ },
+ {
+ :author => 'Voyagerfan5761',
+ :email => '',
+ :url => 'http://voyagerfan5761.blogspot.com/',
+ :body => %q{
+ I like this post. The algorithm you've put together here is quite intriguing, though I'm a bit worried about a couple of the filter rules.
+ For instance, my URL is more than 30 characters, simply because I'm using a subdomain at a free blog host. Does that necessarily mean I'm a spammer? No. I've been thinking about getting my own domain, but -- and this is where it gets interesting -- my ideal registered domain would be 7+3+1+14+1+3+1=30 characters exactly, including http:// and a trailing slash.
+ I suppose I should be thankful that you don't seem to be filtering out *.blogspot.com completely, as I've seen suggested elsewhere on the 'Net.
+ Regarding your filtering URLs containing .html, ?, or &, does that apply to the comment body or just the URL field? Some blog platforms and content sites (PC World for example) end their pages in .html, and/or use query strings to retrieve the correct page, so I'm just curious about that.}
+ },
+ {
+ :author => 'Paul Decowski',
+ :email => '',
+ :url => '',
+ :body => %q{
+ Polish? Never heard of a word with 5 consonants in a row!
+ Wstrzyknąć — to inject.}
+ },
+ {
+ :author => 'Jamis Buck',
+ :email => '',
+ :url => '',
+ :body => %q{@Steve, for #1, I did the following (more or less) with Capistrano’s gateway class, which runs in a thread but must allow other threads to begin connections through the gateway:
+ <pre></code>require 'thread'
+
+ @mutex = Mutex.new
+ Thread.new do
+ loop do
+ @mutex.synchronize { @gateway.process(0.1) }
+ end
+ end
+
+ @mutex.synchronize do
+ @gateway.forward.local(1234, "remote-host", 22)
+ end
+
+ c = Net::SSH.start("localhost", "user", :port => 1234)</code</pre>
+ In other words, run the event loop manually (by looping and calling Net::SSH::Connection::Session#process manually), and wrap the #process call in a mutex. Then, any time you need to access the session outside of a thread, employ the mutex again.
+ As for #2, which “require” is failing?}
+ }
+ ]
+end
Please sign in to comment.
Something went wrong with that request. Please try again.