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