Feat(Initial): Initial Go codebase
All checks were successful
Webhook-Everything/Webhook-Everything/pipeline/head This commit looks good
All checks were successful
Webhook-Everything/Webhook-Everything/pipeline/head This commit looks good
This commit is contained in:
81
backend/internal/common/database.go
Normal file
81
backend/internal/common/database.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
// Opening a database and save the reference to `Database` struct.
|
||||
func InitDB() *gorm.DB {
|
||||
host := os.Getenv("DB_HOST")
|
||||
user := os.Getenv("DB_USER")
|
||||
pass := os.Getenv("DB_PASS")
|
||||
dbName := os.Getenv("DB_NAME")
|
||||
port := os.Getenv("DB_PORT")
|
||||
|
||||
var sslMode string
|
||||
if os.Getenv("DB_SSL") == "TRUE" {
|
||||
sslMode = "enable"
|
||||
} else {
|
||||
sslMode = "disable"
|
||||
}
|
||||
|
||||
// DB Logger config
|
||||
newLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL threshold
|
||||
LogLevel: logger.Silent, // Log level
|
||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
Colorful: true, // Disable color
|
||||
},
|
||||
)
|
||||
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=Asia/Singapore", host, user, pass, dbName, port, sslMode)
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||
Logger: newLogger,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("db err: (Init) ", err)
|
||||
}
|
||||
DB = db
|
||||
return DB
|
||||
}
|
||||
|
||||
// This function will create a temporarily database for running testing cases
|
||||
func TestDBInit() *gorm.DB {
|
||||
testSQLDB, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testDB, err := gorm.Open(postgres.New(postgres.Config{
|
||||
Conn: testSQLDB,
|
||||
}), &gorm.Config{})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("db err: (TestDBInit) ", err)
|
||||
}
|
||||
|
||||
DB = testDB
|
||||
_ = mock
|
||||
return DB
|
||||
}
|
||||
|
||||
// Using this function to get a connection, you can create your connection pool here.
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
||||
56
backend/internal/common/errresponse.go
Normal file
56
backend/internal/common/errresponse.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
type ErrResponse struct {
|
||||
Err error `json:"-"` // low-level runtime error
|
||||
HTTPStatusCode int `json:"-"` // http response status code
|
||||
|
||||
StatusText string `json:"status"` // user-level status message
|
||||
AppCode int64 `json:"code,omitempty"` // application-specific error code
|
||||
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
|
||||
}
|
||||
|
||||
func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
render.Status(r, e.HTTPStatusCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ErrInvalidRequest(err error) render.Renderer {
|
||||
return &ErrResponse{
|
||||
Err: err,
|
||||
HTTPStatusCode: 400,
|
||||
StatusText: "Invalid request.",
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrValidationError(err error) render.Renderer {
|
||||
return &ErrResponse{
|
||||
Err: err,
|
||||
HTTPStatusCode: 422,
|
||||
StatusText: "Validation error.",
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrInternalError(err error) render.Renderer {
|
||||
return &ErrResponse{
|
||||
Err: err,
|
||||
HTTPStatusCode: 500,
|
||||
StatusText: "Invalid request.",
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrNotFound(err error) render.Renderer {
|
||||
return &ErrResponse{
|
||||
HTTPStatusCode: 404,
|
||||
StatusText: "Resource not found.",
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
24
backend/internal/common/textresponse.go
Normal file
24
backend/internal/common/textresponse.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
type TextResponse struct {
|
||||
Status string `json:"status"` // user-level status message
|
||||
Text string `json:"text"` // application-specific error code
|
||||
}
|
||||
|
||||
func (e *TextResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
render.Status(r, http.StatusOK)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewGenericTextResponse(status string, text string) render.Renderer {
|
||||
return &TextResponse{
|
||||
Status: status,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
52
backend/internal/telegrampackage/main.go
Normal file
52
backend/internal/telegrampackage/main.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package telegrampackage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TelegramHandlerFunction func(shortCode string, text string) (handled bool, responseText *string)
|
||||
|
||||
type Env struct {
|
||||
DB *gorm.DB
|
||||
TelegramAPIKey string
|
||||
HandlerFunctions []TelegramHandlerFunction
|
||||
}
|
||||
|
||||
func NewEnv(db *gorm.DB, telegramAPIKey string, handlerFunctions []TelegramHandlerFunction) *Env {
|
||||
var env Env
|
||||
env.DB = db
|
||||
env.TelegramAPIKey = telegramAPIKey
|
||||
env.HandlerFunctions = handlerFunctions
|
||||
// Example handler function
|
||||
_ = env.telegramHandlerExample
|
||||
|
||||
// Seed random
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Start listening job
|
||||
go env.TelegramListenAndGen()
|
||||
|
||||
return &env
|
||||
}
|
||||
|
||||
func (env *Env) AddTelegramHandlerFunc(handlerFunction TelegramHandlerFunction) {
|
||||
env.HandlerFunctions = append(env.HandlerFunctions, handlerFunction)
|
||||
}
|
||||
|
||||
// Not used normally. Shown here as example only
|
||||
func (env *Env) telegramHandlerExample(shortCode string, text string) (bool, *string) {
|
||||
commandSplitted := ParseTelegramBotCommand(text)
|
||||
if len(commandSplitted) > 0 && commandSplitted[0] == "/command" {
|
||||
responseText := fmt.Sprintf("Your short code is %s.\n\nYour message is %s", shortCode, text)
|
||||
responseText = responseText + fmt.Sprintf("\n\nYour parameters are: %s", strings.Join(commandSplitted[1:], "\n - "))
|
||||
responseText = "<code>" + tgbotapi.EscapeText("HTML", responseText) + "</code>"
|
||||
return true, &responseText
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
35
backend/internal/telegrampackage/parsetwitterbotcommand.go
Normal file
35
backend/internal/telegrampackage/parsetwitterbotcommand.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package telegrampackage
|
||||
|
||||
import "strings"
|
||||
|
||||
func ParseTelegramBotCommand(fullCmd string) []string {
|
||||
var results []string
|
||||
currentInsideQuote := false
|
||||
splitted := strings.Split(fullCmd, " ")
|
||||
for _, currSplit := range splitted {
|
||||
if !currentInsideQuote {
|
||||
if strings.HasPrefix(currSplit, "\"") {
|
||||
if len(currSplit) >= 2 && !strings.HasSuffix(currSplit, "\\\"") && strings.HasSuffix(currSplit, "\"") {
|
||||
currSplit = strings.ReplaceAll(currSplit, "\\\"", "\"")
|
||||
results = append(results, currSplit[1:len(currSplit)-1])
|
||||
} else {
|
||||
currentInsideQuote = true
|
||||
currSplit = strings.ReplaceAll(currSplit, "\\\"", "\"")
|
||||
results = append(results, currSplit[1:])
|
||||
}
|
||||
} else {
|
||||
results = append(results, currSplit)
|
||||
}
|
||||
} else {
|
||||
if !strings.HasSuffix(currSplit, "\\\"") && strings.HasSuffix(currSplit, "\"") {
|
||||
currentInsideQuote = false
|
||||
currSplit = strings.ReplaceAll(currSplit, "\\\"", "\"")
|
||||
results[len(results)-1] = results[len(results)-1] + " " + currSplit[:len(currSplit)-1]
|
||||
} else {
|
||||
currSplit = strings.ReplaceAll(currSplit, "\\\"", "\"")
|
||||
results[len(results)-1] = results[len(results)-1] + " " + currSplit
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
173
backend/internal/telegrampackage/telegram.go
Normal file
173
backend/internal/telegrampackage/telegram.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package telegrampackage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ChatIDMap struct {
|
||||
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
ChatID int64
|
||||
ChatIDShort string
|
||||
}
|
||||
|
||||
func (env *Env) TelegramSend(chatIDShort string, msg string) error {
|
||||
if chatIDShort == "" {
|
||||
return errors.New("no chat ID provided")
|
||||
}
|
||||
bot, err := tgbotapi.NewBotAPI(env.TelegramAPIKey)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
var chatIDMap ChatIDMap
|
||||
err = env.DB.Where(&ChatIDMap{ChatIDShort: chatIDShort}).Last(&chatIDMap).Error
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("No such telegram chat ID")
|
||||
return err
|
||||
}
|
||||
chatMsg := tgbotapi.NewMessage(chatIDMap.ChatID, msg)
|
||||
_, err = bot.Send(chatMsg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Env) TelegramListenAndGen() error {
|
||||
for {
|
||||
bot, err := tgbotapi.NewBotAPI(env.TelegramAPIKey)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("Error occured on Telegram listen. Waiting 1 minute")
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
updates := bot.GetUpdatesChan(u)
|
||||
|
||||
for update := range updates {
|
||||
if update.Message == nil { // ignore any non-Message Updates
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("New message: [%s] %s", update.Message.From.UserName, update.Message.Text)
|
||||
|
||||
// check if contains registration
|
||||
if strings.Compare(update.Message.Text, "/register") == 0 {
|
||||
var chatIDMap ChatIDMap
|
||||
err := env.DB.Where(&ChatIDMap{ChatID: update.Message.Chat.ID}).Last(&chatIDMap).Error
|
||||
// chatID already exists
|
||||
if err == nil {
|
||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "You already have a chat ID")
|
||||
_, err := bot.Send(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
msg = tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Your chat ID is: %s", chatIDMap.ChatIDShort))
|
||||
_, err = bot.Send(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
} else {
|
||||
currChatCode := genShortCode(5)
|
||||
chatIDMap.ChatIDShort = currChatCode
|
||||
chatIDMap.ChatID = update.Message.Chat.ID
|
||||
err := env.DB.Create(&chatIDMap).Error
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("Your chat ID is: %s", chatIDMap.ChatIDShort))
|
||||
_, err = bot.Send(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get his current chat ID
|
||||
var chatIDMap ChatIDMap
|
||||
err := env.DB.Where(&ChatIDMap{ChatID: update.Message.Chat.ID}).Last(&chatIDMap).Error
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
var handled = false
|
||||
for _, currFunc := range env.HandlerFunctions {
|
||||
currHandled, responseText := currFunc(chatIDMap.ChatIDShort, update.Message.Text)
|
||||
handled = handled || currHandled
|
||||
if responseText != nil {
|
||||
splitSendMessage(bot, update.Message.Chat.ID, *responseText)
|
||||
}
|
||||
}
|
||||
if !handled {
|
||||
msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Please send /register to register for a chat ID")
|
||||
_, err := bot.Send(msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genShortCode(n int) string {
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
runeCode := make([]rune, n)
|
||||
for i := range runeCode {
|
||||
runeCode[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(runeCode)
|
||||
}
|
||||
|
||||
func splitSendMessage(bot *tgbotapi.BotAPI, chatID int64, message string) {
|
||||
MAXMESSAGELENGTH := 2000
|
||||
|
||||
leftoverMessage := []rune(tgbotapi.EscapeText("HTML", message))
|
||||
if len(leftoverMessage) > MAXMESSAGELENGTH {
|
||||
for len(leftoverMessage) > 0 {
|
||||
var currMessage []rune
|
||||
if len(leftoverMessage) <= MAXMESSAGELENGTH {
|
||||
currMessage = leftoverMessage[:]
|
||||
leftoverMessage = []rune("")
|
||||
} else {
|
||||
currMessage = leftoverMessage[:MAXMESSAGELENGTH]
|
||||
leftoverMessage = leftoverMessage[MAXMESSAGELENGTH:]
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(chatID, string(currMessage))
|
||||
msg.ParseMode = "HTML"
|
||||
msg.DisableWebPagePreview = true
|
||||
_, err := bot.Send(msg)
|
||||
if err != nil {
|
||||
msg := tgbotapi.NewMessage(chatID, err.Error())
|
||||
bot.Send(msg)
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msg := tgbotapi.NewMessage(chatID, string(message))
|
||||
msg.ParseMode = "HTML"
|
||||
msg.DisableWebPagePreview = true
|
||||
_, err := bot.Send(msg)
|
||||
if err != nil {
|
||||
msg := tgbotapi.NewMessage(chatID, err.Error())
|
||||
bot.Send(msg)
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
34
backend/internal/webhookeverything/main.go
Normal file
34
backend/internal/webhookeverything/main.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package webhookeverything
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"git.samuelpua.com/telboon/webhook-everything/backend/internal/telegrampackage"
|
||||
"github.com/go-chi/chi"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
DB *gorm.DB
|
||||
TelegramEnv *telegrampackage.Env
|
||||
HostUrl string
|
||||
}
|
||||
|
||||
func WebhookEverythingRoutes(db *gorm.DB, telegramEnv *telegrampackage.Env, hostURL string) chi.Router {
|
||||
var env Env
|
||||
env.DB = db
|
||||
env.TelegramEnv = telegramEnv
|
||||
|
||||
// Seed random
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Web Routes
|
||||
r := chi.NewRouter()
|
||||
r.HandleFunc("/routes/{routeID}", env.handleWebhook)
|
||||
|
||||
// Telegram handlers
|
||||
env.TelegramEnv.AddTelegramHandlerFunc(env.registerWebhook)
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package webhookeverything
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"git.samuelpua.com/telboon/webhook-everything/backend/internal/telegrampackage"
|
||||
)
|
||||
|
||||
func (env *Env) registerWebhook(shortCode string, text string) (bool, *string) {
|
||||
commandSplitted := telegrampackage.ParseTelegramBotCommand(text)
|
||||
if len(commandSplitted) > 0 && commandSplitted[0] == "/register-webhook" {
|
||||
newWebhookID := genWebhookCode(6)
|
||||
baseURL, _ := url.Parse(env.HostUrl)
|
||||
baseURL.Path = path.Join(baseURL.Path, "webhook")
|
||||
baseURL.Path = path.Join(baseURL.Path, "routes")
|
||||
baseURL.Path = path.Join(baseURL.Path, newWebhookID)
|
||||
webhookURL := baseURL.String()
|
||||
|
||||
var webhookRoute WebhookRoute
|
||||
webhookRoute.TelegramShortCode = shortCode
|
||||
webhookRoute.WebhookID = newWebhookID
|
||||
env.DB.Create(&webhookRoute)
|
||||
|
||||
responseText := fmt.Sprintf("Your generated webhook URL is: %s", webhookURL)
|
||||
return true, &responseText
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func genWebhookCode(n int) string {
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
runeCode := make([]rune, n)
|
||||
for i := range runeCode {
|
||||
runeCode[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(runeCode)
|
||||
}
|
||||
26
backend/internal/webhookeverything/routegencontrollerweb.go
Normal file
26
backend/internal/webhookeverything/routegencontrollerweb.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package webhookeverything
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
func (env *Env) forwardHookToTelegram(r *http.Request, routeID string) error {
|
||||
// Get Telegram code
|
||||
var routeResult WebhookRoute
|
||||
err := env.DB.Where(&WebhookRoute{WebhookID: routeID}).First(&routeResult).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Dump request as string
|
||||
responseStr, err := httputil.DumpRequest(r, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send telegram
|
||||
env.TelegramEnv.TelegramSend(routeResult.TelegramShortCode, string(responseStr))
|
||||
|
||||
return nil
|
||||
}
|
||||
28
backend/internal/webhookeverything/routegenmodel.go
Normal file
28
backend/internal/webhookeverything/routegenmodel.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package webhookeverything
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type WebhookRoute struct {
|
||||
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
WebhookID string `gorm:"index,unique"`
|
||||
TelegramShortCode string `gorm:"index"`
|
||||
}
|
||||
|
||||
type StatusMessage struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (statusMessage *StatusMessage) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
// Pre-processing before a response is marshalled and sent across the wire
|
||||
return nil
|
||||
}
|
||||
34
backend/internal/webhookeverything/routegenroutes.go
Normal file
34
backend/internal/webhookeverything/routegenroutes.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package webhookeverything
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.samuelpua.com/telboon/webhook-everything/backend/internal/common"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
// Handles pre-generated webhooks
|
||||
// @Summary Pre-generated webhooks
|
||||
// @Description Description
|
||||
// @Tags Webhook
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param webhookID path string true "Pre-registered Webhook Path"
|
||||
// @Success 200 {object} common.TextResponse
|
||||
// @Failure 500 {object} common.ErrResponse
|
||||
// @Router /webhook/routes/{webhookID} [POST]
|
||||
func (env *Env) handleWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
routeID := chi.URLParam(r, "routeID")
|
||||
_ = ctx
|
||||
|
||||
err := env.forwardHookToTelegram(r, routeID)
|
||||
|
||||
if err != nil {
|
||||
render.Render(w, r, common.ErrInternalError(err))
|
||||
return
|
||||
}
|
||||
|
||||
render.Render(w, r, common.NewGenericTextResponse("success", ""))
|
||||
}
|
||||
Reference in New Issue
Block a user