diff --git a/cmd/go-mtg-vk/main.go b/cmd/go-mtg-vk/main.go index 4af3303..690413e 100644 --- a/cmd/go-mtg-vk/main.go +++ b/cmd/go-mtg-vk/main.go @@ -4,18 +4,32 @@ import ( "log" "math/rand" "os" + "strconv" "time" "github.com/gin-gonic/gin" + "gitlab.com/flygrounder/go-mtg-vk/internal/caching" "gitlab.com/flygrounder/go-mtg-vk/internal/vk" ) func main() { rand.Seed(time.Now().UTC().UnixNano()) - logFile, _ := os.OpenFile("logs/errors.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - log.SetOutput(logFile) r := gin.Default() - r.POST("callback/message", vk.HandleMessage) + + groupId, _ := strconv.ParseInt(os.Getenv("VK_GROUP_ID"), 10, 64) + handler := vk.Handler{ + Sender: &vk.ApiSender{ + Token: os.Getenv("VK_TOKEN"), + }, + Logger: log.New(os.Stdout, "", 0), + SecretKey: os.Getenv("VK_SECRET_KEY"), + GroupId: groupId, + ConfirmationString: os.Getenv("VK_CONFIRMATION_STRING"), + DictPath: "./assets/additional_cards.json", + CachingClient: caching.GetClient(), + } + + r.POST("callback/message", handler.HandleMessage) _ = r.Run(":8000") } diff --git a/internal/vk/handler.go b/internal/vk/handler.go new file mode 100644 index 0000000..0442f73 --- /dev/null +++ b/internal/vk/handler.go @@ -0,0 +1,113 @@ +package vk + +import ( + "errors" + "log" + "net/http" + "os" + "strings" + + "github.com/gin-gonic/gin" + "gitlab.com/flygrounder/go-mtg-vk/internal/caching" + "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" +) + +type Handler struct { + Sender Sender + Logger *log.Logger + SecretKey string + GroupId int64 + ConfirmationString string + DictPath string + CachingClient *caching.CacheClient +} + +type messageRequest struct { + Type string `json:"type"` + GroupId int64 `json:"group_id"` + Object userMessage `json:"object"` + Secret string `json:"secret"` +} + +type userMessage struct { + Body string `json:"text"` + UserId int64 `json:"peer_id"` +} + +func (h *Handler) HandleMessage(c *gin.Context) { + var req messageRequest + _ = c.BindJSON(&req) + if req.Secret != h.SecretKey { + return + } + switch req.Type { + case "confirmation": + h.handleConfirmation(c, &req) + case "message_new": + go h.handleSearch(&req) + c.String(http.StatusOK, "ok") + } +} + +func (h *Handler) handleSearch(req *messageRequest) { + defer func() { + if r := recover(); r != nil { + h.Logger.Printf("[error] Search panicked. Exception info: %s", r) + } + }() + + cardName, err := h.getCardNameByCommand(req.Object.Body) + if err != nil { + h.Sender.Send(req.Object.UserId, "Некорректная команда") + h.Logger.Printf("[info] Not correct command. Message: %s user input: %s", err.Error(), req.Object.Body) + } else if cardName == "" { + h.Sender.Send(req.Object.UserId, "Карта не найдена") + h.Logger.Printf("[info] Could not find card. User input: %s", req.Object.Body) + } else { + message, err := h.getMessage(cardName) + if err != nil { + h.Sender.Send(req.Object.UserId, "Цены временно недоступны, попробуйте позже") + h.Logger.Printf("[error] Could not find SCG prices. Message: %s card name: %s", err.Error(), cardName) + return + } + h.Sender.Send(req.Object.UserId, message) + } +} + +func (h *Handler) handleConfirmation(c *gin.Context, req *messageRequest) { + if (req.Type == "confirmation") && (req.GroupId == h.GroupId) { + c.String(http.StatusOK, h.ConfirmationString) + } +} + +func (h *Handler) getMessage(cardName string) (string, error) { + val, err := h.CachingClient.Get(cardName) + if err != nil { + prices, err := cardsinfo.GetPrices(cardName) + if err != nil { + return "", err + } + message := cardsinfo.FormatCardPrices(cardName, prices) + h.CachingClient.Set(cardName, message) + return message, nil + } + return val, nil +} + +func (h *Handler) getCardNameByCommand(command string) (string, error) { + var name string + switch { + case strings.HasPrefix(command, "!s"): + split := strings.Split(command, " ") + if len(split) < 3 { + return "", errors.New("wrong command") + } + set := split[1] + number := split[2] + name = cardsinfo.GetNameByCardId(set, number) + default: + dict, _ := os.Open(h.DictPath) + name = cardsinfo.GetOriginalName(command, dict) + } + return name, nil +} diff --git a/internal/vk/handlers.go b/internal/vk/handlers.go deleted file mode 100644 index 520cb38..0000000 --- a/internal/vk/handlers.go +++ /dev/null @@ -1,94 +0,0 @@ -package vk - -import ( - "errors" - "log" - "net/http" - "os" - "strings" - - "github.com/gin-gonic/gin" - "gitlab.com/flygrounder/go-mtg-vk/internal/caching" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -var dictPath = "./assets/additional_cards.json" - -func HandleMessage(c *gin.Context) { - var req MessageRequest - _ = c.BindJSON(&req) - if req.Secret != SecretKey { - return - } - switch req.Type { - case "confirmation": - handleConfirmation(c, &req) - case "message_new": - go handleSearch(&req) - c.String(http.StatusOK, "ok") - } -} - -func handleSearch(req *MessageRequest) { - defer func() { - if r := recover(); r != nil { - log.Printf("[error] Search panicked. Exception info: %s", r) - } - }() - - cardName, err := getCardNameByCommand(req.Object.Body) - if err != nil { - Message(req.Object.UserId, "Некорректная команда") - log.Printf("[info] Not correct command. Message: %s user input: %s", err.Error(), req.Object.Body) - } else if cardName == "" { - Message(req.Object.UserId, "Карта не найдена") - log.Printf("[info] Could not find card. User input: %s", req.Object.Body) - } else { - message, err := GetMessage(cardName) - if err != nil { - Message(req.Object.UserId, "Цены временно недоступны, попробуйте позже") - log.Printf("[error] Could not find SCG prices. Message: %s card name: %s", err.Error(), cardName) - return - } - Message(req.Object.UserId, message) - } -} - -func GetMessage(cardName string) (string, error) { - client := caching.GetClient() - val, err := client.Get(cardName) - if err != nil { - prices, err := cardsinfo.GetPrices(cardName) - if err != nil { - return "", err - } - message := cardsinfo.FormatCardPrices(cardName, prices) - client.Set(cardName, message) - return message, nil - } - return val, nil -} - -func getCardNameByCommand(command string) (string, error) { - var name string - switch { - case strings.HasPrefix(command, "!s"): - split := strings.Split(command, " ") - if len(split) < 3 { - return "", errors.New("wrong command") - } - set := split[1] - number := split[2] - name = cardsinfo.GetNameByCardId(set, number) - default: - dict, _ := os.Open(dictPath) - name = cardsinfo.GetOriginalName(command, dict) - } - return name, nil -} - -func handleConfirmation(c *gin.Context, req *MessageRequest) { - if (req.Type == "confirmation") && (req.GroupId == GroupId) { - c.String(http.StatusOK, ConfirmationString) - } -} diff --git a/internal/vk/message.go b/internal/vk/message.go deleted file mode 100644 index d3c21e5..0000000 --- a/internal/vk/message.go +++ /dev/null @@ -1,37 +0,0 @@ -package vk - -import ( - "encoding/json" - "io/ioutil" - "log" - "math/rand" - "net/http" - "net/url" - "strconv" - "strings" -) - -const SendMessageUrl = "https://api.vk.com/method/messages.send" - -func Message(userId int64, message string) { - randomId := rand.Int63() - params := []string{ - "access_token=" + Token, - "peer_id=" + strconv.FormatInt(userId, 10), - "message=" + url.QueryEscape(message), - "v=5.95", - "random_id=" + strconv.FormatInt(randomId, 10), - } - paramString := strings.Join(params, "&") - resp, err := http.Get(SendMessageUrl + "?" + paramString) - if err != nil || resp.StatusCode != http.StatusOK { - log.Printf("[error] Could not send message. User: %d", userId) - return - } - responseBytes, _ := ioutil.ReadAll(resp.Body) - var response SendMessageResponse - _ = json.Unmarshal(responseBytes, &response) - if response.Error.ErrorCode != 0 { - log.Printf("[error] Message was not sent. User: %d error message: %s", userId, response.Error.ErrorMsg) - } -} diff --git a/internal/vk/secrets.go b/internal/vk/secrets.go deleted file mode 100644 index 1c85c2d..0000000 --- a/internal/vk/secrets.go +++ /dev/null @@ -1,11 +0,0 @@ -package vk - -import ( - "os" - "strconv" -) - -var Token = os.Getenv("VK_TOKEN") -var SecretKey = os.Getenv("VK_SECRET_KEY") -var GroupId, _ = strconv.ParseInt(os.Getenv("VK_GROUP_ID"), 10, 64) -var ConfirmationString = os.Getenv("VK_CONFIRMATION_STRING") diff --git a/internal/vk/sender.go b/internal/vk/sender.go new file mode 100644 index 0000000..3535bb7 --- /dev/null +++ b/internal/vk/sender.go @@ -0,0 +1,55 @@ +package vk + +import ( + "encoding/json" + "io/ioutil" + "log" + "math/rand" + "net/http" + "net/url" + "strconv" + "strings" +) + +const SendMessageUrl = "https://api.vk.com/method/messages.send" + +type Sender interface { + Send(userId int64, message string) +} + +type ApiSender struct { + Token string +} + +type sendMessageResponse struct { + Error errorResponse `json:"error"` +} + +type errorResponse struct { + ErrorCode int `json:"error_code"` + ErrorMsg string `json:"error_msg"` +} + +func (s *ApiSender) Send(userId int64, message string) { + randomId := rand.Int63() + params := []string{ + "access_token=" + s.Token, + "peer_id=" + strconv.FormatInt(userId, 10), + "message=" + url.QueryEscape(message), + "v=5.95", + "random_id=" + strconv.FormatInt(randomId, 10), + } + joined := strings.Join(params, "&") + reqUrl := SendMessageUrl + "?" + joined + resp, err := http.Get(reqUrl) + if err != nil || resp.StatusCode != http.StatusOK { + log.Printf("[error] Could not Send message. User: %d", userId) + return + } + respContent, _ := ioutil.ReadAll(resp.Body) + var unmarshalled sendMessageResponse + _ = json.Unmarshal(respContent, &unmarshalled) + if unmarshalled.Error.ErrorCode != 0 { + log.Printf("[error] Message was not sent. User: %d error message: %s", userId, unmarshalled.Error.ErrorMsg) + } +} diff --git a/internal/vk/sender_test.go b/internal/vk/sender_test.go new file mode 100644 index 0000000..75b9900 --- /dev/null +++ b/internal/vk/sender_test.go @@ -0,0 +1,7 @@ +package vk + +import "testing" + +func TestApiSender_Send(t *testing.T) { + +} diff --git a/internal/vk/structs.go b/internal/vk/structs.go deleted file mode 100644 index 9a7c3e6..0000000 --- a/internal/vk/structs.go +++ /dev/null @@ -1,22 +0,0 @@ -package vk - -type MessageRequest struct { - Type string `json:"type"` - GroupId int64 `json:"group_id"` - Object UserMessage `json:"object"` - Secret string `json:"secret"` -} - -type UserMessage struct { - Body string `json:"text"` - UserId int64 `json:"peer_id"` -} - -type SendMessageResponse struct { - Error ErrorResponse `json:"error"` -} - -type ErrorResponse struct { - ErrorCode int `json:"error_code"` - ErrorMsg string `json:"error_msg"` -}