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 }