From b4e5dc52c7faa25ffd5c357b0d4424a66df3a8c4 Mon Sep 17 00:00:00 2001 From: totanvix Date: Sun, 25 Jun 2023 03:21:45 +0000 Subject: [PATCH] feat: add devcontainer --- .devcontainer/devcontainer.json | 27 ++ .example.env | 6 +- .github/workflows/production.yml | 58 ++-- .gitignore | 2 +- .vscode/settings.json | 24 +- README.md | 24 +- api/hook/index.go | 204 ++++++------- api/index.go | 56 ++-- api/trip/index.go | 88 +++--- api/url/index.go | 58 ++-- go.mod | 58 ++-- go.sum | 122 ++++---- utils/bitcoin/bitcoin.go | 162 +++++----- utils/bot/bot.go | 508 +++++++++++++++---------------- utils/channel/channel.go | 110 +++---- utils/help/help.go | 64 ++-- utils/kqxs/kqxs.go | 130 ++++---- utils/lunar/lunar.go | 136 ++++----- utils/middleware/index.go | 196 ++++++------ utils/qr/qr.go | 54 ++-- utils/quote/quote.go | 76 ++--- utils/random/random.go | 56 ++-- utils/redis/redis.go | 54 ++-- utils/shortener/shortener.go | 98 +++--- utils/structs/structs.go | 190 ++++++------ utils/weather/weather.go | 218 ++++++------- vercel.json | 40 +-- 27 files changed, 1423 insertions(+), 1396 deletions(-) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..459a692 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go +{ + "name": "Go", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/go:0-1.20", + "features": { + "ghcr.io/devcontainers-contrib/features/vercel-cli:1": {} + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" + + "initializeCommand": "git config --global alias.co checkout && git config --global alias.br branch && git config --global alias.ci commit && git config --global alias.st status" +} diff --git a/.example.env b/.example.env index 4b17f0e..78047b3 100644 --- a/.example.env +++ b/.example.env @@ -1,4 +1,4 @@ -APP_URL="" -TELE_BOT_TOKEN="" -OPEN_WEATHER_MAP_APP_ID="" +APP_URL="" +TELE_BOT_TOKEN="" +OPEN_WEATHER_MAP_APP_ID="" UPSTASH_URL="" \ No newline at end of file diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml index 81b4dd6..33c8853 100644 --- a/.github/workflows/production.yml +++ b/.github/workflows/production.yml @@ -1,30 +1,30 @@ -name: Vercel Production Deployment -env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} -on: - push: - branches: - - main -jobs: - Deploy-Production: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install Vercel CLI - run: npm install --global vercel@latest - - name: Pull Vercel Environment Information - run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} - # - name: Build Project Artifacts - # run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} - # - name: Deploy Project Artifacts to Vercel - # run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} - - name: Deploy to Vercel - run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }} - - name: Send telegram message - uses: appleboy/telegram-action@master - with: - to: ${{ secrets.TELEGRAM_TO }} - token: ${{ secrets.TELEGRAM_TOKEN }} - message: | +name: Vercel Production Deployment +env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} +on: + push: + branches: + - main +jobs: + Deploy-Production: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Vercel CLI + run: npm install --global vercel@latest + - name: Pull Vercel Environment Information + run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} + # - name: Build Project Artifacts + # run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} + # - name: Deploy Project Artifacts to Vercel + # run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} + - name: Deploy to Vercel + run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }} + - name: Send telegram message + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_TO }} + token: ${{ secrets.TELEGRAM_TOKEN }} + message: | Code has been deployed to Vercel successfully 🥰🥰 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 83baf96..90419bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -.vercel +.vercel .env \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b48124..94153cd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,13 @@ -{ - "cSpell.words": [ - "gonanoid", - "kqxs", - "qrcode", - "rediss", - "shorturl", - "upstash", - "Vercel", - "zeril", - "zerill" - ] +{ + "cSpell.words": [ + "gonanoid", + "kqxs", + "qrcode", + "rediss", + "shorturl", + "upstash", + "Vercel", + "zeril", + "zerill" + ] } \ No newline at end of file diff --git a/README.md b/README.md index 4b21f0c..cc4a396 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# Zeril Bot - -[Zeril Bot](https://t.me/zerill_bot) is my practice using Go - -## Lesson Learned -* Go and things in Go -* Vercel Serverless Function -* Telegram Bot API -* Github Action -* RSS -* Redis -* NanoID +# Zeril Bot + +[Zeril Bot](https://t.me/zerill_bot) is my practice using Go + +## Lesson Learned +* Go and things in Go +* Vercel Serverless Function +* Telegram Bot API +* Github Action +* RSS +* Redis +* NanoID diff --git a/api/hook/index.go b/api/hook/index.go index 6641a6a..c76c722 100644 --- a/api/hook/index.go +++ b/api/hook/index.go @@ -1,102 +1,102 @@ -package hook - -import ( - "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/quote" - "zeril-bot/utils/random" - "zeril-bot/utils/shortener" - "zeril-bot/utils/structs" - "zeril-bot/utils/weather" -) - -func Handler(w http.ResponseWriter, r *http.Request) { - data := r.Context().Value("data").(structs.HookData) - - channel.GetWg().Add(2) - - if data.CallbackQuery.Data != "" { - resolveCallback(data) - } else { - resolveCommand(data) - } - - channel.GetWg().Wait() -} - -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() - }() -} +package hook + +import ( + "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/quote" + "zeril-bot/utils/random" + "zeril-bot/utils/shortener" + "zeril-bot/utils/structs" + "zeril-bot/utils/weather" +) + +func Handler(w http.ResponseWriter, r *http.Request) { + data := r.Context().Value("data").(structs.HookData) + + channel.GetWg().Add(2) + + if data.CallbackQuery.Data != "" { + resolveCallback(data) + } else { + resolveCommand(data) + } + + channel.GetWg().Wait() +} + +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 b070ccf..048b7eb 100644 --- a/api/index.go +++ b/api/index.go @@ -1,28 +1,28 @@ -package api - -import ( - "net/http" - "zeril-bot/api/hook" - "zeril-bot/api/trip" - "zeril-bot/api/url" - "zeril-bot/utils/middleware" - - "github.com/go-chi/chi/v5" - chiMiddle "github.com/go-chi/chi/v5/middleware" -) - -func Handler(wri http.ResponseWriter, req *http.Request) { - - r := chi.NewRouter() - r.Use(chiMiddle.Logger) - r.Use(middleware.PreRequest) - r.Use(middleware.Recoverer) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) {}) - r.Post("/api/hook", hook.Handler) - r.Get("/url", url.Handler) - r.Get("/trip", trip.Handler) - r.Post("/trip", trip.Handler) - - r.ServeHTTP(wri, req) -} +package api + +import ( + "net/http" + "zeril-bot/api/hook" + "zeril-bot/api/trip" + "zeril-bot/api/url" + "zeril-bot/utils/middleware" + + "github.com/go-chi/chi/v5" + chiMiddle "github.com/go-chi/chi/v5/middleware" +) + +func Handler(wri http.ResponseWriter, req *http.Request) { + + r := chi.NewRouter() + r.Use(chiMiddle.Logger) + r.Use(middleware.PreRequest) + r.Use(middleware.Recoverer) + + r.Get("/", func(w http.ResponseWriter, r *http.Request) {}) + r.Post("/api/hook", hook.Handler) + r.Get("/url", url.Handler) + r.Get("/trip", trip.Handler) + r.Post("/trip", trip.Handler) + + r.ServeHTTP(wri, req) +} diff --git a/api/trip/index.go b/api/trip/index.go index d8b9f40..4844108 100644 --- a/api/trip/index.go +++ b/api/trip/index.go @@ -1,44 +1,44 @@ -package trip - -import ( - "encoding/json" - "net/http" - "zeril-bot/utils/redis" -) - -type data struct { - Url string `json:"url"` -} - -func Handler(w http.ResponseWriter, r *http.Request) { - method := r.Method - - if method == http.MethodPost { - var d data - // Try to decode the request body into the struct. If there is an error, - // respond to the client with the error message and a 400 status code. - err := json.NewDecoder(r.Body).Decode(&d) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - redis.Set("trip", d.Url, 0) - Response(w, d.Url) - - } else { - url := redis.Get("trip") - Response(w, url.Val()) - } -} - -func Response(w http.ResponseWriter, url string) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - resp := make(map[string]string) - resp["url"] = url - - jsonResp, _ := json.Marshal(resp) - w.Write(jsonResp) - return -} +package trip + +import ( + "encoding/json" + "net/http" + "zeril-bot/utils/redis" +) + +type data struct { + Url string `json:"url"` +} + +func Handler(w http.ResponseWriter, r *http.Request) { + method := r.Method + + if method == http.MethodPost { + var d data + // Try to decode the request body into the struct. If there is an error, + // respond to the client with the error message and a 400 status code. + err := json.NewDecoder(r.Body).Decode(&d) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + redis.Set("trip", d.Url, 0) + Response(w, d.Url) + + } else { + url := redis.Get("trip") + Response(w, url.Val()) + } +} + +func Response(w http.ResponseWriter, url string) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + resp := make(map[string]string) + resp["url"] = url + + jsonResp, _ := json.Marshal(resp) + w.Write(jsonResp) + return +} diff --git a/api/url/index.go b/api/url/index.go index a094fd4..d585ee3 100644 --- a/api/url/index.go +++ b/api/url/index.go @@ -1,29 +1,29 @@ -package url - -import ( - "fmt" - "net/http" - "strings" - "unicode" - "zeril-bot/utils/redis" -) - -func Handler(w http.ResponseWriter, r *http.Request) { - path := r.URL.Path - - f := func(c rune) bool { - return unicode.IsPunct(c) == unicode.IsPunct('/') - } - - els := strings.FieldsFunc(path, f) - if els[0] == "url" { - id := els[1] - url := redis.Get(id).Val() - if url != "" { - http.Redirect(w, r, url, http.StatusSeeOther) - return - } - } - - fmt.Fprintf(w, "

Không có dữ liệu

") -} +package url + +import ( + "fmt" + "net/http" + "strings" + "unicode" + "zeril-bot/utils/redis" +) + +func Handler(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + + f := func(c rune) bool { + return unicode.IsPunct(c) == unicode.IsPunct('/') + } + + els := strings.FieldsFunc(path, f) + if els[0] == "url" { + id := els[1] + url := redis.Get(id).Val() + if url != "" { + http.Redirect(w, r, url, http.StatusSeeOther) + return + } + } + + fmt.Fprintf(w, "

Không có dữ liệu

") +} diff --git a/go.mod b/go.mod index c45c5ba..5faff88 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,29 @@ -module zeril-bot - -go 1.20 - -require ( - github.com/go-chi/chi/v5 v5.0.8 - github.com/leekchan/accounting v1.0.0 - github.com/matoous/go-nanoid v1.5.0 - github.com/mmcdole/gofeed v1.2.1 - github.com/oliamb/cutter v0.2.2 - github.com/redis/go-redis/v9 v9.0.5 - github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e -) - -require ( - github.com/PuerkitoBio/goquery v1.8.0 // indirect - github.com/andybalholm/cascadia v1.3.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cockroachdb/apd v1.1.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/mmcdole/goxpp v1.1.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pkg/errors v0.8.1 // indirect - github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/text v0.7.0 // indirect -) +module zeril-bot + +go 1.20 + +require ( + github.com/go-chi/chi/v5 v5.0.8 + github.com/leekchan/accounting v1.0.0 + github.com/matoous/go-nanoid v1.5.0 + github.com/mmcdole/gofeed v1.2.1 + github.com/oliamb/cutter v0.2.2 + github.com/redis/go-redis/v9 v9.0.5 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e +) + +require ( + github.com/PuerkitoBio/goquery v1.8.0 // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/apd v1.1.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mmcdole/goxpp v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/text v0.7.0 // indirect +) diff --git a/go.sum b/go.sum index 333cd3b..0c8744b 100644 --- a/go.sum +++ b/go.sum @@ -1,61 +1,61 @@ -github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= -github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= -github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= -github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/leekchan/accounting v1.0.0 h1:+Wd7dJ//dFPa28rc1hjyy+qzCbXPMR91Fb6F1VGTQHg= -github.com/leekchan/accounting v1.0.0/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek= -github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= -github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s= -github.com/mmcdole/gofeed v1.2.1/go.mod h1:2wVInNpgmC85q16QTTuwbuKxtKkHLCDDtf0dCmnrNr4= -github.com/mmcdole/goxpp v1.1.0 h1:WwslZNF7KNAXTFuzRtn/OKZxFLJAAyOA9w82mDz2ZGI= -github.com/mmcdole/goxpp v1.1.0/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k= -github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1iryBU= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= -github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= -github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= +github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/leekchan/accounting v1.0.0 h1:+Wd7dJ//dFPa28rc1hjyy+qzCbXPMR91Fb6F1VGTQHg= +github.com/leekchan/accounting v1.0.0/go.mod h1:3timm6YPhY3YDaGxl0q3eaflX0eoSx3FXn7ckHe4tO0= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/matoous/go-nanoid v1.5.0 h1:VRorl6uCngneC4oUQqOYtO3S0H5QKFtKuKycFG3euek= +github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= +github.com/mmcdole/gofeed v1.2.1 h1:tPbFN+mfOLcM1kDF1x2c/N68ChbdBatkppdzf/vDe1s= +github.com/mmcdole/gofeed v1.2.1/go.mod h1:2wVInNpgmC85q16QTTuwbuKxtKkHLCDDtf0dCmnrNr4= +github.com/mmcdole/goxpp v1.1.0 h1:WwslZNF7KNAXTFuzRtn/OKZxFLJAAyOA9w82mDz2ZGI= +github.com/mmcdole/goxpp v1.1.0/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k= +github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1iryBU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/utils/bitcoin/bitcoin.go b/utils/bitcoin/bitcoin.go index 5b36c3d..d5b6db9 100644 --- a/utils/bitcoin/bitcoin.go +++ b/utils/bitcoin/bitcoin.go @@ -1,81 +1,81 @@ -package bitcoin - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "strconv" - "zeril-bot/utils/channel" - "zeril-bot/utils/structs" - - "github.com/leekchan/accounting" -) - -func SendBitcoinPrice(chatId int) { - acUsd := accounting.Accounting{Symbol: "$", Precision: 2} - acVnd := accounting.Accounting{Symbol: "", Precision: 0, Thousand: "."} - - btc := getBitcoinPrice() - p, _ := strconv.ParseFloat(btc.Price, 64) - usd := acUsd.FormatMoney(p) - - v := exchangeUsdToVnd(p) - vnd := acVnd.FormatMoney(v) + " đ" - - message := fmt.Sprintf("1 Bitcoin = %s (%s)", usd, vnd) - - channel.SendMessage(chatId, message) -} - -func getBitcoinPrice() structs.Btc { - res, err := http.Get("https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT") - - if err != nil { - log.Panic(err) - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - - if err != nil { - log.Panic(err) - } - - var data structs.Btc - - err = json.Unmarshal(body, &data) - if err != nil { - log.Panic(err) - } - - return data -} - -func exchangeUsdToVnd(p float64) float64 { - price := fmt.Sprintf("%.2f", p) - res, err := http.Get("https://api.exchangerate.host/convert?from=USD&to=VND&amount=" + price) - - if err != nil { - log.Panic(err) - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - - if err != nil { - log.Panic(err) - } - - var data structs.Exchange - - err = json.Unmarshal(body, &data) - if err != nil { - log.Panic(err) - } - - return data.Result -} +package bitcoin + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strconv" + "zeril-bot/utils/channel" + "zeril-bot/utils/structs" + + "github.com/leekchan/accounting" +) + +func SendBitcoinPrice(chatId int) { + acUsd := accounting.Accounting{Symbol: "$", Precision: 2} + acVnd := accounting.Accounting{Symbol: "", Precision: 0, Thousand: "."} + + btc := getBitcoinPrice() + p, _ := strconv.ParseFloat(btc.Price, 64) + usd := acUsd.FormatMoney(p) + + v := exchangeUsdToVnd(p) + vnd := acVnd.FormatMoney(v) + " đ" + + message := fmt.Sprintf("1 Bitcoin = %s (%s)", usd, vnd) + + channel.SendMessage(chatId, message) +} + +func getBitcoinPrice() structs.Btc { + res, err := http.Get("https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT") + + if err != nil { + log.Panic(err) + } + + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + + if err != nil { + log.Panic(err) + } + + var data structs.Btc + + err = json.Unmarshal(body, &data) + if err != nil { + log.Panic(err) + } + + return data +} + +func exchangeUsdToVnd(p float64) float64 { + price := fmt.Sprintf("%.2f", p) + res, err := http.Get("https://api.exchangerate.host/convert?from=USD&to=VND&amount=" + price) + + if err != nil { + log.Panic(err) + } + + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + + if err != nil { + log.Panic(err) + } + + var data structs.Exchange + + err = json.Unmarshal(body, &data) + if err != nil { + log.Panic(err) + } + + return data.Result +} diff --git a/utils/bot/bot.go b/utils/bot/bot.go index 845eb58..eb0c48f 100644 --- a/utils/bot/bot.go +++ b/utils/bot/bot.go @@ -1,254 +1,254 @@ -package bot - -import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" - "log" - "mime/multipart" - "net/http" - "os" - "path/filepath" - "strconv" - "zeril-bot/utils/structs" -) - -var API_URL string = "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") - -var chatType string -var chatFrom structs.From - -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) - } - - 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") -} - -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)) - } - - log.Println("SendPhoto OK") -} - -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 - } - 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{} - - 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)) - } - - log.Println("SendMessageWithReplyMarkup OK") -} - -func SetTypingAction(chatId int) { - uri := API_URL + "/sendChatAction" - 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("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() -} - -func GetBotCommands() structs.BotCommands { - uri := API_URL + "/getMyCommands" - req, err := http.NewRequest("GET", uri, 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 botCommands structs.BotCommands - - err = json.Unmarshal(body, &botCommands) - if err != nil { - log.Panic(err) - } - - if botCommands.Ok == false { - log.Fatalln(string(body)) - } - - log.Println("GetBotCommands OK") - return botCommands -} - -func SetChatFrom(chat structs.From) { - chatFrom = chat -} -func SetChatType(t string) { - chatType = t -} +package bot + +import ( + "bytes" + "encoding/json" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "os" + "path/filepath" + "strconv" + "zeril-bot/utils/structs" +) + +var API_URL string = "https://api.telegram.org/bot" + os.Getenv("TELE_BOT_TOKEN") + +var chatType string +var chatFrom structs.From + +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) + } + + 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") +} + +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)) + } + + log.Println("SendPhoto OK") +} + +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 + } + 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{} + + 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)) + } + + log.Println("SendMessageWithReplyMarkup OK") +} + +func SetTypingAction(chatId int) { + uri := API_URL + "/sendChatAction" + 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("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() +} + +func GetBotCommands() structs.BotCommands { + uri := API_URL + "/getMyCommands" + req, err := http.NewRequest("GET", uri, 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 botCommands structs.BotCommands + + err = json.Unmarshal(body, &botCommands) + if err != nil { + log.Panic(err) + } + + if botCommands.Ok == false { + log.Fatalln(string(body)) + } + + log.Println("GetBotCommands OK") + return botCommands +} + +func SetChatFrom(chat structs.From) { + chatFrom = chat +} +func SetChatType(t string) { + chatType = t +} diff --git a/utils/channel/channel.go b/utils/channel/channel.go index 88e77dd..7c10554 100644 --- a/utils/channel/channel.go +++ b/utils/channel/channel.go @@ -1,55 +1,55 @@ -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 -} +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 index 0d4e5c3..1cc74e2 100644 --- a/utils/help/help.go +++ b/utils/help/help.go @@ -1,32 +1,32 @@ -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 !") -} +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 caee34e..393a138 100644 --- a/utils/kqxs/kqxs.go +++ b/utils/kqxs/kqxs.go @@ -1,65 +1,65 @@ -package kqxs - -import ( - "fmt" - "strings" - "zeril-bot/utils/channel" - "zeril-bot/utils/structs" - - "github.com/mmcdole/gofeed" -) - -func Send(chatId int, text string) { - text = strings.TrimSpace(text) - arr := strings.Fields(text) - args := arr[1:] - - if len(args) != 1 { - SendSuggest(chatId, args) - return - } - - zone := text[6:] - - switch zone { - case "mien-nam-xsmn", "mien-bac-xsmb", "mien-trung-xsmt": - fp := gofeed.NewParser() - feed, _ := fp.ParseURL(fmt.Sprintf("https://xosothienphu.com/ket-qua-xo-so-%s.rss", zone)) - fmt.Println(feed.Items[0].Description) - - message := strings.Replace(feed.Items[0].Description, "Giải", "\nGiải", -1) - message = strings.Replace(message, "[", "\n\n[", -1) - - if zone == "mien-bac-xsmb" { - message = strings.Replace(message, "ĐB:", "\n\nĐB:", -1) - } - - channel.SendMessage(chatId, feed.Items[0].Title+message) - default: - channel.SendMessage(chatId, "Tôi không hiểu câu lệnh của bạn !!!") - } - -} - -func SendSuggest(chatId int, args []string) { - var buttons []structs.ButtonCallback - var btn1, btn2, btn3 structs.ButtonCallback - - btn1.Text = "Miền Nam" - btn1.CallbackData = "/kqxs mien-nam-xsmn" - - btn2.Text = "Miền Bắc" - btn2.CallbackData = "/kqxs mien-bac-xsmb" - - btn3.Text = "Miền Trung" - btn3.CallbackData = "/kqxs mien-trung-xsmt" - - buttons = append(buttons, btn1) - 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 - } -} +package kqxs + +import ( + "fmt" + "strings" + "zeril-bot/utils/channel" + "zeril-bot/utils/structs" + + "github.com/mmcdole/gofeed" +) + +func Send(chatId int, text string) { + text = strings.TrimSpace(text) + arr := strings.Fields(text) + args := arr[1:] + + if len(args) != 1 { + SendSuggest(chatId, args) + return + } + + zone := text[6:] + + switch zone { + case "mien-nam-xsmn", "mien-bac-xsmb", "mien-trung-xsmt": + fp := gofeed.NewParser() + feed, _ := fp.ParseURL(fmt.Sprintf("https://xosothienphu.com/ket-qua-xo-so-%s.rss", zone)) + fmt.Println(feed.Items[0].Description) + + message := strings.Replace(feed.Items[0].Description, "Giải", "\nGiải", -1) + message = strings.Replace(message, "[", "\n\n[", -1) + + if zone == "mien-bac-xsmb" { + message = strings.Replace(message, "ĐB:", "\n\nĐB:", -1) + } + + channel.SendMessage(chatId, feed.Items[0].Title+message) + default: + channel.SendMessage(chatId, "Tôi không hiểu câu lệnh của bạn !!!") + } + +} + +func SendSuggest(chatId int, args []string) { + var buttons []structs.ButtonCallback + var btn1, btn2, btn3 structs.ButtonCallback + + btn1.Text = "Miền Nam" + btn1.CallbackData = "/kqxs mien-nam-xsmn" + + btn2.Text = "Miền Bắc" + btn2.CallbackData = "/kqxs mien-bac-xsmb" + + btn3.Text = "Miền Trung" + btn3.CallbackData = "/kqxs mien-trung-xsmt" + + buttons = append(buttons, btn1) + 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 + } +} diff --git a/utils/lunar/lunar.go b/utils/lunar/lunar.go index a86866a..35ec312 100644 --- a/utils/lunar/lunar.go +++ b/utils/lunar/lunar.go @@ -1,68 +1,68 @@ -package lunar - -import ( - "errors" - "fmt" - "image/jpeg" - "net/http" - "os" - "time" - "zeril-bot/utils/channel" - - "github.com/oliamb/cutter" -) - -func SendLunarDateNow(chatId int) { - 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) - - channel.SendPhoto(chatId, path) -} - -func downloadAndCropImage(URL, fileName string) error { - //Get the response bytes from the url - response, err := http.Get(URL) - if err != nil { - return err - } - defer response.Body.Close() - - if response.StatusCode != 200 { - return errors.New("Received non 200 response code") - } - - //Create a empty file - file, err := os.Create(fileName) - if err != nil { - return err - } - defer file.Close() - - img, err := jpeg.Decode(response.Body) - if err != nil { - return err - } - - cImg, err := cutter.Crop(img, cutter.Config{ - Height: 560, // height in pixel or Y ratio(see Ratio Option below) - Width: 1200, // width in pixel or X ratio - // Mode: cutter.Centered, // Accepted Mode: TopLeft, Centered - // Anchor: image.Point{10, 10}, // Position of the top left point - // Options: 0, // Accepted Option: Ratio - }) - - if err != nil { - return err - } - - if err := jpeg.Encode(file, cImg, &jpeg.Options{ - Quality: 100, - }); err != nil { - return err - } - - return nil -} +package lunar + +import ( + "errors" + "fmt" + "image/jpeg" + "net/http" + "os" + "time" + "zeril-bot/utils/channel" + + "github.com/oliamb/cutter" +) + +func SendLunarDateNow(chatId int) { + 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) + + channel.SendPhoto(chatId, path) +} + +func downloadAndCropImage(URL, fileName string) error { + //Get the response bytes from the url + response, err := http.Get(URL) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != 200 { + return errors.New("Received non 200 response code") + } + + //Create a empty file + file, err := os.Create(fileName) + if err != nil { + return err + } + defer file.Close() + + img, err := jpeg.Decode(response.Body) + if err != nil { + return err + } + + cImg, err := cutter.Crop(img, cutter.Config{ + Height: 560, // height in pixel or Y ratio(see Ratio Option below) + Width: 1200, // width in pixel or X ratio + // Mode: cutter.Centered, // Accepted Mode: TopLeft, Centered + // Anchor: image.Point{10, 10}, // Position of the top left point + // Options: 0, // Accepted Option: Ratio + }) + + if err != nil { + return err + } + + if err := jpeg.Encode(file, cImg, &jpeg.Options{ + Quality: 100, + }); err != nil { + return err + } + + return nil +} diff --git a/utils/middleware/index.go b/utils/middleware/index.go index 80c119d..f03bdf8 100644 --- a/utils/middleware/index.go +++ b/utils/middleware/index.go @@ -1,98 +1,98 @@ -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() { - path := r.URL.Path - if path == "/trip" { - return - } - - resp := make(map[string]string) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - // fmt.Println("After R") - if rvr := recover(); rvr != nil { - resp["status"] = "ERROR" - resp["message"] = rvr.(string) - - logEntry := middleware.GetLogEntry(r) - if logEntry != nil { - logEntry.Panic(rvr, debug.Stack()) - } else { - middleware.PrintPrettyStack(rvr) - } - - } else { - resp["status"] = "OK" - } - jsonResp, _ := json.Marshal(resp) - w.Write(jsonResp) - }() - - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} +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() { + path := r.URL.Path + if path == "/trip" { + return + } + + resp := make(map[string]string) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + // fmt.Println("After R") + if rvr := recover(); rvr != nil { + resp["status"] = "ERROR" + resp["message"] = rvr.(string) + + logEntry := middleware.GetLogEntry(r) + if logEntry != nil { + logEntry.Panic(rvr, debug.Stack()) + } else { + middleware.PrintPrettyStack(rvr) + } + + } else { + resp["status"] = "OK" + } + jsonResp, _ := json.Marshal(resp) + w.Write(jsonResp) + }() + + next.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} diff --git a/utils/qr/qr.go b/utils/qr/qr.go index 5ad04fd..c6d54a2 100644 --- a/utils/qr/qr.go +++ b/utils/qr/qr.go @@ -1,27 +1,27 @@ -package qr - -import ( - "log" - "strings" - "zeril-bot/utils/channel" - - "github.com/skip2/go-qrcode" -) - -func SendQRImage(chatId int, text string) { - 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 - } - - content := text[4:] - path := "/tmp/qr.png" - err := qrcode.WriteFile(content, qrcode.Medium, 256, path) - if err != nil { - log.Panic(err) - } - - channel.SendPhoto(chatId, path) -} +package qr + +import ( + "log" + "strings" + "zeril-bot/utils/channel" + + "github.com/skip2/go-qrcode" +) + +func SendQRImage(chatId int, text string) { + 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 + } + + content := text[4:] + path := "/tmp/qr.png" + err := qrcode.WriteFile(content, qrcode.Medium, 256, path) + if err != nil { + log.Panic(err) + } + + channel.SendPhoto(chatId, path) +} diff --git a/utils/quote/quote.go b/utils/quote/quote.go index 63d8c02..1d48f4d 100644 --- a/utils/quote/quote.go +++ b/utils/quote/quote.go @@ -1,38 +1,38 @@ -package quote - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "zeril-bot/utils/channel" - "zeril-bot/utils/structs" -) - -func SendAQuote(chatId int) { - quote := getAQuote() - quoteFormat := fmt.Sprintf(""%s" - %s", quote.Quote, quote.Author) - channel.SendMessage(chatId, quoteFormat) -} - -func getAQuote() structs.QuoteData { - res, err := http.Get("https://zenquotes.io/api/random") - - if err != nil { - log.Panic(err) - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - - var data []structs.QuoteData - err = json.Unmarshal(body, &data) - - if err != nil { - log.Panic(err) - } - - return data[0] -} +package quote + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "zeril-bot/utils/channel" + "zeril-bot/utils/structs" +) + +func SendAQuote(chatId int) { + quote := getAQuote() + quoteFormat := fmt.Sprintf(""%s" - %s", quote.Quote, quote.Author) + channel.SendMessage(chatId, quoteFormat) +} + +func getAQuote() structs.QuoteData { + res, err := http.Get("https://zenquotes.io/api/random") + + if err != nil { + log.Panic(err) + } + + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + + var data []structs.QuoteData + err = json.Unmarshal(body, &data) + + if err != nil { + log.Panic(err) + } + + return data[0] +} diff --git a/utils/random/random.go b/utils/random/random.go index b07b4a4..34081e9 100644 --- a/utils/random/random.go +++ b/utils/random/random.go @@ -1,28 +1,28 @@ -package random - -import ( - "fmt" - "math/rand" - "strings" - "unicode" - "zeril-bot/utils/channel" -) - -func RandomElements(chatId int, text string) { - 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 - } - - f := func(c rune) bool { - return unicode.IsPunct(c) == unicode.IsPunct(',') - } - - 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))) -} +package random + +import ( + "fmt" + "math/rand" + "strings" + "unicode" + "zeril-bot/utils/channel" +) + +func RandomElements(chatId int, text string) { + 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 + } + + f := func(c rune) bool { + return unicode.IsPunct(c) == unicode.IsPunct(',') + } + + 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))) +} diff --git a/utils/redis/redis.go b/utils/redis/redis.go index 9c311be..7c08588 100644 --- a/utils/redis/redis.go +++ b/utils/redis/redis.go @@ -1,27 +1,27 @@ -package redis - -import ( - "context" - "os" - "time" - - "github.com/redis/go-redis/v9" -) - -var ctx = context.Background() - -func client() *redis.Client { - upstashUrl := os.Getenv("UPSTASH_URL") - opt, _ := redis.ParseURL(upstashUrl) - client := redis.NewClient(opt) - - return client -} - -func Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd { - return client().Set(ctx, key, value, expiration) -} - -func Get(key string) *redis.StringCmd { - return client().Get(ctx, key) -} +package redis + +import ( + "context" + "os" + "time" + + "github.com/redis/go-redis/v9" +) + +var ctx = context.Background() + +func client() *redis.Client { + upstashUrl := os.Getenv("UPSTASH_URL") + opt, _ := redis.ParseURL(upstashUrl) + client := redis.NewClient(opt) + + return client +} + +func Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd { + return client().Set(ctx, key, value, expiration) +} + +func Get(key string) *redis.StringCmd { + return client().Get(ctx, key) +} diff --git a/utils/shortener/shortener.go b/utils/shortener/shortener.go index 2cac415..ead817b 100644 --- a/utils/shortener/shortener.go +++ b/utils/shortener/shortener.go @@ -1,49 +1,49 @@ -package shortener - -import ( - "fmt" - "log" - "net/url" - "os" - "strings" - "time" - "zeril-bot/utils/channel" - "zeril-bot/utils/redis" - - gonanoid "github.com/matoous/go-nanoid" -) - -const rawAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - -func Generate(chatId int, text string) { - arr := strings.Fields(text) - args := arr[1:] - - 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 - } - - url := text[11:] - if !isUrl(url) { - channel.SendMessage(chatId, "URL không đúng định dạng") - return - } - - id, err := gonanoid.Generate(rawAlphabet, 6) - if err != nil { - log.Panic(err) - } - - redis.Set(id, url, time.Hour) - - shortUrl := fmt.Sprintf("%s/url/%s", os.Getenv("APP_URL"), id) - - channel.SendMessage(chatId, fmt.Sprintf("URL sau khi rút gọn: %s", shortUrl, shortUrl)) - -} - -func isUrl(str string) bool { - u, err := url.Parse(str) - return err == nil && u.Scheme != "" && u.Host != "" -} +package shortener + +import ( + "fmt" + "log" + "net/url" + "os" + "strings" + "time" + "zeril-bot/utils/channel" + "zeril-bot/utils/redis" + + gonanoid "github.com/matoous/go-nanoid" +) + +const rawAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +func Generate(chatId int, text string) { + arr := strings.Fields(text) + args := arr[1:] + + 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 + } + + url := text[11:] + if !isUrl(url) { + channel.SendMessage(chatId, "URL không đúng định dạng") + return + } + + id, err := gonanoid.Generate(rawAlphabet, 6) + if err != nil { + log.Panic(err) + } + + redis.Set(id, url, time.Hour) + + shortUrl := fmt.Sprintf("%s/url/%s", os.Getenv("APP_URL"), id) + + channel.SendMessage(chatId, fmt.Sprintf("URL sau khi rút gọn: %s", shortUrl, shortUrl)) + +} + +func isUrl(str string) bool { + u, err := url.Parse(str) + return err == nil && u.Scheme != "" && u.Host != "" +} diff --git a/utils/structs/structs.go b/utils/structs/structs.go index 1e867c2..5e3f828 100644 --- a/utils/structs/structs.go +++ b/utils/structs/structs.go @@ -1,95 +1,95 @@ -package structs - -type Chat struct { - ID int `json:"id"` - Type string `json:"type"` - FirstName string `json:"first_name,omitempty"` - Username string `json:"username,omitempty"` - Title string `json:"title,omitempty"` -} - -type From struct { - ID int `json:"id"` - FirstName string `json:"first_name"` - Username string `json:"username"` -} - -type Message struct { - Text string `json:"text"` - Chat Chat `json:"chat"` - From From `json:"from"` -} - -type HookData struct { - UpdateId int `json:"update_id"` - Message Message `json:"message"` - CallbackQuery struct { - From From `json:"from"` - Message Message `json:"message"` - Data string `json:"data"` - } `json:"callback_query"` -} - -type Btc struct { - Symbol string `json:"symbol"` - Price string `json:"price"` -} - -type Exchange struct { - Result float64 `json:"result"` -} - -type QuoteData struct { - Quote string `json:"q"` - Author string `json:"a"` -} - -type Status struct { - Ok bool `json:"ok"` -} - -type WeatherData struct { - Name string `json:"name"` - Weather []struct { - Description string `json:"description"` - } `json:"weather"` - Main struct { - Temp float32 `json:"temp"` - Humidity int `json:"humidity"` - } `json:"main"` -} - -type BotCommands struct { - Status - Result []struct { - Command string `json:"command"` - Description string `json:"description"` - } -} - -type SendMessage struct { - ChatId int - Message string -} - -type SendPhoto struct { - ChatId int - Path string -} - -type SendMessageWithReplyMarkup struct { - ChatId int - Message string - ReplyMark []ButtonCallback -} - -type BodyReplyMarkup struct { - ReplyMarkup struct { - InlineKeyboard [][]ButtonCallback `json:"inline_keyboard"` - } `json:"reply_markup"` -} - -type ButtonCallback struct { - Text string `json:"text"` - CallbackData string `json:"callback_data"` -} +package structs + +type Chat struct { + ID int `json:"id"` + Type string `json:"type"` + FirstName string `json:"first_name,omitempty"` + Username string `json:"username,omitempty"` + Title string `json:"title,omitempty"` +} + +type From struct { + ID int `json:"id"` + FirstName string `json:"first_name"` + Username string `json:"username"` +} + +type Message struct { + Text string `json:"text"` + Chat Chat `json:"chat"` + From From `json:"from"` +} + +type HookData struct { + UpdateId int `json:"update_id"` + Message Message `json:"message"` + CallbackQuery struct { + From From `json:"from"` + Message Message `json:"message"` + Data string `json:"data"` + } `json:"callback_query"` +} + +type Btc struct { + Symbol string `json:"symbol"` + Price string `json:"price"` +} + +type Exchange struct { + Result float64 `json:"result"` +} + +type QuoteData struct { + Quote string `json:"q"` + Author string `json:"a"` +} + +type Status struct { + Ok bool `json:"ok"` +} + +type WeatherData struct { + Name string `json:"name"` + Weather []struct { + Description string `json:"description"` + } `json:"weather"` + Main struct { + Temp float32 `json:"temp"` + Humidity int `json:"humidity"` + } `json:"main"` +} + +type BotCommands struct { + Status + Result []struct { + Command string `json:"command"` + Description string `json:"description"` + } +} + +type SendMessage struct { + ChatId int + Message string +} + +type SendPhoto struct { + ChatId int + Path string +} + +type SendMessageWithReplyMarkup struct { + ChatId int + Message string + ReplyMark []ButtonCallback +} + +type BodyReplyMarkup struct { + ReplyMarkup struct { + InlineKeyboard [][]ButtonCallback `json:"inline_keyboard"` + } `json:"reply_markup"` +} + +type ButtonCallback struct { + Text string `json:"text"` + CallbackData string `json:"callback_data"` +} diff --git a/utils/weather/weather.go b/utils/weather/weather.go index 34850c4..e2f82a6 100644 --- a/utils/weather/weather.go +++ b/utils/weather/weather.go @@ -1,109 +1,109 @@ -package weather - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "strings" - "zeril-bot/utils/channel" - "zeril-bot/utils/structs" -) - -var APP_ID = os.Getenv("OPEN_WEATHER_MAP_APP_ID") -var API_URL = "https://api.openweathermap.org" - -func SendForecastOfWeather(chatId int, text string) { - text = strings.TrimSpace(text) - arr := strings.Fields(text) - args := arr[1:] - - if len(args) == 0 { - SendSuggestForecast(chatId, args) - return - } - - cityName := text[9:] - data, err := GetWeather(cityName) - if err != nil { - channel.SendMessage(chatId, "Không tìm thấy thông tin thời tiết") - return - } - - 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)) -} - -func SendSuggestForecast(chatId int, args []string) { - var buttons []structs.ButtonCallback - var btn1, btn2, btn3 structs.ButtonCallback - - btn1.Text = "Hồ Chí Minh" - btn1.CallbackData = "/weather ho chi minh" - - btn2.Text = "Hà Nội" - btn2.CallbackData = "/weather ha noi" - - btn3.Text = "Nha Trang" - btn3.CallbackData = "/weather nha trang" - - buttons = append(buttons, btn1) - 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 - } -} - -func GetWeather(cityName string) (structs.WeatherData, error) { - uri := API_URL + "/data/2.5/weather" - req, err := http.NewRequest("GET", uri, nil) - - if err != nil { - log.Panic(err) - } - - q := req.URL.Query() - q.Add("appid", APP_ID) - q.Add("q", cityName) - q.Add("lang", "vi") - q.Add("units", "metric") - - req.URL.RawQuery = q.Encode() - - 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 data structs.WeatherData - - if res.StatusCode != 200 { - log.Println(string(body)) - return data, errors.New("Không tìm thấy thông tin thời tiết") - } - - err = json.Unmarshal(body, &data) - if err != nil { - log.Panic(err) - } - - log.Println("GetWeather OK") - - return data, nil -} +package weather + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "strings" + "zeril-bot/utils/channel" + "zeril-bot/utils/structs" +) + +var APP_ID = os.Getenv("OPEN_WEATHER_MAP_APP_ID") +var API_URL = "https://api.openweathermap.org" + +func SendForecastOfWeather(chatId int, text string) { + text = strings.TrimSpace(text) + arr := strings.Fields(text) + args := arr[1:] + + if len(args) == 0 { + SendSuggestForecast(chatId, args) + return + } + + cityName := text[9:] + data, err := GetWeather(cityName) + if err != nil { + channel.SendMessage(chatId, "Không tìm thấy thông tin thời tiết") + return + } + + 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)) +} + +func SendSuggestForecast(chatId int, args []string) { + var buttons []structs.ButtonCallback + var btn1, btn2, btn3 structs.ButtonCallback + + btn1.Text = "Hồ Chí Minh" + btn1.CallbackData = "/weather ho chi minh" + + btn2.Text = "Hà Nội" + btn2.CallbackData = "/weather ha noi" + + btn3.Text = "Nha Trang" + btn3.CallbackData = "/weather nha trang" + + buttons = append(buttons, btn1) + 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 + } +} + +func GetWeather(cityName string) (structs.WeatherData, error) { + uri := API_URL + "/data/2.5/weather" + req, err := http.NewRequest("GET", uri, nil) + + if err != nil { + log.Panic(err) + } + + q := req.URL.Query() + q.Add("appid", APP_ID) + q.Add("q", cityName) + q.Add("lang", "vi") + q.Add("units", "metric") + + req.URL.RawQuery = q.Encode() + + 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 data structs.WeatherData + + if res.StatusCode != 200 { + log.Println(string(body)) + return data, errors.New("Không tìm thấy thông tin thời tiết") + } + + err = json.Unmarshal(body, &data) + if err != nil { + log.Panic(err) + } + + log.Println("GetWeather OK") + + return data, nil +} diff --git a/vercel.json b/vercel.json index c2934a6..93282c8 100644 --- a/vercel.json +++ b/vercel.json @@ -1,20 +1,20 @@ -{ - "routes": [ - { - "src": "/url/(.*)", - "dest": "/api/url/index.go" - }, - { - "src": "/trip/(.*)", - "dest": "/api/trip/index.go" - }, - { - "src": "/(.*)", - "dest": "/api/index.go" - } - ], - "github": { - "enabled": false, - "silent": true - } -} +{ + "routes": [ + { + "src": "/url/(.*)", + "dest": "/api/url/index.go" + }, + { + "src": "/trip/(.*)", + "dest": "/api/trip/index.go" + }, + { + "src": "/(.*)", + "dest": "/api/index.go" + } + ], + "github": { + "enabled": false, + "silent": true + } +}