saleonlines.netに引っ掛かってしまったアカウントを検索するRubyスクリプト

1/10から大量発生しているsaleonlines.netスパムを検索するためのスクリプトです。
参考:saleonlines.net というスパムツイートに注意! - Togetterまとめ

30秒おきにツイートを検索します。検索1回に付き100ツイートまで検索出来ます(手抜きスクリプトのためそれ以上の検索は出来ません)。最短で5秒間隔にまで短縮することが出来ます(スクリプト末尾の sleep の数値を変更してください)。
twitter gem(バージョン5以上)だけ入ってれば動きます。バージョンは1.9.3から2.2.0まで(サポート中の全てのバージョン)で動きます。
Consumer Key / Secret もそのまま使えます。 Key / Secret はこのスクリプト専用として公開させて頂きます(権限はread onlyですので流用しての悪用はほとんど出来ません)。

…よく考えると、同じ Consumer Key / Secret を使うと、使用者合計の利用数でツイート規制が掛かりますねorz まぁ30秒間隔ぐらいにしておいてください(それでも6人まで使えるし)。

#!/usr/bin/env ruby
# coding: utf-8

require 'twitter'
require 'time'

SEARCH_KWS = [
  "saleonlines.net",
  "家電 新作の格安検索なら。定番からトレンド品まで豊富な品揃え!",
  "新店舗開催中!最大90%世界中のお買い得アイテムを続々出品!",
  "家電人気アウトレットなどが格安価格《期間限定》最大70%オフ!買うなら今",
]
keyword = SEARCH_KWS.map{|kw|'"' + kw + '"'}.join(' OR ')
max_id = nil

app_only_client = Twitter::REST::Client.new(
  consumer_key: 'mr1KTpTdoo5FM644RSaRpvGjM',
  consumer_secret: 'qmo6JNtiUsbZIDsCvBJn8t2B774LJ0Ucs9sjf2cf54wmOo8Q7m',
)

loop do
  puts "== " + Time.now.to_s + " =="
  retry_count = 0
  begin
    search_result = app_only_client.search(keyword, count: 100, lang: 'ja', locale: 'ja', since_id: max_id)
  rescue Twitter::Error::Forbidden => forbidden_error
    puts forbidden_error.message
  rescue Twitter::Error::ServiceUnavailable => over_capacity_error
    puts "(__-){ Twitter is over capacity. tweet retry..."
    sleep 3
    retry
  rescue Twitter::Error::ClientError => client_error
    puts client_error.message
  rescue Twitter::Error::ServerError => server_error
    puts "#{server_error.message} retry..."
    sleep 3
    retry
  rescue Timeout::Error => execution_expired
    puts "#{execution_expired.message} retry..."
    sleep 2
    retry
  rescue => ex
    puts ex
  end
  max_id = search_result.attrs[:search_metadata][:max_id]
  search_result.take(100).reverse.each do |tw| 
    client_name = tw.source[/>([^<>\/]+)<\/a>/, 1]
    text = tw.full_text.dup

    # expand t.co
    tco_repl = {}
    tw.urls.each do |u|
      tco_repl[u.url] = u.expanded_url
    end
    unless tco_repl.empty?
      tco_repl.each do |tco, expd|
        text.sub!(tco.to_s, expd.to_s)
      end
    end

    # expand t.co on media
    tco_media_repl = {}
    tw.media.each do |u|
      tco_media_repl[u.url] = u.display_url
    end
    unless tco_media_repl.empty?
      tco_media_repl.each do |tco, expd|
        text.sub!(tco.to_s, expd.to_s)
      end
    end

    puts "@#{tw.user.screen_name}: #{text}\n#{Time.parse(tw.created_at.to_s).localtime('+09:00')}|client: #{client_name}"
  end
  sleep 30  
end