3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS】クラス化でLINE-botを爆速で作成する

Last updated at Posted at 2024-03-30

LINEのbotを作ると、記述が冗長になったり条件分岐が多すぎたりしたので、クラスにして簡単に書けるようにしました。

ソースコード

おうむ返しbotを作成します。
const client = new Client_("ACCESS_TOKEN");
client.on("message",event => {
    const text = event.message.text;
    event.reply(text);

});

function doPost(e){
    const data = JSON.parse(e.postData.contents);
    client.run(data);
}

完成しました。(1分)

ただし、これではClient_が定義されていないので、次のソースコードをコピペしてください。

クラスの用意

class Client_{

    constructor(ACCESS_TOKEN){
        this.token = ACCESS_TOKEN;
        this.users = new UserManager_(this);
        this.followers = new FollowerManger(this);
        this.https = HttpsRequest_(this);
        this.functions = {};
    }

    on(type,func){
        const arr = this.functions[type] || [];
        arr.push(func);
    }

    onFailed(func){ //funcの引数は、event,err

        this.failedHandler = func;

    }
    run(data){

        const [events] = data.events;
        this.functions[events.type].forEach(f => {
            try{
                f(new Events_(events,this));
            }catch(err){
                this.failedHandler(events,err);
            }
        });

    }

}


class FollowerManger{

    constructor(client){
        this.client = client;
    }
    /**
     * 
     * @param {string} userId 
     * @returns {User_}
     */
    fetch(userId){

        const endpoint = "https://api.line.me/v2/bot/profile/" + userId;
        
        const userdata = this.client.https.get(endpoint);
        // {
        //     "displayName": "LINE taro",
        //     "userId": "U4af4980629...",
        //     "language": "en",
        //     "pictureUrl": "https://obs.line-apps.com/...",
        //     "statusMessage": "Hello, LINE!"
        //   }

        return new User_(client,userdata);
    }
    /**
     * 
     * @param {object} {limit,start} 
     */
    getAllFollowers({limit,start}){
        const endpoint = "https://api.line.me/v2/bot/followers/ids";
        const requestUrl = new URLSearchParams(endpoint).setParams({limit,start}).toString();
        return this.client.https.get(requestUrl);
        // {
        //     "userIds": ["U4af4980629...", "U0c229f96c4...", "U95afb1d4df..."],
        //     "next": "yANU9IA..."
        //   }

    }

}

class UserManager_{

    constructor(client){
        this.client = client;

    }
    /**
     * 
     * @param {string} userId 
     * @returns {User_}
     */
    fetch(userId){
        const endpoint = "https://api.line.me/v2/bot/profile/" + userId;
        
        const userdata = this.client.https.get(endpoint);
        // {
        //     "displayName": "LINE taro",
        //     "userId": "U4af4980629...",
        //     "language": "en",
        //     "pictureUrl": "https://obs.line-apps.com/...",
        //     "statusMessage": "Hello, LINE!"
        //   }

        return new User_(this.client,userdata);
    }

}


class User_{
    /**
     * 
     * @param {Client_} client 
     * @param {JSON} userdata 
     */
    constructor(client,userdata){

        this.displayName = userdata.displayName;
        this.userId = userdata.userId;
        this.language = userdata.language;
        this.pictureUrl = userdata.pictureUrl;
        this.statusMessage = userdata.statusMessage;
        this.client = client;

    }

    updateInfo(){

        const endpoint = "https://api.line.me/v2/bot/profile/" + this.userId;
        
        const userdata = this.client.https.get(endpoint);

        return new User_(this.client,userdata);

    }

    toString(){
        return `<m userId="${this.userId}">`
    }

}


class Events_{

    constructor(events,client){

        this.client = client;
        this.rawdata = events;
        this.type = events.type;
        this.replyToken = events.replyToken;
        this.source = new User_(client,events.source);
        this.timestamp= events.timestamp;
        this.message = events.message;
        this.postback = events.postback;
        this.follow = events.follow;

    }

    reply(messages){
        if(typeof messages === "string"){
            messages = {
                type : "text",
                text : messages
            };
        }

        if(!Array.isArray(messages)){
            messages = [messages];
        }
        
        const endpoint = "https://api.line.me/v2/bot/message/reply";
        const payload=  {
            messages : messages,
            replyToken: this.replyToken
          };

        this.client.post(endpoint,payload)
        
    }



}



class HttpsRequest_{

    constructor(client){
        this.client = client
    }
    /**
     * 
     * @param {string} url 
     * @param {object} params 
     * @returns {JSON}
     */
    get(url,params){

        const requestUrl = new URLSearchParams(url).setParams(params).toString();
        const options = {
            method: 'get',
            contentType: 'application/json',
            headers: {
              Authorization: 'Bearer ' + this.client.token
            }
          };
        const result =  UrlFetchApp.fetch(requestUrl,options).getContentText();
        return JSON.parse(result);

    }

    /**
     * 
     * @param {string} url 
     * @param {object} payload 
     */
    post(url,payload){

        const options = {
            method: 'post',
            contentType: 'application/json',
            headers: {
              Authorization: 'Bearer ' + this.client.token
            },
            payload: JSON.stringify(payload) 
        };
        const result = UrlFetchApp.fetch(url,options).getContentText();
        return JSON.parse(result);

    }


}

class URLSearchParams{
    constructor(url){

        this.originUrl = url.split("?")?.[0];
        this.param = url.split("?")?.[1] || "";

    }

    setParams(obj){
        this.param = Object.entries(obj).filter(arr => arr[1]).map(arr => arr.join("=")).join("&");
    }

    toString(){
        return this.originUrl + "?" +  this.param;
    }
}

(記法はdiscord.jsリスペクトです。)

使い方(簡易)

詳細は次回以降に回します。
//メイン
const client = new Client_("ACCESS_TOKEN");
function doPost(e){
    const data = JSON.parse(e.postData.contents);
    client.run(data);
}

//メッセージイベントハンドラのセット。
client.on("message",event => {

    //eventは、e.postData.contents.events[0]とほぼ同等。
    const text = event.message.text; //"こんにちは"

    //rawdataはevent.rawdata。doPostで受け取ったJSONがそのまま入っている

    //replyメソッドでquick-replyが可能。
    event.reply(text);

});
//以下の内容と同等の処理をしている。
function doPost(e){
    const data = JSON.parse(e.postData.contents);
    const event = data.events[0];

    if(event.type !== "message") return;

    const text = event.message.text;
    const replyToken = event.replyToken;

    UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
      'headers': {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer ' + ACCESS_TOKEN,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'replyToken': replyToken,
        'messages': [{
          'type': 'text',
          'text': text,
        }]
      })
    });
}

//フォローイベント
client.on("follow",event => {});
//アンフォローイベント
client.on("unfollow",event => {});
//postbackイベント
client.on("postback",event => {});
//その他イベントも同様に記述可能。

//失敗時のハンドラ
client.onFailed((event,err) => {})

//ユーザー取得
client.users.fetch("U4af4980629...");

//友達ID取得
client.followers.getAllFollowers({limit,start});

//メンション
client.on("follow",event => {
    const user = event.source; /**@type {User_} */
    const message = "登録ありがとうございます!";
    event.reply(user.toString() + message); //メンションつきメッセージ
})

//その他、Access tokenが必要なLINEのAPIを叩くとき
//get
client.https.get("url",{param1 : value1});
//post
const payload = {};
client.https.post("url",payload); 

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?