github.com/line/line-bot-sdk-go/v7@v7.21.0/linebot/webhook.go (about)

     1  // Copyright 2016 LINE Corporation
     2  //
     3  // LINE Corporation licenses this file to you under the Apache License,
     4  // version 2.0 (the "License"); you may not use this file except in compliance
     5  // with the License. You may obtain a copy of the License at:
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package linebot
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"crypto/hmac"
    21  	"crypto/sha256"
    22  	"encoding/base64"
    23  	"encoding/json"
    24  	"io"
    25  	"net/http"
    26  )
    27  
    28  // ParseRequest method
    29  func (client *Client) ParseRequest(r *http.Request) ([]*Event, error) {
    30  	return ParseRequest(client.channelSecret, r)
    31  }
    32  
    33  // ParseRequest func
    34  func ParseRequest(channelSecret string, r *http.Request) ([]*Event, error) {
    35  	defer r.Body.Close()
    36  	body, err := io.ReadAll(r.Body)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	if !validateSignature(channelSecret, r.Header.Get("x-line-signature"), body) {
    41  		return nil, ErrInvalidSignature
    42  	}
    43  
    44  	request := &struct {
    45  		Events []*Event `json:"events"`
    46  	}{}
    47  	if err = json.Unmarshal(body, request); err != nil {
    48  		return nil, err
    49  	}
    50  	return request.Events, nil
    51  }
    52  
    53  func validateSignature(channelSecret, signature string, body []byte) bool {
    54  	decoded, err := base64.StdEncoding.DecodeString(signature)
    55  	if err != nil {
    56  		return false
    57  	}
    58  	hash := hmac.New(sha256.New, []byte(channelSecret))
    59  
    60  	_, err = hash.Write(body)
    61  	if err != nil {
    62  		return false
    63  	}
    64  
    65  	return hmac.Equal(decoded, hash.Sum(nil))
    66  }
    67  
    68  // GetWebhookInfo method
    69  func (client *Client) GetWebhookInfo() *GetWebhookInfo {
    70  	return &GetWebhookInfo{
    71  		c:        client,
    72  		endpoint: APIEndpointGetWebhookInfo,
    73  	}
    74  }
    75  
    76  // GetWebhookInfo type
    77  type GetWebhookInfo struct {
    78  	c        *Client
    79  	ctx      context.Context
    80  	endpoint string
    81  }
    82  
    83  // WithContext method
    84  func (call *GetWebhookInfo) WithContext(ctx context.Context) *GetWebhookInfo {
    85  	call.ctx = ctx
    86  	return call
    87  }
    88  
    89  // Do method
    90  func (call *GetWebhookInfo) Do() (*WebhookInfoResponse, error) {
    91  	res, err := call.c.get(call.ctx, call.c.endpointBase, call.endpoint, nil)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	defer closeResponse(res)
    96  	return decodeToWebhookInfoResponse(res)
    97  }
    98  
    99  // TestWebhook type
   100  type TestWebhook struct {
   101  	c        *Client
   102  	ctx      context.Context
   103  	endpoint string
   104  }
   105  
   106  // SetWebhookEndpointURLCall type
   107  type SetWebhookEndpointURLCall struct {
   108  	c   *Client
   109  	ctx context.Context
   110  
   111  	endpoint string
   112  }
   113  
   114  // SetWebhookEndpointURL method
   115  func (client *Client) SetWebhookEndpointURL(webhookEndpoint string) *SetWebhookEndpointURLCall {
   116  	return &SetWebhookEndpointURLCall{
   117  		c:        client,
   118  		endpoint: webhookEndpoint,
   119  	}
   120  }
   121  
   122  // WithContext method
   123  func (call *SetWebhookEndpointURLCall) WithContext(ctx context.Context) *SetWebhookEndpointURLCall {
   124  	call.ctx = ctx
   125  	return call
   126  }
   127  
   128  func (call *SetWebhookEndpointURLCall) encodeJSON(w io.Writer) error {
   129  	enc := json.NewEncoder(w)
   130  	return enc.Encode(&struct {
   131  		Endpoint string `json:"endpoint"`
   132  	}{
   133  		Endpoint: call.endpoint,
   134  	})
   135  }
   136  
   137  // Do method
   138  func (call *SetWebhookEndpointURLCall) Do() (*BasicResponse, error) {
   139  	var buf bytes.Buffer
   140  	if err := call.encodeJSON(&buf); err != nil {
   141  		return nil, err
   142  	}
   143  	res, err := call.c.put(call.ctx, APIEndpointSetWebhookEndpoint, &buf)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	defer closeResponse(res)
   148  	return decodeToBasicResponse(res)
   149  }
   150  
   151  // TestWebhook method
   152  func (client *Client) TestWebhook() *TestWebhook {
   153  	return &TestWebhook{
   154  		c:        client,
   155  		endpoint: APIEndpointTestWebhook,
   156  	}
   157  }
   158  
   159  // WithContext method
   160  func (call *TestWebhook) WithContext(ctx context.Context) *TestWebhook {
   161  	call.ctx = ctx
   162  	return call
   163  }
   164  
   165  // Do method
   166  func (call *TestWebhook) Do() (*TestWebhookResponse, error) {
   167  	res, err := call.c.post(call.ctx, call.endpoint, nil)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	defer closeResponse(res)
   172  	return decodeToTestWebhookResponse(res)
   173  }