-
Notifications
You must be signed in to change notification settings - Fork 0
/
function.go
241 lines (201 loc) · 6.03 KB
/
function.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package function
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/ozame/sticker-simp/imaging"
)
var TOKEN string = os.Getenv("TELEGRAM_BOT_TOKEN")
var REQUESTURL string = "https://api.telegram.org/bot" + TOKEN + "/"
type Update struct {
UpdateId int `json:"update_id"`
Message Message `json:"message"`
}
type Message struct {
Text string `json:"text"`
Chat Chat `json:"chat"`
Document Document `json:"document"`
Photos []Photo `json:"photo"`
}
type Photo struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
}
type Document struct {
FileID string `json:"file_id"`
FileName string `json:"file_name"`
}
// A Telegram Chat indicates the conversation to which the message belongs.
type Chat struct {
Id int64 `json:"id"`
}
type FileResult struct {
Ok bool `json:"ok"`
File File `json:"result"`
}
type File struct {
FileID string `json:"file_id"`
FilePath string `json:"file_path"`
}
func init() {
functions.HTTP("sticker-bot", RunStickerCreation)
}
// parseTelegramRequest handles incoming update from the Telegram web hook
func parseTelegramRequest(r *http.Request) (*Update, error) {
var update Update
if err := json.NewDecoder(r.Body).Decode(&update); err != nil {
log.Printf("could not decode incoming update %s", err.Error())
return nil, err
}
r.Body.Close()
return &update, nil
}
// sendPhotoToTelegramChat sends a text message to the Telegram chat identified by its chat Id
func sendPhotoToTelegramChat(chatId int64, reader *os.File) error {
log.Printf("Sending photo to chat_id: %d", chatId)
var telegramApi string = REQUESTURL + "sendDocument"
values := map[string]io.Reader{
"chat_id": strings.NewReader(fmt.Sprintf("%d", chatId)),
"document": reader,
}
err := Upload(chatId, telegramApi, values)
if err != nil {
log.Printf("Sending photo failed")
}
return err
}
// sendTextToTelegramChat sends a text message to the Telegram chat identified by its chat Id
func sendTextToTelegramChat(chatId int, text string) (string, error) {
log.Printf("Sending %s to chat_id: %d", text, chatId)
var telegramApi string = "https://api.telegram.org/bot" + TOKEN + "/sendMessage"
response, err := http.PostForm(
telegramApi,
url.Values{
"chat_id": {strconv.Itoa(chatId)},
"text": {text},
})
if err != nil {
log.Printf("error when posting text to the chat: %s", err.Error())
return "", err
}
defer response.Body.Close()
var bodyBytes, errRead = io.ReadAll(response.Body)
if errRead != nil {
log.Printf("error in parsing telegram answer %s", errRead.Error())
return "", err
}
bodyString := string(bodyBytes)
log.Printf("Body of Telegram Response: %s", bodyString)
return bodyString, nil
}
// RunStickerCreation edits the received image and sends the result back to the chat
func RunStickerCreation(w http.ResponseWriter, r *http.Request) {
// Parse incoming request
var update, err = parseTelegramRequest(r)
if err != nil {
log.Printf("error parsing update, %s", err.Error())
w.WriteHeader(http.StatusOK)
return
}
log.Printf("%v", update)
// Getting the photo file uri
if (len(update.Message.Photos)) <= 0 {
log.Printf("Warn - No photos included in the message")
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusAccepted)
go handlePhotoFromUpdate(update)
}
func handlePhotoFromUpdate(update *Update) {
bigPic := update.Message.Photos[len(update.Message.Photos)-1]
getFileUrl := REQUESTURL + "getFile" + "?file_id=" + bigPic.FileID
log.Printf("Getting file info from %s", getFileUrl)
fileResp, err := http.Get(getFileUrl)
if err != nil {
log.Printf("Getting file failed")
}
var fileResult FileResult
if err := json.NewDecoder(fileResp.Body).Decode(&fileResult); err != nil {
log.Printf("could not decode incoming file %s", err.Error())
}
//Download the file
fileUrl := "https://api.telegram.org/file/bot" + TOKEN + "/" + fileResult.File.FilePath
log.Printf("Downloading file from %s", fileUrl)
fileDownloadResp, err := http.Get(fileUrl)
if err != nil {
log.Printf("Downloading file failed")
} else {
log.Printf("Photo was downloaded successfully")
}
tmp, _ := os.CreateTemp(".", "tmp*.png")
// not closing tmp file since it's needed later in goroutine
imaging.RecodeAndScale(fileDownloadResp.Body, tmp)
tmp.Seek(0, 0)
log.Printf("Scaled the image")
// Send the edited picture image back to Telegram
var errTelegram = sendPhotoToTelegramChat(update.Message.Chat.Id, tmp)
if errTelegram != nil {
log.Printf("got error %s from telegram server", errTelegram.Error())
} else {
log.Printf("Edited picture was successfully shared to chat id %d", update.Message.Chat.Id)
}
}
func Upload(chatId int64, url string, values map[string]io.Reader) (err error) {
// Prepare a form that you will submit to that URL.
var b bytes.Buffer
w := multipart.NewWriter(&b)
for key, r := range values {
var fw io.Writer
if x, ok := r.(io.Closer); ok {
defer x.Close()
}
// Add an image file
if x, ok := r.(*os.File); ok {
defer os.Remove(x.Name())
if fw, err = w.CreateFormFile(key, x.Name()); err != nil {
return
}
} else {
// Add other fields
if fw, err = w.CreateFormField(key); err != nil {
return
}
}
if _, err = io.Copy(fw, r); err != nil {
return err
}
}
// Don't forget to close the multipart writer.
// If you don't close it, your request will be missing the terminating boundary.
w.Close()
// Now that you have a form, you can submit it to your handler.
req, err := http.NewRequest("POST", url, &b)
if err != nil {
return
}
// Don't forget to set the content type, this will contain the boundary.
req.Header.Set("Content-Type", w.FormDataContentType())
log.Printf("making request to address %s", url)
// Submit the request
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return
}
// Check the response
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("bad status: %v", res)
}
return
}