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 件のコメント:
コメントを投稿