園児ニアのメモ

ただのエンジニア。何でも屋みたいな扱い受けてます。

【GAS】Twitterで相互フォローじゃない人を定期的に自動リムーブする

はじめに

Twitterをやっていると前は相互フォローだったのに片思いになってしまった人などがいると思います。
片思いというのは、自分は相手をフォローしているが相手は自分をフォローしていない状態のことです。

しかし、誰と片思いになったかどうかを確認して1人1人リムーブするのは意外と手間がかかりますよね。

かといって一括ツールを使うと公式アカウントなど片思いと知っておきながらフォローしているアカウントまで解除されてしまいます。

GoogleAppScript(以下GAS)でやれば自分の思い通りにできるのでやってみました。

一応、手順とメソッド単位での説明がありますが、最後にソースだけをまとめています。

GASとTwitterAPIの連携

過去記事でGASとTwitterAPIを連携させるライブラリを作成しています。
こちらを参考に導入までどうぞ
madgenius.hateblo.jp

フォロー、フォロワーの取得

片思いかどうかを調べるためには自分のフォローとフォロワーのリストが必要です。

フォローの取得

ELETwitterのTwitterFollowListを使用します。

//フォローしている人のリストを作成
function GetFollowList(screen_name,user_id) {
    var followCursor = -1;
    var followList = [];
    while (followCursor != 0) {
        var follow_list = ELETwitter.TwitterFollowList(5000,screen_name,user_id,followCursor);
        followCursor = 0;
        if (follow_list != null) {
            if (follow_list.errors == null) {
                followCursor = follow_list.next_cursor;
                followList = followList.concat(follow_list.ids);
            } else {
                break;
            }
        }
    }
    return followList;
}
フォロワーの取得

ELETwitterのTwitterFollowerListを使用します。

//フォロワーのリストを作成
function GetFollowerList(screen_name,user_id) {
    var followerCursor = -1;
    var followerList = [];
    while (followerCursor != 0) {
        var follower_list = ELETwitter.TwitterFollowerList(5000,screen_name,user_id,followerCursor);
        followerCursor = 0;
        if (follower_list != null) {
            if (follower_list.errors == null) {
                followerCursor = follower_list.next_cursor;
                followerList = followerList.concat(follower_list.ids);
            } else {
                break;
            }
        }
    }
    return followerList;
}

一応これでフォロー全件とフォロワー全件が取得できるはずです。
1回の通信で5000人分なので超有名人でもないかぎりAPI規制がかかったりすることはないはずです。

片思いのユーザーのリストを作成

片思いを判定するのにフォローとフォロワーのリスト配列の差集合を求めます。(差集合でフォローにいてフォロワーにいない人だけのリストになる)

自力でもでき、処理スピード的には変わらないですが、Underscoreというライブラリを使うことにしました。
GASの「リソース > ライブラリ」を開き、Underscoreのプロジェクトキー「1PcEHcGVC1njZd8SfXtmgQk19djwVd2GrrW1gd7U5hNk033tzi6IUvIAV」を入力して追加を押してください。
f:id:nanokanato:20170906121147p:plain:w300

以下が片思いのリストを作るメソッドです。
引数

  • followList - フォローのリスト
  • followerList - フォロワーのリスト
//片思いのリストを作成
function GetUnrequitedList(followList, followerList) {
    var unrequitedList = [];
    //フォローしているのにフォロワーじゃないユーザーは解除する
    var _ = Underscore.load();
    unrequitedList = _.difference(followList,followerList);
    return unrequitedList;
}

user_idをscreen_nameに変更

片思いのリストを作ったのであとはリムーブするだけ....
残念、user_idでリムーブ可能と公式のTwitterAPIに書いてますがuser_idで探したけど見つからないみたいなエラーが出てほとんどリムーブできません。

なのでscreen_name(@hogehoge)に変換してあげます。
この変換の時はなぜか見つからないエラーが起きないのが救い...

ELETwitterのTwitterUsersLookupで複数ユーザーを指定してその詳細からscreen_nameを取得します。

//user_idのリストをscreen_nameのリストに変更する
function ConvertScreenNameList(userIDList) {
    var screenNameList = [];
    while (userIDList.length > 0) {
        var idList = [];
        if (userIDList.length <= 100) {
            idList = userIDList.slice(0);
            userIDList = [];
        } else {
            idList = userIDList.slice(0,100);
            userIDList = userIDList.slice(100);
        }
        var ids = "";
        for (var i = 0; i < idList.length; i++) {
            var id = idList[i];
            if (ids != "") {
                ids += ",";
            }
            ids += id;
        }
        if (ids != "") {
            var lockups = ELETwitter.TwitterUsersLookup(null,ids);
            if (lockups == null) {
                break;
            } else {
                if (lockups.errors == null) {
                    for (var i = 0; i < lockups.length; i++) {
                        var lockup = lockups[i];
                        if (lockup != null) {
                            if (lockup.screen_name != null) {
                                screenNameList[screenNameList.length] = lockup.screen_name;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        }
    }
    return screenNameList;
}

これで片思いのユーザーのscreen_nameのリストが取得できました。

「var lockup = lockups[i];」の部分でユーザーの詳細があるので特定のユーザーは片思いでも見逃してスルーするなどの処理を入れることができます。(リムーブ時にscreen_nameで判別してでも良い)

上のメソッドを利用してリムーブまで行う

こんどこそ片思いのリストが用意できたのであとはリムーブするだけです。

リムーブはを使用します。


以下が片思いを取得してリムーブするメソッドです。
このメソッドをタイマーなどで呼べば定期的にリムーブすることができます。

//片思いを解除する
function UnFollow() {
    var user_id = "0000000000";
    //フォローしているユーザーのリストを作成
    var followList = GetFollowList(null,user_id);
    if (followList != null && followList.length > 0) {
        //フォロワーのリストを作成
        var followerList = GetFollowerList(null,user_id);
        if (followerList != null && followerList.length > 0) {
            //片思いのリストを作成
            var unrequitedList = GetUnrequitedList(followList, followerList);
            if (unrequitedList != null && unrequitedList.length > 0) {
                //user_idのリストをscreen_nameに変更
                var screenNameList = ConvertScreenNameList(unrequitedList);
                if (screenNameList != null && screenNameList.length > 0) {
                    //フォローを解除する
                    for (var i = 0; i < screenNameList.length; i++) {
                        var screen_name = screenNameList[i];
                        if (screen_name != null) {
                            var unfollow = ELETwitter.TwitterUnfollow(screen_name, null);
                            if (unfollow == null || (unfollow != null && unfollow.errors != null)) {
                                
                            }
                        }
                    }
                }
            }
        }
    }
}

全ソース

TwitterAPIの連携や通信周りをまとめたスクリプトファイル

AutoUnFollowを毎分のタイマーに設定することで毎日0時0分に片思いを解除してくれる。
Unfollow.gs

ELETwitter.TWITTER_CONSUMER_KEY = 'CONSUMER_KEY';
ELETwitter.TWITTER_CONSUMER_SECRET = 'CONSUMER_SECRET';
ELETwitter.OAUTH_USER_KEY = 'Unfollow';

//片思いフォローしているユーザーへのフォローを解除する
//毎日0時0分
function AutoUnFollow() {
    var Nowymdhms = new Date();
    var NowHour = Nowymdhms.getHours();
    var NowMin = Nowymdhms.getMinutes();
    if (NowHour == 0 && NowMin == 0) {
        UnFollow();
    }
}

//片思いを解除する
function UnFollow() {
    var user_id = "your user id";
    //フォローしているユーザーのリストを作成
    var followList = GetFollowList(null,user_id);
    if (followList != null && followList.length > 0) {
        //フォロワーのリストを作成
        var followerList = GetFollowerList(null,user_id);
        if (followerList != null && followerList.length > 0) {
            //片思いのリストを作成
            var unrequitedList = GetUnrequitedList(followList, followerList);
            if (unrequitedList != null && unrequitedList.length > 0) {
                //user_idのリストをscreen_nameに変更
                var screenNameList = ConvertScreenNameList(unrequitedList);
                if (screenNameList != null && screenNameList.length > 0) {
                    //フォローを解除する
                    for (var i = 0; i < screenNameList.length; i++) {
                        var screen_name = screenNameList[i];
                        if (screen_name != null) {
                            var unfollow = ELETwitter.TwitterUnfollow(screen_name, null);
                            if (unfollow == null || (unfollow != null && unfollow.errors != null)) {
                                
                            }
                        }
                    }
                }
            }
        }
    }
}

//user_idのリストをscreen_nameのリストに変更する
function ConvertScreenNameList(userIDList) {
    var screenNameList = [];
    while (userIDList.length > 0) {
        var idList = [];
        if (userIDList.length <= 100) {
            idList = userIDList.slice(0);
            userIDList = [];
        } else {
            idList = userIDList.slice(0,100);
            userIDList = userIDList.slice(100);
        }
        var ids = "";
        for (var i = 0; i < idList.length; i++) {
            var id = idList[i];
            if (ids != "") {
                ids += ",";
            }
            ids += id;
        }
        if (ids != "") {
            var lockups = ELETwitter.TwitterUsersLookup(null,ids);
            if (lockups == null) {
                break;
            } else {
                if (lockups.errors == null) {
                    for (var i = 0; i < lockups.length; i++) {
                        var lockup = lockups[i];
                        if (lockup != null) {
                            if (lockup.screen_name != null) {
                                screenNameList[screenNameList.length] = lockup.screen_name;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        }
    }
    return screenNameList;
}

//片思いのリストを作成
function GetUnrequitedList(followList, followerList) {
    var unrequitedList = [];
    //フォローしているのにフォロワーじゃないユーザーは解除する
    var _ = Underscore.load();
    unrequitedList = _.difference(followList,followerList);
    return unrequitedList;
}

//フォローしている人のリストを作成
function GetFollowList(screen_name,user_id) {
    var followCursor = -1;
    var followList = [];
    while (followCursor != 0) {
        var follow_list = ELETwitter.TwitterFollowList(5000,screen_name,user_id,followCursor);
        followCursor = 0;
        if (follow_list != null) {
            if (follow_list.errors == null) {
                followCursor = follow_list.next_cursor;
                followList = followList.concat(follow_list.ids);
            } else {
                break;
            }
        }
    }
    return followList;
}

//フォロワーのリストを作成
function GetFollowerList(screen_name,user_id) {
    var followerCursor = -1;
    var followerList = [];
    while (followerCursor != 0) {
        var follower_list = ELETwitter.TwitterFollowerList(5000,screen_name,user_id,followerCursor);
        followerCursor = 0;
        if (follower_list != null) {
            if (follower_list.errors == null) {
                followerCursor = follower_list.next_cursor;
                followerList = followerList.concat(follower_list.ids);
            } else {
                break;
            }
        }
    }
    return followerList;
}

余談

1回の実行で片思いが多すぎる人などは途中でTwitterAPIの規制や、GASのURLFetchの規制で処理が途中で終わる場合があります。

過去記事の応用などもあり、省略部分があるかと思いますので不明点やエラーが出た場合はコメントにお願いします。