1
0
mirror of synced 2024-11-21 20:46:05 +03:00
mg-transport-api-client-go/examples/telegram/server.go
2023-12-29 11:19:45 +03:00

132 lines
3.6 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"github.com/gin-gonic/gin"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
v1 "github.com/retailcrm/mg-transport-api-client-go/v1"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
)
const ChatIDPrefix = "tg_"
func Listen() {
router := gin.Default()
router.POST("/api/v1/mg", MGWebhookHandler)
router.POST("/api/v1/tg", TGWebhookHandler)
srv := &http.Server{
Addr: AppConfig.Listen,
Handler: router,
}
go func() {
log.Printf("listening on %s", AppConfig.Listen)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen: %s\n", err)
}
}()
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("shutting down, please wait...")
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("shutdown error:", err)
}
select {
case <-ctx.Done():
log.Println("quitting...")
}
}
func MGWebhookHandler(c *gin.Context) {
var wh v1.WebhookRequest
if err := c.ShouldBindJSON(&wh); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest,
v1.NewTransportErrorResponse(v1.MessageErrorGeneral, "invalid webhook data"))
return
}
if wh.Type != v1.MessageSendWebhookType {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity,
v1.NewTransportErrorResponse(v1.MessageErrorGeneral, "unsupported webhook type"))
return
}
whMsg := wh.MessageWebhookData()
if !strings.HasPrefix(whMsg.ExternalChatID, ChatIDPrefix) {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity,
v1.NewTransportErrorResponse(v1.MessageErrorGeneral, "unexpected chat ID"))
return
}
chatID, err := strconv.ParseInt(whMsg.ExternalChatID[len(ChatIDPrefix):], 10, 64)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity,
v1.NewTransportErrorResponse(v1.MessageErrorGeneral, "unparsable chat ID"))
return
}
resp, err := TG.Send(tgbotapi.NewMessage(chatID, whMsg.Content))
if err != nil {
c.AbortWithStatusJSON(http.StatusUnprocessableEntity,
v1.NewTransportErrorResponse(v1.MessageErrorGeneral, err.Error()))
return
}
c.JSON(http.StatusOK, v1.NewSentMessageResponse(strconv.Itoa(resp.MessageID)))
}
func TGWebhookHandler(c *gin.Context) {
var update tgbotapi.Update
if err := c.ShouldBindJSON(&update); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
if update.Message == nil {
c.AbortWithStatus(http.StatusOK)
return
}
_, _, err := MG.Messages(v1.SendData{
Message: v1.Message{
ExternalID: newExternalMessageID(update.Message.MessageID),
Type: v1.MsgTypeText,
Text: update.Message.Text,
},
Originator: v1.OriginatorCustomer,
Customer: v1.Customer{
ExternalID: strconv.FormatInt(update.Message.From.ID, 10),
Nickname: update.Message.From.UserName,
Firstname: update.Message.From.FirstName,
Lastname: update.Message.From.LastName,
ProfileURL: fmt.Sprintf("https://t.me/%s", update.Message.From.UserName),
Language: update.Message.From.LanguageCode,
},
Channel: Channel.ID,
ExternalChatID: ChatIDPrefix + strconv.FormatInt(update.Message.Chat.ID, 10),
})
if err != nil {
log.Printf("error: cannot send message: %s", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "cannot send message"})
return
}
c.JSON(http.StatusOK, gin.H{})
}
func newExternalMessageID(messageID int) string {
return fmt.Sprintf("%d-%d", TG.Self.ID, messageID)
}