84139
**したいこと**  
GASでスプレットシートの情報を取得し、複数のdiscordのチャンネルに文章として送信するプログラムを作りたいと考えています。

このプログラムは、スプレットシートを二つ使います。  
一つ目のsheet1には送信するデータなどが記入されていてget_value()関数でデータを取得しています。  
sheet１は表となっていて、いくつかのデータを取得して送信する流れになっています。  
そのさい、 **F列で取得できるhaisintimeの値が0であった場合は送信しないようにする** ため、  
values.filterで取り除いています。

二つ目のsheet2はdiscordに送信するうえで必要である、webhookURLとtokenがチャンネルごとに記入されています。

sheet1とsheet2は連動しています。 **例えばsheet1のC列とsheet2のB列にdiscordのチャンネル名が書かれており**
、それは上から全く同じになっていて、sheet1も2もそのチャンネルのデータが行に書かれています。

[![画像の説明をここに入力](https://i.stack.imgur.com/SpYuj.png)](https://i.stack.imgur.com/SpYuj.png)  
[![画像の説明をここに入力](https://i.stack.imgur.com/HZ9gV.png)](https://i.stack.imgur.com/HZ9gV.png)

**こまっていること**  
私のコードを実行すると次のようになります。(誕生日と名前が変だが、例文のために気にしなくていい）

```

    14:00:34    お知らせ    実行開始
    14:00:36    情報  test1 : 期間は"12/1~12/8"です。私は今年で7歳になります。誕生日は6670です。名前はC3です。
    14:00:37    情報  test2 : 期間は"12/1~12/8"です。私は今年で15歳になります。誕生日は12540です。名前はB2です。
    14:00:38    情報  test3 : 期間は"12/1~12/8"です。私は今年で10歳になります。誕生日は7560です。名前はC2です。
    14:00:38    情報  test4 : 期間は"12/1~12/8"です。私は今年で16歳になります。誕生日は43390です。名前はB3です。
    14:00:39    情報  test5 : 期間は"12/1~12/8"です。私は今年で11歳になります。誕生日は2790です。名前はC3です。
    14:00:40    情報  test6 : 期間は"12/1~12/8"です。私は今年で6歳になります。誕生日は4370です。名前はC2です。
    14:00:41    お知らせ    実行完了
    
```

ここでsheet1のtest1のデータを見てもらうと0時間になっています。0時間なのでtest1というdiscordチャンネルにはデータを送信したくはないのですが、test2のデータが送信されてしまっているのがわかります。  
この先にも0時間があったとすると、どんどん後ろの間違ったデータが送信されることになってしまいます。

本来ならこのようになるはずです。

```

    情報  test2 : 期間は"12/1~12/8"です。私は今年で12歳になります。誕生日は22280です。名前はAです。
    情報  test3 : 期間は"12/1~12/8"です。私は今年で15歳になります。誕生日は12540です。名前はB2です。
    情報  test4 : 期間は"12/1~12/8"です。私は今年で10歳になります。誕生日は7560です。名前はC2です。
    情報  test5 : 期間は"12/1~12/8"です。私は今年で16歳になります。誕生日は43390です。名前はB3です。
    情報  test6 : 期間は"12/1~12/8"です。私は今年で11歳になります。誕生日は2790です。名前はC3です。
    
```

**聞きたいこと**  
おそらく、sheet1だけをhaisintime の値が0は除くというフィルターにかけていることが原因だと思います。  
**sheet2でsheet1でフィルターをかけた行に該当するチャンネルには送らないよう、sheet2にもフィルターをかけるとするのであればどのようにすればよいでしょうか？**

どのようにすれば正しく送信することができるでしょうか？

**実際のコード**

```

    function submit(){
      discord()
    };
    
    function get_value() {
      var sheet1 = SpreadsheetApp.getActiveSheet();
      
      var [[date], [format]] = sheet1.getRange("C2:C3").getValues();
      format = format.replace(/[ 　"+]+/g, "").replace("date", date);
      var values = sheet1.getRange("D11:R" + sheet1.getLastRow()).getValues();
      var messages_array = values.filter(r => r[2] != 0).map(([allpoint,,haisintime,haisinkaisu,,,,,,,,,,,comrank]) =>
        Object.entries({allpoint, haisintime, haisinkaisu, comrank}).reduce((s, e) => s.replace(...e).replace(/\\n/g, "\n"), format)
      );
      return messages_array;
    }
    
    //googleスプレットシート  自動送信
    function get_sheet(gss_url,sheet_num) {
      var ss = SpreadsheetApp.openByUrl(gss_url);
      var sheet = ss.getSheets()[sheet_num];
      return sheet;
    };
    
    
    function get_sheet2(gss_url,sheet_num) {
      var discord = SpreadsheetApp.openByUrl(gss_url);
      var sheet2 = discord.getSheets()[sheet_num];
      return sheet2;
    };
    
    function discord(message) {
        var sheet2 = get_sheet2('https://docs.google.com/spreadsheets/d/yyyyyyyyyyyyyyyy',0);
    
        var messages = get_value();  // call get_value() at here
    
        var lastRow2 = sheet2.getLastRow();  //最終行取得
        
        for (let j = 11; j <= lastRow2; j++) {
    
          //webhook
          var webhookcoord = sheet2.getRange(j,3);
          var webhook = webhookcoord.getValue();
    
        //token
          var tokencoord = sheet2.getRange(j,4);
          var dtoken = tokencoord.getValue();
          
      　//channel
          var channelcoord = sheet2.getRange(j,2);
          var dchannel = channelcoord.getValue();
          
        //format
          const url        = webhook;
          const token      = dtoken;
          const channel    = dchannel;
          const text       = messages[j-11];
          if (text === 'None') {
            console.log(channel + " : 送信なし"); 
            continue;
          }
    
          
          
    
          const username   = '〇〇〇';
          const avatar_url = "http://drive.google.com/uc?export/aaaaaaaaaaaaaaaaaaa";
          const parse      = 'full';
          const method     = 'post';
    
          const payload = {
              'token'      : token,
              'channel'    : channel,
              "content"    : text,
              'username'   : username,
              'parse'      : parse,
              'avatar_url' : avatar_url,
          };
    
          const params = {
              'method' : method,
              'payload' : payload,
              'muteHttpExceptions': true   
    
          };
          Utilities.sleep(500);
          
        response = UrlFetchApp.fetch(url, params);
        //実行ログ
        console.log(channel + " : " + text); 
    
    
        }
        
    }
    
    
```

**追記1**  
上記を修正したところ以下のようなエラーになりました。  
こちらはどういったことが問題なのでしょうか？

```

    エラー 
    Exception: You do not have permission to access the requested document.
    get_sheet2  @ コード.gs:39
    discord @ コード.gs:45
    submit  @ コード.gs:2
    
```
CC BY-SA 4.0
2021-12-12T05:11:14.797
84071
2022-01-06T22:49:01.733
2021-12-15T04:51:20.660
47546
47546
question
google-apps-script google-spreadsheet
GASでdiscordのチャンネルに送信したいのだが正しい人に送信されない。
`discord` 関数の中の

```

    const text       = messages[j-11];
    
```

で、 `channel`(discord名前)に対応したメッセージを取得しようとしていると思われますが、実装はそうなっていません。

* * *

sheet1, sheet2 とも `channel`(discord名前)
を識別子としたレコードを管理しているので、データ構造もそれに倣えば整理しやすいかと思います。

#### 具体的には

`get_value` 関数の戻り値を、 `channel` をキーにした `Map` に変更します:

```

    function get_value() {
      var sheet1 = SpreadsheetApp.getActiveSheet();
    
      var [[date], [format]] = sheet1.getRange("C2:C3").getValues();
      format = format.replace(/[ 　"+]+/g, "").replace("date", date);
      // C列(channel(discord名前)) を含める
      var values = sheet1.getRange("C11:R" + sheet1.getLastRow()).getValues();
      var messages_array = values
        .filter((r) => r[3] != 0)
        .map(
          ([channel, allpoint, , haisintime, haisinkaisu, , , , , , , , , , , comrank]) => {
            const message = Object.entries({
              allpoint,
              haisintime,
              haisinkaisu,
              comrank,
            }).reduce((s, e) => s.replace(...e).replace(/\\n/g, "\n"), format);
    
            return [channel, message];
          }
        );
    
      return new Map(messages_array);
    }
    
```

冒頭記載したメッセージ取得処理を修正します:

```

        const text = messages.get(channel);
        if (!text) {
          console.log(channel + " : 送信なし");
          continue;
        }
    
```

* * *

**追記1** で記載されているエラーについては、対応後のコードの `39`
行目がどこなのかを確認してみてください。おそらく下記コメント箇所ではないかと思います:

```

    function get_sheet2(gss_url,sheet_num) {
      var discord = SpreadsheetApp.openByUrl(gss_url); // おそらくここが 39 行目
      var sheet2 = discord.getSheets()[sheet_num];
      return sheet2;
    };
    
```

そうであれば、 `sheet2` として参照しようとしているシートが所属するスプレッドシート(質問文のコード中では
`https://docs.google.com/spreadsheets/d/yyyyyyyyyyyyyyyy`
と例示しているもの)への参照権限が無いため発生しているエラーです。

質問文記載時点で実行できていたということは、コード中のURLか、スプレッドシートの参照権限の設定を変更したのだと思いますが、どうでしょう。  
もし変更されたのであれば元に戻して実行してみてください。

* * *

[コメント](https://ja.stackoverflow.com/questions/84071/gas%e3%81%a7discord%e3%81%ae%e3%83%81%e3%83%a3%e3%83%b3%e3%83%8d%e3%83%ab%e3%81%ab%e9%80%81%e4%bf%a1%e3%81%97%e3%81%9f%e3%81%84%e3%81%ae%e3%81%a0%e3%81%8c%e6%ad%a3%e3%81%97%e3%81%84%e4%ba%ba%e3%81%ab%e9%80%81%e4%bf%a1%e3%81%95%e3%82%8c%e3%81%aa%e3%81%84/84139?noredirect=1#comment96960_84139)
での追加質問について:

> getvalue()関数の .filter((r) => r[2] != 0) のr とはいったい何を指しているのでしょうか？

まず、この部分のコードが誤っていましたので回答を更新しています。正しくは `values.filter((r) => r[3] != 0)`になります。

次にこのコードの意味ですが、これは質問文にある次の処理に該当します。

> F列で取得できるhaisintimeの値が0であった場合は送信しないようにするため、  
>  values.filterで取り除いています。

C列目を0列目としているので、F列は3列目になります。そのため抽出する条件は `r[3] != 0` となります。  
(※質問文中コードではD列以降を取得していますが、回答コードではC列以降を取得するように変更しています。)

> また、その下の.reduceのところで出てくるsやeなどもどのような意味合いで使用されているかを教えていただけるとありがたいです。

この点は質問文中のコードから変更はありません。  
[`reduce()`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
は配列の要素を畳み込んで1つの値にする関数です。  
今回は少し変わった使い方をしていますが、

  * `s` は初期値を `format`(つまり`" "期間は12/1〜12/8です。私は今年でhaisintime歳になります。誕生日はallpointです。名前はcomrankです。"`) とする文字列
  * `e` は、 `Object.entries()` によって生成された `["allpoint", <D列の値>]` のような、列名と列の値を組にした配列

となります。  
具体的に何をしているかというと、  
`"期間は12/1〜12/8です。私は今年でhaisintime歳になります。誕生日はallpointです。名前はcomrankです。"` という文字列内の
"haisintime" などの列名をsheet1に記載されている実際の値に置換している、ということになります。

簡単な例で見てみると理解しやすいかと思います:

```

    const format = "吾輩はkindである。名前はmy_name。";
    const variables = [
      ["kind", "猫"],
      ["my_name", "まだ無い"],
    ];
    
    const text = variables.reduce((s, e) => s.replace(...e), format);
    //// ↓ s.replace(...e); の変数 s, e を具体的な値に置き換えると:
    // "吾輩はkindである。名前はmy_name。".replace("kind", "猫");
    // "吾輩は猫である。名前はmy_name。".replace("my_name", "まだ無い");
    
    console.log(text);
```
CC BY-SA 4.0
2021-12-14T23:20:43.137
84139
2022-01-06T22:49:01.733
2022-01-06T22:49:01.733
2808
2808
84071
answer