2011年5月14日土曜日

cl-twitter を使ったフォロワーを自動で増やすサンプルプログラム


このエントリーをはてなブックマークに追加


Twitterのフォロワーを増え方を調査した時に見つけた、フォロワーを自動で増やすプログラムをまねて作ってみた。

1週間ほど動かしたところ、1000 → 3000までフォロワーが増えました。

勉強がてら作っているので、かなりコードはしょぼいです。エラー処理はコンディションが良く分からなかったので無視。リスト処理も集合処理を知らなかったので力技で処理してます。


Win7 64bit + SBCL or CCL で動作しました。
必要なパッケージは事前にquicklispで取得しています。
(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :cl-twit-repl))

(defpackage :incr-follower
    (:use :cl :cl-twit-repl :cl-twitter)
    (:export #:main))

(in-package :incr-follower)

(defvar *owner-screen-name* "username")
(defvar *follow-scale*      1.5)
(defvar *owner-id*           nil)

(defvar *follow-list/i->you*  nil)
(defvar *follow-list/you->me* nil)

(defvar *usernum* nil)

;; 現在時刻を取得する。
(defun now ()
  (let ((times (multiple-value-list (get-decoded-time))))
    (format t "~4d/~2d/~2d ~2d:~2d:~2d :"
     (sixth  times)
     (fifth  times)
     (fourth times)
     (third  times)
     (second times)
     (first  times))))

;; APIリクエストの上限を出力
(defun check-api-limit ()
  (let ((obj (rate-limit-status)))
    (format t " api limit ~3d/~3d~%"
     (rate-limit-remaining-hits obj)
     (rate-limit-hourly-limit obj))))


;; ユーザ名からIDを取得する
(defun get-user-id-by-screen-name (screen-name)
  (twitter-user-id (get-user screen-name)))

;; IDからユーザ名を取得する
(defun get-screen-name-by-id (id)
  (twitter-user-screen-name (show-user-by-id id)))

;; 操作するユーザのIDをセットする。
(defun set-owner-user-id ()
  (setf *owner-id* (get-user-id-by-screen-name *owner-screen-name*)))

;; 自分がフォローしている人をセットする。
(defun set-follower-list/i->you ()
  (setf *follow-list/i->you* (collect-friend-ids *owner-screen-name*)))

;; 自分をフォローしている人をセットする。
(defun set-follower-list/you->me ()
  (setf *follow-list/you->me* (collect-follower-ids *owner-screen-name*)))

;; リストから指定ユーザを除去する
(defun delete-my-id (list userid)
  (remove-if #'(lambda (x) (= x userid)) list))

;; 自分はフォローしているが、自分をフォローしていないユーザを抽出する。
(defun collect-unfollower-id-list (i->you you->me)
  (let ((friends   (delete-my-id i->you  *owner-id*))
 (followers (delete-my-id you->me *owner-id*)))
    (loop for i in followers
   do (setf friends (remove-if #'(lambda (x) (= x i)) friends)))
    friends))

;; unfollowerを48で割った数を一回の処理量とする。二日でunfollowerを一巡させるため。
;; 割った結果が40以上の場合は40を上限とする、これは一日にフォローできる人数が1000人なため(*40 24)
;; 最低でも1人はフォローする。
(defun calc-exec/hour ()
  (setf *usernum* (floor
     (/ (list-length
        (collect-unfollower-id-list *follow-list/i->you* *follow-list/you->me*))
       48)))
  (cond
    ((< *usernum*  1) (setf *usernum* 1))
    ((> *usernum* 40) (setf *usernum* 40))
    (t *usernum*)))

;; リストの後ろから、指定人数分だけ抜き出したリストを作成する。
(defun pick-up-unfollow-user (lists num)
  ;; もしリストよりも指定人数が多い場合はnilを返す。
  (if (> num (list-length lists))
      (return-from pick-up-unfollow-user nil))
  (subseq lists (- (list-length lists) num) (list-length lists)))

;; リストで渡されたIDをunfollowする。
(defun unfollow-from-list (lists)
  (loop for i in lists
     do (progn
   (sleep 2)
   (format t "unfollow: ~a~%" (ignore-errors
           (twitter-op :FRIENDSHIPS/DESTROY :USER_ID i)))
   (force-output))))

;; リストからランダムにユーザを1人抜き出す
(defun return-a-random-user-id (lists)
  (let ((ids (list-length lists)))
    (if (= ids 0)
 (return-from return-a-random-user-id nil))
    (let ((target (random ids)))
      (if (or
    (= target 0)
    (= target ids))
   (return-from return-a-random-user-id (car lists)))
      (car (subseq lists (- target 1) target)))))

;; ユーザのフォローリストを取得し、既に自分がフォロー済みのユーザを削除する
(defun get-user-list-from-friend ()
  (loop
     do
       (let ((userlists nil)
      (randomuser
       (return-a-random-user-id (delete-my-id *follow-list/i->you* *owner-id*)))
      (friends (delete-my-id *follow-list/i->you* *owner-id*)))

  (setf userlists
        (ignore-errors
   (delete-my-id
    (collect-follower-ids
     (get-screen-name-by-id randomuser))
    *owner-id*)))

  (loop for i in friends
     do (setf userlists (remove-if #'(lambda (x) (= x i)) userlists)))

  (now)
  (format t "Picked up userlist from ~a : ~a followers~%" randomuser (list-length userlists))
  (force-output)

  (if (> (list-length userlists) 40)
      (return-from get-user-list-from-friend userlists)))))

;; リストからnum分だけのランダムにIDを抽出する。
(defun collect-random-user-for-following (lists num)
  (let ((templists lists))
    (remove-duplicates
     (delete-my-id
      (loop for i from 1 to (* num *follow-scale*)
  collect (return-a-random-user-id templists))
      *owner-id*))))


;; リストで渡されたIDをfollowする。
(defun follow-from-list (lists)
  (loop for i in lists
     do (progn
   (sleep 2)
   (format t "follow: ~a~%" (ignore-errors
         (twitter-op :FRIENDSHIPS/CREATE :USER_ID i)))
   (force-output))))


;; メイン処理
(defun main ()

  ;; 自分のIDをセット
  (set-owner-user-id)

  ;; OAuthをセット
  (get-authenticated-user *owner-screen-name*)

  (let ((i 1))
    (loop
       do
  (progn
    (now)
    (format t " ~3d loop start ------------------------------~%" i)
    (now)
    (check-api-limit)
    (force-output)

    (sleep 1)
    (set-follower-list/i->you)

    (sleep 1)
    (set-follower-list/you->me)

    (now)
    (format t " follower i to you  ~5d/~5d~%"
     (list-length *follow-list/i->you*)
     (floor (* 1.1 (list-length *follow-list/you->me*))))

    (now)
    (format t " follower you to me ~5d~%" (list-length *follow-list/you->me*))

    (now)
    (format t " unfollowers        ~5d~%"
     (list-length
      (collect-unfollower-id-list *follow-list/i->you* *follow-list/you->me*)))

    (now)
    (format t " ::: unfollow ~d   follow ~d~%"
     (calc-exec/hour)
     (floor (* *follow-scale* (calc-exec/hour))))

    (force-output)

    (ignore-errors
      (unfollow-from-list
       (pick-up-unfollow-user
        (collect-unfollower-id-list *follow-list/i->you* *follow-list/you->me*)
        *usernum*)))

    (ignore-errors
      (follow-from-list
       (collect-random-user-for-following
        (get-user-list-from-friend)
        *usernum*)))

    (now)
    (format t " ~3d loop end. wait 3600sec ------------------~%" i)
    (force-output)
    (setf i (+ i 1))

    (now)
    (check-api-limit)
    (force-output)

    (dotimes (j 4)
      (sleep  900)
      (now)
      (format t "    waited ~4d sec, remaining ~4d sec until restart. ~%"
       (* (+ j 1) 900)
       (- 3600 (* (+ j 1) 900)))
      (force-output))))))

0 件のコメント:

コメントを投稿