diff --git a/api/hook/index.go b/api/hook/index.go index 26481d9..2d1690b 100644 --- a/api/hook/index.go +++ b/api/hook/index.go @@ -2,21 +2,9 @@ package hook import ( "encoding/json" - "fmt" - "log" "net/http" - "strings" - "zeril-bot/utils/bitcoin" "zeril-bot/utils/bot" - "zeril-bot/utils/channel" - "zeril-bot/utils/help" - "zeril-bot/utils/kqxs" - "zeril-bot/utils/lunar" - "zeril-bot/utils/qr" - "zeril-bot/utils/random" - "zeril-bot/utils/shortener" "zeril-bot/utils/structs" - "zeril-bot/utils/weather" ) func Handler(w http.ResponseWriter, r *http.Request) { @@ -48,71 +36,3 @@ func Response(w http.ResponseWriter, res map[string]string, httpStatus int) { mRes, _ := json.Marshal(res) w.Write(mRes) } - -func resolveCommand(data structs.HookData) { - name := data.Message.From.FirstName - chatId := data.Message.Chat.ID - text := data.Message.Text - arr := strings.Fields(text) - - setBotIsTyping(chatId) - - log.Println(fmt.Sprintf("Yêu cầu từ bạn %s: %s", name, text)) - - command := arr[0] - - switch command { - case "/start", "/start@zerill_bot": - help.SendStartMessage(chatId, name) - case "/help", "/help@zerill_bot": - help.SendHelpMessage(chatId) - case "/groupid", "/groupid@zerill_bot": - help.SendGroupId(chatId, string(data.Message.Chat.Type)) - case "/quote", "/quote@zerill_bot": - // quote.SendAQuote(chatId) - case "/lunar", "/lunar@zerill_bot": - lunar.SendLunarDateNow(chatId) - case "/weather", "/weather@zerill_bot": - weather.SendForecastOfWeather(chatId, text) - case "/bitcoin", "/bitcoin@zerill_bot": - bitcoin.SendBitcoinPrice(chatId) - case "/qr", "/qr@zerill_bot": - qr.SendQRImage(chatId, text) - case "/random", "/random@zerill_bot": - random.RandomElements(chatId, text) - case "/kqxs", "/kqxs@zerill_bot": - kqxs.Send(chatId, text) - case "/shortener", "/shortener@zerill_bot": - shortener.Generate(chatId, text) - default: - channel.SendMessage(chatId, "Tôi không hiểu câu lệnh của bạn !!!") - } -} - -func resolveCallback(callback structs.HookData) { - name := callback.CallbackQuery.Message.From.FirstName - chatId := callback.CallbackQuery.Message.Chat.ID - text := callback.CallbackQuery.Message.Text - data := callback.CallbackQuery.Data - - setBotIsTyping(chatId) - - log.Println(fmt.Sprintf("Yêu cầu từ bạn %s: %s, callback data: %s", name, text, data)) - - arr := strings.Fields(data) - command := arr[0] - - switch command { - case "/weather": - weather.SendForecastOfWeather(chatId, data) - case "/kqxs": - kqxs.Send(chatId, data) - } -} - -func setBotIsTyping(chatId int) { - go func() { - // bot.SetTypingAction(chatId) - // channel.GetWg().Done() - }() -} diff --git a/api/index.go b/api/index.go index 73ea403..21bd870 100644 --- a/api/index.go +++ b/api/index.go @@ -12,16 +12,16 @@ import ( ) func Handler(wri http.ResponseWriter, req *http.Request) { - r := chi.NewRouter() r.Use(chiMiddle.Logger) - // r.Use(middleware.PreRequest) r.Use(middleware.Recoverer) r.NotFound(middleware.Handle404NotFound()) r.MethodNotAllowed(middleware.Handle405MethodNotAllowed()) - // r.Get("/", func(w http.ResponseWriter, r *http.Request) {}) + r.Get("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hi there !")) + }) r.Post("/api/hook", hook.Handler) r.Get("/url", url.Handler) r.Get("/trip", trip.Handler) diff --git a/utils/bitcoin/bitcoin.go b/utils/bitcoin/bitcoin.go index d5b6db9..bbd649d 100644 --- a/utils/bitcoin/bitcoin.go +++ b/utils/bitcoin/bitcoin.go @@ -4,78 +4,87 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "net/http" "strconv" - "zeril-bot/utils/channel" "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" "github.com/leekchan/accounting" ) -func SendBitcoinPrice(chatId int) { +func SendBitcoinPrice(data structs.DataTele) error { acUsd := accounting.Accounting{Symbol: "$", Precision: 2} acVnd := accounting.Accounting{Symbol: "", Precision: 0, Thousand: "."} - btc := getBitcoinPrice() - p, _ := strconv.ParseFloat(btc.Price, 64) + btc, err := getBitcoinPrice() + if err != nil { + return err + } + + p, err := strconv.ParseFloat(btc.Price, 64) + if err != nil { + return err + } + usd := acUsd.FormatMoney(p) + v, err := exchangeUsdToVnd(p) + if err != nil { + return err + } - v := exchangeUsdToVnd(p) - vnd := acVnd.FormatMoney(v) + " đ" + vnd := acVnd.FormatMoney(*v) + " đ" message := fmt.Sprintf("1 Bitcoin = %s (%s)", usd, vnd) - channel.SendMessage(chatId, message) + data.ReplyMessage = message + + return telegram.SendMessage(data) } -func getBitcoinPrice() structs.Btc { +func getBitcoinPrice() (*structs.Btc, error) { res, err := http.Get("https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT") - if err != nil { - log.Panic(err) + return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Panic(err) + return nil, err } var data structs.Btc err = json.Unmarshal(body, &data) if err != nil { - log.Panic(err) + return nil, err } - return data + return &data, nil } -func exchangeUsdToVnd(p float64) float64 { +func exchangeUsdToVnd(p float64) (*float64, error) { price := fmt.Sprintf("%.2f", p) - res, err := http.Get("https://api.exchangerate.host/convert?from=USD&to=VND&amount=" + price) + res, err := http.Get("https://api.exchangerate.host/convert?from=USD&to=VND&amount=" + price) if err != nil { - log.Panic(err) + return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Panic(err) + return nil, err } var data structs.Exchange err = json.Unmarshal(body, &data) if err != nil { - log.Panic(err) + return nil, err } - return data.Result + return &data.Result, nil } diff --git a/utils/bot/bot.go b/utils/bot/bot.go index fbfeccf..95da57c 100644 --- a/utils/bot/bot.go +++ b/utils/bot/bot.go @@ -1,29 +1,22 @@ package bot import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" + "fmt" "log" - "mime/multipart" - "net/http" - "os" - "path/filepath" - "strconv" "strings" - // "zeril-bot/utils/quote" + "zeril-bot/utils/bitcoin" + "zeril-bot/utils/kqxs" + "zeril-bot/utils/lunar" + "zeril-bot/utils/qr" "zeril-bot/utils/quote" + "zeril-bot/utils/random" + "zeril-bot/utils/shortener" "zeril-bot/utils/structs" "zeril-bot/utils/telegram" + "zeril-bot/utils/weather" ) -var API_URL string = "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") - -var chatType string -var chatFrom structs.From - type Bot struct { HookData structs.HookData typingCh chan struct{} @@ -38,10 +31,10 @@ func NewBot(hookData structs.HookData) *Bot { } func (b Bot) ResolveHook() error { - go b.setTypingAction() - var err error + go b.setTypingAction() + switch { case b.isCommand(): go b.resolveCommand() @@ -56,6 +49,23 @@ func (b Bot) ResolveHook() error { return err } +func (b Bot) getTelegramData() structs.DataTele { + data := b.HookData + rawMessage := b.getRawMessage() + name := data.Message.From.FirstName + + log.Printf("Yêu cầu từ bạn %s: %s", name, rawMessage) + + return structs.DataTele{ + ChatId: b.getChatId(), + ChatType: b.getChatType(), + Username: b.getUsername(), + FirstName: b.getFirstName(), + RawMessage: rawMessage, + Command: b.getCommand(), + } +} + func (b Bot) resolveCommand() error { var err error @@ -63,34 +73,55 @@ func (b Bot) resolveCommand() error { b.commandCh <- err }() - data := b.HookData - name := data.Message.From.FirstName - chatId := data.Message.Chat.ID - text := data.Message.Text - arr := strings.Fields(text) + data := b.getTelegramData() - log.Printf("Yêu cầu từ bạn %s: %s", name, text) - - tData := telegram.Data{ - ChatId: chatId, - ChatType: "", - Username: "", - } - - command := arr[0] - - switch command { + switch data.Command { + case "/start", "/start@zerill_bot": + err = b.sendStartMessage(data) + case "/help", "/help@zerill_bot": + err = b.sendHelpMessage(data) case "/quote", "/quote@zerill_bot": - err = quote.SendAQuote(tData) + err = quote.SendAQuote(data) + case "/groupid", "/groupid@zerill_bot": + err = b.sendGroupId(data) + case "/lunar", "/lunar@zerill_bot": + err = lunar.SendLunarDateNow(data) + case "/weather", "/weather@zerill_bot": + err = weather.SendForecastOfWeather(data) + case "/bitcoin", "/bitcoin@zerill_bot": + err = bitcoin.SendBitcoinPrice(data) + case "/qr", "/qr@zerill_bot": + err = qr.SendQRImage(data) + case "/random", "/random@zerill_bot": + err = random.RandomElements(data) + case "/kqxs", "/kqxs@zerill_bot": + err = kqxs.Send(data) + case "/shortener", "/shortener@zerill_bot": + shortener.Generate(data) default: - // channel.SendMessage(chatId, "Tôi không hiểu câu lệnh của bạn !!!") + err = b.invalidCommand(data) } - return nil + return err } func (b Bot) resolveCallbackCommand() error { - return nil + var err error + + defer func() { + b.commandCh <- err + }() + + data := b.getTelegramData() + + switch data.Command { + case "/weather": + err = weather.SendForecastOfWeather(data) + case "/kqxs": + err = kqxs.Send(data) + } + + return err } func (b Bot) isCommand() bool { @@ -101,243 +132,96 @@ func (b Bot) isCallbackCommand() bool { return b.HookData.CallbackQuery.Data != "" } -func (b Bot) getApiURL() string { - return "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") -} - -func SendMessage(chatId int, message string) { - if chatType == "group" { - message = message + "\n@" + chatFrom.Username - } - - uri := API_URL + "/sendMessage" - req, err := http.NewRequest("GET", uri, nil) - - if err != nil { - log.Println(err) - return - } - - q := req.URL.Query() - q.Add("chat_id", strconv.Itoa(chatId)) - q.Add("text", message) - q.Add("parse_mode", "html") - - req.URL.RawQuery = q.Encode() - - client := &http.Client{} - - res, err := client.Do(req) - - if err != nil { - log.Panic(err) +func (b Bot) getChatType() string { + if b.isCallbackCommand() { + return b.HookData.Message.Chat.Type } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - - if err != nil { - log.Panic(err) - } - - var status structs.Status - - err = json.Unmarshal(body, &status) - if err != nil { - log.Panic(err) - } - - if status.Ok == false { - log.Panic(string(body)) - } - - log.Println("SendMessage OK") + return b.HookData.Message.Chat.Type } -func SendPhoto(chatId int, path string) { - uri := API_URL + "/sendPhoto" - - file, _ := os.Open(path) - defer file.Close() - - payload := &bytes.Buffer{} - writer := multipart.NewWriter(payload) - writer.WriteField("chat_id", strconv.Itoa(chatId)) - - if chatType == "group" { - writer.WriteField("caption", "@"+chatFrom.Username) - } - - part, _ := writer.CreateFormFile("photo", filepath.Base(path)) - io.Copy(part, file) - - writer.Close() - - req, _ := http.NewRequest("GET", uri, payload) - req.Header.Set("Content-Type", writer.FormDataContentType()) - - client := &http.Client{} - res, err := client.Do(req) - if err != nil { - log.Panic(err) - } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Panic(err) - } - - var status structs.Status - - err = json.Unmarshal(body, &status) - if err != nil { - log.Panic(err) - } - - if status.Ok == false { - log.Panic(string(body)) +func (b Bot) getUsername() string { + if b.isCallbackCommand() { + return b.HookData.CallbackQuery.From.Username } - - log.Println("SendPhoto OK") + return b.HookData.Message.From.Username } -func SendMessageWithReplyMarkup(chatId int, message string, replyMark []structs.ButtonCallback) { - uri := API_URL + "/sendMessage" - - var markup structs.BodyReplyMarkup - markup.ReplyMarkup.InlineKeyboard = append(markup.ReplyMarkup.InlineKeyboard, replyMark) - marshalled, err := json.Marshal(markup) - - req, err := http.NewRequest("GET", uri, bytes.NewReader(marshalled)) - req.Header.Add("Content-Type", "application/json") - if err != nil { - log.Println(err) - return +func (b Bot) getRawMessage() string { + if b.isCallbackCommand() { + return b.HookData.CallbackQuery.Data } - if chatType == "group" { - message = message + "\n@" + chatFrom.Username - } - - q := req.URL.Query() - q.Add("chat_id", strconv.Itoa(chatId)) - q.Add("text", message) - q.Add("parse_mode", "html") - - req.URL.RawQuery = q.Encode() - - client := &http.Client{} + return b.HookData.Message.Text +} - res, err := client.Do(req) - if err != nil { - log.Panic(err) - } - defer res.Body.Close() +func (b Bot) getCommand() string { + var arr []string - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Panic(err) + if b.isCallbackCommand() { + data := b.HookData.CallbackQuery.Data + arr = strings.Fields(data) + } else { + text := b.HookData.Message.Text + arr = strings.Fields(text) } - var status structs.Status + return arr[0] +} - err = json.Unmarshal(body, &status) - if err != nil { - log.Panic(err) +func (b Bot) getChatId() int { + if b.isCallbackCommand() { + return b.HookData.CallbackQuery.Message.Chat.ID } + return b.HookData.Message.Chat.ID +} - if status.Ok == false { - log.Panic(string(body)) +func (b Bot) getFirstName() string { + if b.isCallbackCommand() { + return b.HookData.CallbackQuery.Message.From.FirstName } - - log.Println("SendMessageWithReplyMarkup OK") + return b.HookData.Message.From.FirstName } func (b Bot) setTypingAction() { defer close(b.typingCh) - url := b.getApiURL() - chatId := b.HookData.Message.Chat.ID - - req, err := http.NewRequest("GET", url+"/sendChatAction", nil) - if err != nil { - log.Println(err) - return - } - - q := req.URL.Query() - q.Add("chat_id", strconv.Itoa(chatId)) - q.Add("action", "typing") - - req.URL.RawQuery = q.Encode() - - client := &http.Client{} - - res, err := client.Do(req) - - if err != nil { - log.Println(err) - return - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - - if err != nil { - log.Panic(err) - } - - if body != nil { - log.Println("SetTypingAction OK") - } - - // channel.GetWg().Done() + data := b.getTelegramData() + telegram.SetTypingAction(data) } -func GetBotCommands() structs.BotCommands { - uri := API_URL + "/getMyCommands" - req, err := http.NewRequest("GET", uri, nil) - - if err != nil { - log.Panic(err) - } - - client := &http.Client{} +func (b Bot) sendStartMessage(data structs.DataTele) error { + message := fmt.Sprintf("Xin chào %s \n\nGõ /help để xem danh sách các lệnh mà bot hỗ trợ nhé.\n\nBạn cũng có thể truy cập nhanh các chức năng bằng cách nhấn nút Menu bên dưới.", data.FirstName) + data.ReplyMessage = message + return telegram.SendMessage(data) +} - res, err := client.Do(req) +func (b Bot) sendHelpMessage(data structs.DataTele) error { + messages := "" + botCommands := b.getBotCommands() - if err != nil { - log.Panic(err) + for _, command := range botCommands.Result { + messages += fmt.Sprintf("/%s - %s\n\n", command.Command, command.Description) } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) + data.ReplyMessage = messages - if err != nil { - log.Panic(err) - } - - var botCommands structs.BotCommands + return telegram.SendMessage(data) +} - err = json.Unmarshal(body, &botCommands) - if err != nil { - log.Panic(err) - } +func (b Bot) getBotCommands() structs.BotCommands { + return telegram.GetBotCommands() +} - if botCommands.Ok == false { - log.Fatalln(string(body)) +func (b Bot) sendGroupId(data structs.DataTele) error { + if data.ChatType == "group" { + data.ReplyMessage = fmt.Sprintf("Group ID: %v", data.ChatId) + } else { + data.ReplyMessage = "Không tìm thấy nhóm, bạn cần thêm bot vào nhóm trước khi thực hiện lệnh này !" } - log.Println("GetBotCommands OK") - return botCommands + return telegram.SendMessage(data) } -func SetChatFrom(chat structs.From) { - chatFrom = chat -} -func SetChatType(t string) { - chatType = t +func (b Bot) invalidCommand(data structs.DataTele) error { + data.ReplyMessage = "Tôi không hiểu câu lệnh của bạn !!!" + return telegram.SendMessage(data) } diff --git a/utils/channel/channel.go b/utils/channel/channel.go deleted file mode 100644 index 7c10554..0000000 --- a/utils/channel/channel.go +++ /dev/null @@ -1,55 +0,0 @@ -package channel - -import ( - "log" - "sync" - "time" - "zeril-bot/utils/bot" - "zeril-bot/utils/structs" -) - -var sendMessageChan = make(chan structs.SendMessage) -var sendPhotoChan = make(chan structs.SendPhoto) -var sendMessageWithReplyMarkupChan = make(chan structs.SendMessageWithReplyMarkup) -var Wg sync.WaitGroup - -func Create() { - go func() { - for { - select { - case sm := <-sendMessageChan: - bot.SendMessage(sm.ChatId, sm.Message) - Wg.Done() - case sp := <-sendPhotoChan: - bot.SendPhoto(sp.ChatId, sp.Path) - Wg.Done() - case srm := <-sendMessageWithReplyMarkupChan: - bot.SendMessageWithReplyMarkup(srm.ChatId, srm.Message, srm.ReplyMark) - Wg.Done() - case <-time.After(3 * time.Second): - log.Println("Timeout") - Wg.Done() - } - } - }() -} - -func SendMessage(chatId int, message string) { - sendMessageChan <- structs.SendMessage{ChatId: chatId, Message: message} -} - -func SendPhoto(chatId int, path string) { - sendPhotoChan <- structs.SendPhoto{ChatId: chatId, Path: path} -} - -func SendMessageWithReplyMarkup(chatId int, message string, replyMark []structs.ButtonCallback) { - sendMessageWithReplyMarkupChan <- structs.SendMessageWithReplyMarkup{ - ChatId: chatId, - Message: message, - ReplyMark: replyMark, - } -} - -func GetWg() *sync.WaitGroup { - return &Wg -} diff --git a/utils/help/help.go b/utils/help/help.go deleted file mode 100644 index 1cc74e2..0000000 --- a/utils/help/help.go +++ /dev/null @@ -1,32 +0,0 @@ -package help - -import ( - "fmt" - "zeril-bot/utils/bot" - "zeril-bot/utils/channel" -) - -func SendStartMessage(chatId int, name string) { - message := fmt.Sprintf("Xin chào %s \n\nGõ /help để xem danh sách các lệnh mà bot hỗ trợ nhé.\n\nBạn cũng có thể truy cập nhanh các chức năng bằng cách nhấn nút Menu bên dưới.", name) - channel.SendMessage(chatId, message) -} - -func SendHelpMessage(chatId int) { - messages := "" - botCommands := bot.GetBotCommands() - - for _, command := range botCommands.Result { - messages += fmt.Sprintf("/%s - %s\n\n", command.Command, command.Description) - } - - channel.SendMessage(chatId, messages) -} - -func SendGroupId(chatId int, chatType string) { - if chatType == "group" { - channel.SendMessage(chatId, fmt.Sprintf("Group ID: %v", chatId)) - return - } - - channel.SendMessage(chatId, "Không tìm thấy nhóm, bạn cần thêm bot vào nhóm trước khi thực hiện lệnh này !") -} diff --git a/utils/kqxs/kqxs.go b/utils/kqxs/kqxs.go index 393a138..323f176 100644 --- a/utils/kqxs/kqxs.go +++ b/utils/kqxs/kqxs.go @@ -3,20 +3,20 @@ package kqxs import ( "fmt" "strings" - "zeril-bot/utils/channel" "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" "github.com/mmcdole/gofeed" ) -func Send(chatId int, text string) { +func Send(data structs.DataTele) error { + text := data.RawMessage text = strings.TrimSpace(text) arr := strings.Fields(text) args := arr[1:] if len(args) != 1 { - SendSuggest(chatId, args) - return + return SendSuggest(data) } zone := text[6:] @@ -34,14 +34,15 @@ func Send(chatId int, text string) { message = strings.Replace(message, "ĐB:", "\n\nĐB:", -1) } - channel.SendMessage(chatId, feed.Items[0].Title+message) + data.ReplyMessage = feed.Items[0].Title + message + return telegram.SendMessage(data) default: - channel.SendMessage(chatId, "Tôi không hiểu câu lệnh của bạn !!!") + data.ReplyMessage = "Tôi không hiểu câu lệnh của bạn !!!" + return telegram.SendMessage(data) } - } -func SendSuggest(chatId int, args []string) { +func SendSuggest(data structs.DataTele) error { var buttons []structs.ButtonCallback var btn1, btn2, btn3 structs.ButtonCallback @@ -58,8 +59,7 @@ func SendSuggest(chatId int, args []string) { buttons = append(buttons, btn2) buttons = append(buttons, btn3) - if len(args) == 0 { - channel.SendMessageWithReplyMarkup(chatId, "Hãy chọn khu vực muốn xem kết quả xổ số", buttons) - return - } + data.ReplyMessage = "Hãy chọn khu vực muốn xem kết quả xổ số" + return telegram.SendMessageWithReplyMarkup(data, buttons) + } diff --git a/utils/lunar/lunar.go b/utils/lunar/lunar.go index 35ec312..86c22a7 100644 --- a/utils/lunar/lunar.go +++ b/utils/lunar/lunar.go @@ -7,19 +7,23 @@ import ( "net/http" "os" "time" - "zeril-bot/utils/channel" + "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" "github.com/oliamb/cutter" ) -func SendLunarDateNow(chatId int) { +func SendLunarDateNow(data structs.DataTele) error { y, m, d := time.Now().Date() path := "/tmp/lunar.jpg" - downloadAndCropImage(fmt.Sprintf("https://licham365.vn/images/lich-am-ngay-%v-thang-%v-nam-%v.jpg", d, int(m), y), path) + err := downloadAndCropImage(fmt.Sprintf("https://licham365.vn/images/lich-am-ngay-%v-thang-%v-nam-%v.jpg", d, int(m), y), path) + if err != nil { + return err + } - channel.SendPhoto(chatId, path) + return telegram.SendPhoto(data, path) } func downloadAndCropImage(URL, fileName string) error { diff --git a/utils/middleware/index.go b/utils/middleware/index.go index 8bad06e..81bf165 100644 --- a/utils/middleware/index.go +++ b/utils/middleware/index.go @@ -1,66 +1,13 @@ package middleware import ( - "context" "encoding/json" - "log" "net/http" "runtime/debug" - "zeril-bot/utils/bot" - "zeril-bot/utils/channel" - "zeril-bot/utils/structs" "github.com/go-chi/chi/v5/middleware" ) -func PreRequest(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - path := r.URL.Path - - if path == "/trip" { - next.ServeHTTP(w, r) - return - } - - var data structs.HookData - err := json.NewDecoder(r.Body).Decode(&data) - - if err != nil { - log.Println("Request is not from Telegram") - next.ServeHTTP(w, r) - return - } - - channel.Create() - - if data.Message.Text == "" && data.CallbackQuery.Data == "" { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - resp := make(map[string]string) - resp["status"] = "ERROR" - resp["message"] = "No message found" - - jsonResp, _ := json.Marshal(resp) - w.Write(jsonResp) - return - } - - ctx := context.WithValue(r.Context(), "data", data) - - if data.CallbackQuery.Data != "" { - bot.SetChatFrom(data.CallbackQuery.From) - bot.SetChatType(data.CallbackQuery.Message.Chat.Type) - } else { - bot.SetChatFrom(data.Message.From) - bot.SetChatType(data.Message.Chat.Type) - } - - next.ServeHTTP(w, r.WithContext(ctx)) - } - - return http.HandlerFunc(fn) -} - func Recoverer(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { defer func() { diff --git a/utils/qr/qr.go b/utils/qr/qr.go index c6d54a2..ccd2e7f 100644 --- a/utils/qr/qr.go +++ b/utils/qr/qr.go @@ -1,27 +1,28 @@ package qr import ( - "log" "strings" - "zeril-bot/utils/channel" + "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" "github.com/skip2/go-qrcode" ) -func SendQRImage(chatId int, text string) { +func SendQRImage(data structs.DataTele) error { + text := data.RawMessage arr := strings.Fields(text) args := arr[1:] if len(args) == 0 { - channel.SendMessage(chatId, "Sử dụng cú pháp /qr nội dung để tạo mã QR.") - return + data.ReplyMessage = "Sử dụng cú pháp /qr nội dung để tạo mã QR." + return telegram.SendMessage(data) } content := text[4:] path := "/tmp/qr.png" err := qrcode.WriteFile(content, qrcode.Medium, 256, path) if err != nil { - log.Panic(err) + return err } - channel.SendPhoto(chatId, path) + return telegram.SendPhoto(data, path) } diff --git a/utils/quote/quote.go b/utils/quote/quote.go index fb366d2..1edbd78 100644 --- a/utils/quote/quote.go +++ b/utils/quote/quote.go @@ -9,14 +9,14 @@ import ( "zeril-bot/utils/telegram" ) -func SendAQuote(data telegram.Data) error { +func SendAQuote(data structs.DataTele) error { quote, err := getAQuote() if err != nil { return err } quoteFormat := fmt.Sprintf(""%s" - %s", quote.Quote, quote.Author) - data.Message = quoteFormat + data.ReplyMessage = quoteFormat return telegram.SendMessage(data) } diff --git a/utils/random/random.go b/utils/random/random.go index 34081e9..1b06028 100644 --- a/utils/random/random.go +++ b/utils/random/random.go @@ -5,15 +5,18 @@ import ( "math/rand" "strings" "unicode" - "zeril-bot/utils/channel" + "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" ) -func RandomElements(chatId int, text string) { +func RandomElements(data structs.DataTele) error { + text := data.RawMessage arr := strings.Fields(text) if len(arr[1:]) == 0 { - channel.SendMessage(chatId, "Sử dụng cú pháp /random A, B, C để chọn phần tử ngẫu nhiên") - return + data.ReplyMessage = "Sử dụng cú pháp /random A, B, C để chọn phần tử ngẫu nhiên" + return telegram.SendMessage(data) + } f := func(c rune) bool { @@ -21,8 +24,9 @@ func RandomElements(chatId int, text string) { } els := strings.FieldsFunc(text[8:], f) - el := els[rand.Intn(len(els))] - channel.SendMessage(chatId, fmt.Sprintf("Phần từ được chọn sau khi random: %v", strings.TrimSpace(el))) + data.ReplyMessage = fmt.Sprintf("Phần từ được chọn sau khi random: %v", strings.TrimSpace(el)) + + return telegram.SendMessage(data) } diff --git a/utils/shortener/shortener.go b/utils/shortener/shortener.go index ead817b..1a49d96 100644 --- a/utils/shortener/shortener.go +++ b/utils/shortener/shortener.go @@ -7,27 +7,30 @@ import ( "os" "strings" "time" - "zeril-bot/utils/channel" "zeril-bot/utils/redis" + "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" gonanoid "github.com/matoous/go-nanoid" ) const rawAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -func Generate(chatId int, text string) { +func Generate(data structs.DataTele) error { + text := data.RawMessage arr := strings.Fields(text) args := arr[1:] + fmt.Println(args) - if len(args) != 1 { - channel.SendMessage(chatId, "Sử dụng cú pháp /shortener https://example.com/ để tạo rút gọn liên kết") - return + if len(args) == 0 { + data.ReplyMessage = "Sử dụng cú pháp /shortener https://example.com/ để tạo rút gọn liên kết" + return telegram.SendMessage(data) } url := text[11:] if !isUrl(url) { - channel.SendMessage(chatId, "URL không đúng định dạng") - return + data.ReplyMessage = "URL không đúng định dạng" + return telegram.SendMessage(data) } id, err := gonanoid.Generate(rawAlphabet, 6) @@ -38,9 +41,9 @@ func Generate(chatId int, text string) { redis.Set(id, url, time.Hour) shortUrl := fmt.Sprintf("%s/url/%s", os.Getenv("APP_URL"), id) + data.ReplyMessage = fmt.Sprintf("URL sau khi rút gọn: %s", shortUrl, shortUrl) - channel.SendMessage(chatId, fmt.Sprintf("URL sau khi rút gọn: %s", shortUrl, shortUrl)) - + return telegram.SendMessage(data) } func isUrl(str string) bool { diff --git a/utils/structs/structs.go b/utils/structs/structs.go index 5e3f828..e13effc 100644 --- a/utils/structs/structs.go +++ b/utils/structs/structs.go @@ -93,3 +93,13 @@ type ButtonCallback struct { Text string `json:"text"` CallbackData string `json:"callback_data"` } + +type DataTele struct { + ChatId int + ChatType string + FirstName string + Username string + ReplyMessage string + RawMessage string + Command string +} diff --git a/utils/telegram/telegram.go b/utils/telegram/telegram.go index 3cf9bdc..1ffccb6 100644 --- a/utils/telegram/telegram.go +++ b/utils/telegram/telegram.go @@ -17,25 +17,31 @@ import ( var API_URL string = "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") -func getApiURL() string { - return "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") -} - -type Data struct { - ChatId int - ChatType string - Username string - Message string +func getApiURL(t string) string { + url := "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") + + switch t { + case "sendMessage": + return url + "/sendMessage" + case "sendPhoto": + return url + "/sendPhoto" + case "sendChatAction": + return url + "/sendChatAction" + case "getMyCommands": + return url + "/getMyCommands" + default: + return "" + } } -func SendMessage(data Data) error { - message := data.Message +func SendMessage(data structs.DataTele) error { + message := data.ReplyMessage if data.ChatType == "group" { - message = message + "\n@" + data.Username + message += "\n@" + data.Username } - uri := API_URL + "/sendMessage" - req, err := http.NewRequest("GET", uri, nil) + url := getApiURL("sendMessage") + req, err := http.NewRequest("GET", url, nil) if err != nil { return err @@ -79,101 +85,182 @@ func SendMessage(data Data) error { return errors.New(string(body)) } -func SendPhoto(chatId int, path string) { - uri := API_URL + "/sendPhoto" - +func SendPhoto(data structs.DataTele, path string) error { file, _ := os.Open(path) defer file.Close() payload := &bytes.Buffer{} writer := multipart.NewWriter(payload) - writer.WriteField("chat_id", strconv.Itoa(chatId)) + writer.WriteField("chat_id", strconv.Itoa(data.ChatId)) - // if chatType == "group" { - // writer.WriteField("caption", "@"+chatFrom.Username) - // } + if data.ChatType == "group" { + writer.WriteField("caption", "@"+data.Username) + } part, _ := writer.CreateFormFile("photo", filepath.Base(path)) io.Copy(part, file) writer.Close() - req, _ := http.NewRequest("GET", uri, payload) + url := getApiURL("sendPhoto") + req, _ := http.NewRequest("GET", url, payload) req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} res, err := client.Do(req) if err != nil { - log.Panic(err) + return err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { - log.Panic(err) + return err } var status structs.Status err = json.Unmarshal(body, &status) if err != nil { - log.Panic(err) + return err } - if status.Ok == false { - log.Panic(string(body)) + if status.Ok { + log.Println("SendPhoto OK") + return nil } - log.Println("SendPhoto OK") + return errors.New(string(body)) } -func SendMessageWithReplyMarkup(chatId int, message string, replyMark []structs.ButtonCallback) { - uri := API_URL + "/sendMessage" - +func SendMessageWithReplyMarkup(data structs.DataTele, replyMark []structs.ButtonCallback) error { var markup structs.BodyReplyMarkup markup.ReplyMarkup.InlineKeyboard = append(markup.ReplyMarkup.InlineKeyboard, replyMark) marshalled, err := json.Marshal(markup) + if err != nil { + return err + } - req, err := http.NewRequest("GET", uri, bytes.NewReader(marshalled)) + url := getApiURL("sendMessage") + req, err := http.NewRequest("GET", url, bytes.NewReader(marshalled)) req.Header.Add("Content-Type", "application/json") + if err != nil { + return err + } + + message := data.ReplyMessage + if data.ChatType == "group" { + message += "\n@" + data.Username + } + + q := req.URL.Query() + q.Add("chat_id", strconv.Itoa(data.ChatId)) + q.Add("text", message) + q.Add("parse_mode", "html") + + req.URL.RawQuery = q.Encode() + + client := &http.Client{} + + res, err := client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + var status structs.Status + + err = json.Unmarshal(body, &status) + if err != nil { + return err + } + + if status.Ok { + log.Println("SendMessageWithReplyMarkup OK") + return nil + } + + return errors.New(string(body)) +} + +func SetTypingAction(data structs.DataTele) { + chatId := data.ChatId + + url := getApiURL("sendChatAction") + req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) return } - // if chatType == "group" { - // message = message + "\n@" + chatFrom.Username - // } q := req.URL.Query() q.Add("chat_id", strconv.Itoa(chatId)) - q.Add("text", message) - q.Add("parse_mode", "html") + q.Add("action", "typing") req.URL.RawQuery = q.Encode() client := &http.Client{} res, err := client.Do(req) + + if err != nil { + log.Println(err) + return + } + + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + + if err != nil { + log.Panic(err) + } + + if body != nil { + log.Println("SetTypingAction OK") + } +} + +func GetBotCommands() structs.BotCommands { + url := getApiURL("getMyCommands") + req, err := http.NewRequest("GET", url, nil) + + if err != nil { + log.Panic(err) + } + + client := &http.Client{} + + res, err := client.Do(req) + if err != nil { log.Panic(err) } + defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) + if err != nil { log.Panic(err) } - var status structs.Status + var botCommands structs.BotCommands - err = json.Unmarshal(body, &status) + err = json.Unmarshal(body, &botCommands) if err != nil { log.Panic(err) } - if status.Ok == false { - log.Panic(string(body)) + if botCommands.Ok == false { + log.Fatalln(string(body)) } - log.Println("SendMessageWithReplyMarkup OK") + log.Println("GetBotCommands OK") + return botCommands } diff --git a/utils/weather/weather.go b/utils/weather/weather.go index e2f82a6..cda2f5a 100644 --- a/utils/weather/weather.go +++ b/utils/weather/weather.go @@ -9,34 +9,37 @@ import ( "net/http" "os" "strings" - "zeril-bot/utils/channel" "zeril-bot/utils/structs" + "zeril-bot/utils/telegram" ) +const API_URL = "https://api.openweathermap.org" + var APP_ID = os.Getenv("OPEN_WEATHER_MAP_APP_ID") -var API_URL = "https://api.openweathermap.org" -func SendForecastOfWeather(chatId int, text string) { +func SendForecastOfWeather(data structs.DataTele) error { + text := data.RawMessage + fmt.Println(text) text = strings.TrimSpace(text) arr := strings.Fields(text) args := arr[1:] if len(args) == 0 { - SendSuggestForecast(chatId, args) - return + return SendSuggestForecast(data) } cityName := text[9:] - data, err := GetWeather(cityName) + wData, err := GetWeather(cityName) if err != nil { - channel.SendMessage(chatId, "Không tìm thấy thông tin thời tiết") - return + data.ReplyMessage = "Không tìm thấy thông tin thời tiết" + return telegram.SendMessage(data) } - channel.SendMessage(chatId, fmt.Sprintf("🏙 Thời tiết hiện tại ở %s\n\n🌡 Nhiệt độ: %.2f°C\n\n💧 Độ ẩm: %v%\n\nℹ️ Tổng quan: %s", data.Name, data.Main.Temp, data.Main.Humidity, data.Weather[0].Description)) + data.ReplyMessage = fmt.Sprintf("🏙 Thời tiết hiện tại ở %s\n\n🌡 Nhiệt độ: %.2f°C\n\n💧 Độ ẩm: %v%\n\nℹ️ Tổng quan: %s", wData.Name, wData.Main.Temp, wData.Main.Humidity, wData.Weather[0].Description) + return telegram.SendMessage(data) } -func SendSuggestForecast(chatId int, args []string) { +func SendSuggestForecast(data structs.DataTele) error { var buttons []structs.ButtonCallback var btn1, btn2, btn3 structs.ButtonCallback @@ -53,10 +56,8 @@ func SendSuggestForecast(chatId int, args []string) { buttons = append(buttons, btn2) buttons = append(buttons, btn3) - if len(args) == 0 { - channel.SendMessageWithReplyMarkup(chatId, "Sử dụng cú pháp /weather tên thành phố hoặc chọn các gợi ý bên dưới để xem thời tiết", buttons) - return - } + data.ReplyMessage = "Sử dụng cú pháp /weather tên thành phố hoặc chọn các gợi ý bên dưới để xem thời tiết" + return telegram.SendMessageWithReplyMarkup(data, buttons) } func GetWeather(cityName string) (structs.WeatherData, error) {