Telegram bot contact intergratie
This commit is contained in:
239
main.go
239
main.go
@ -1,28 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Configuration holds all website configuration
|
||||
type Config struct {
|
||||
CompanyName string
|
||||
KVK string
|
||||
Email string
|
||||
Phone string
|
||||
Port string
|
||||
CompanyName string
|
||||
KVK string
|
||||
Email string
|
||||
Phone string
|
||||
Port string
|
||||
TelegramBotToken string
|
||||
TelegramChatID string
|
||||
}
|
||||
|
||||
// PageData holds data for template rendering
|
||||
type PageData struct {
|
||||
CompanyName string
|
||||
Title string
|
||||
KVK string
|
||||
Email string
|
||||
Phone string
|
||||
Config
|
||||
Title string
|
||||
CurrentYear int
|
||||
ErrorMessage string
|
||||
SuccessMessage string
|
||||
FormData ContactForm
|
||||
}
|
||||
|
||||
// ContactForm holds form data
|
||||
type ContactForm struct {
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
Computer string
|
||||
Service string
|
||||
Message string
|
||||
}
|
||||
|
||||
// Server holds the application state
|
||||
@ -31,6 +51,13 @@ type Server struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
// Rate limiting
|
||||
var (
|
||||
mu sync.Mutex
|
||||
lastSubmissionTime = make(map[string]time.Time)
|
||||
submissionCooldown = 5 * time.Minute
|
||||
)
|
||||
|
||||
// getEnv returns environment variable or default value
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
@ -43,11 +70,13 @@ func getEnv(key, defaultValue string) string {
|
||||
func NewServer() *Server {
|
||||
// Configuration - can be overridden with environment variables
|
||||
config := Config{
|
||||
CompanyName: getEnv("COMPANY_NAME", "Hogeland Linux"),
|
||||
KVK: getEnv("KVK", "12345678"), // Replace with actual KVK number
|
||||
Email: getEnv("EMAIL", "info@hogelandlinux.nl"),
|
||||
Phone: getEnv("PHONE", "+31 6 12345678"),
|
||||
Port: ":" + getEnv("PORT", "8080"),
|
||||
CompanyName: getEnv("COMPANY_NAME", "Hogeland Linux"),
|
||||
KVK: getEnv("KVK", "12345678"), // Replace with actual KVK number
|
||||
Email: getEnv("EMAIL", "info@hogelandlinux.nl"),
|
||||
Phone: getEnv("PHONE", "+31 6 12345678"),
|
||||
Port: ":" + getEnv("PORT", "8080"),
|
||||
TelegramBotToken: getEnv("TELEGRAM_BOT_TOKEN", ""), // Set this in environment
|
||||
TelegramChatID: getEnv("TELEGRAM_CHAT_ID", ""), // Set this in environment
|
||||
}
|
||||
|
||||
// Parse templates with error handling
|
||||
@ -65,11 +94,9 @@ func NewServer() *Server {
|
||||
// createPageData creates PageData with the given title
|
||||
func (s *Server) createPageData(title string) PageData {
|
||||
return PageData{
|
||||
CompanyName: s.config.CompanyName,
|
||||
Config: s.config,
|
||||
Title: title,
|
||||
KVK: s.config.KVK,
|
||||
Email: s.config.Email,
|
||||
Phone: s.config.Phone,
|
||||
CurrentYear: time.Now().Year(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +117,183 @@ func (s *Server) homeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// contactHandler handles the contact page
|
||||
func (s *Server) contactHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := s.createPageData("Contact - " + s.config.CompanyName)
|
||||
|
||||
if r.Method == "POST" {
|
||||
s.handleContactForm(w, r, &data)
|
||||
return
|
||||
}
|
||||
|
||||
s.renderTemplate(w, "contact.html", data)
|
||||
}
|
||||
|
||||
// handleContactForm processes the contact form submission
|
||||
func (s *Server) handleContactForm(w http.ResponseWriter, r *http.Request, data *PageData) {
|
||||
// Get client IP for rate limiting
|
||||
ip := getClientIP(r)
|
||||
|
||||
// Check rate limiting
|
||||
mu.Lock()
|
||||
if lastTime, exists := lastSubmissionTime[ip]; exists {
|
||||
if time.Since(lastTime) < submissionCooldown {
|
||||
mu.Unlock()
|
||||
data.ErrorMessage = "U heeft recent al een bericht verstuurd. Probeer het over een paar minuten opnieuw."
|
||||
s.renderTemplate(w, "contact.html", *data)
|
||||
return
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
// Parse form data
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Printf("Error parsing form: %v", err)
|
||||
data.ErrorMessage = "Er is een fout opgetreden bij het verwerken van uw bericht."
|
||||
s.renderTemplate(w, "contact.html", *data)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract form data
|
||||
form := ContactForm{
|
||||
Name: strings.TrimSpace(r.FormValue("name")),
|
||||
Email: strings.TrimSpace(r.FormValue("email")),
|
||||
Phone: strings.TrimSpace(r.FormValue("phone")),
|
||||
Computer: strings.TrimSpace(r.FormValue("computer")),
|
||||
Service: strings.TrimSpace(r.FormValue("service")),
|
||||
Message: strings.TrimSpace(r.FormValue("message")),
|
||||
}
|
||||
|
||||
// Store form data for re-rendering on error
|
||||
data.FormData = form
|
||||
|
||||
// Validate required fields
|
||||
if form.Name == "" || form.Email == "" || form.Message == "" {
|
||||
data.ErrorMessage = "Vul alle verplichte velden in (naam, email, bericht)."
|
||||
s.renderTemplate(w, "contact.html", *data)
|
||||
return
|
||||
}
|
||||
|
||||
// Send to Telegram if configured
|
||||
if s.config.TelegramBotToken != "" && s.config.TelegramChatID != "" {
|
||||
err := s.sendToTelegram(form)
|
||||
if err != nil {
|
||||
log.Printf("Error sending message to Telegram: %v", err)
|
||||
data.ErrorMessage = "Er is een fout opgetreden bij het versturen van uw bericht. Probeer het later opnieuw."
|
||||
s.renderTemplate(w, "contact.html", *data)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Update last submission time on success
|
||||
mu.Lock()
|
||||
lastSubmissionTime[ip] = time.Now()
|
||||
mu.Unlock()
|
||||
|
||||
// On success, render success message
|
||||
data.SuccessMessage = "Bedankt voor uw bericht! Wij nemen zo snel mogelijk contact met u op."
|
||||
data.FormData = ContactForm{} // Clear form data
|
||||
s.renderTemplate(w, "contact.html", *data)
|
||||
}
|
||||
|
||||
// sendToTelegram sends the contact form data to Telegram
|
||||
func (s *Server) sendToTelegram(form ContactForm) error {
|
||||
// Format message
|
||||
message := fmt.Sprintf(
|
||||
"🔔 Nieuw contactformulier\n\n"+
|
||||
"👤 *Naam:* %s\n"+
|
||||
"📧 *Email:* %s\n"+
|
||||
"📞 *Telefoon:* %s\n"+
|
||||
"💻 *Computer:* %s\n"+
|
||||
"🛠️ *Service:* %s\n\n"+
|
||||
"💬 *Bericht:*\n%s",
|
||||
escapeMarkdown(form.Name),
|
||||
escapeMarkdown(form.Email),
|
||||
escapeMarkdown(form.Phone),
|
||||
escapeMarkdown(form.Computer),
|
||||
escapeMarkdown(form.Service),
|
||||
escapeMarkdown(form.Message),
|
||||
)
|
||||
|
||||
// Prepare Telegram API request
|
||||
telegramURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", s.config.TelegramBotToken)
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"chat_id": s.config.TelegramChatID,
|
||||
"text": message,
|
||||
"parse_mode": "Markdown",
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal JSON: %v", err)
|
||||
}
|
||||
|
||||
// Send HTTP request
|
||||
resp, err := http.Post(telegramURL, "application/json", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send HTTP request: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("Telegram API error: %s, Status Code: %d", string(bodyBytes), resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// escapeMarkdown escapes special characters for Telegram Markdown
|
||||
func escapeMarkdown(text string) string {
|
||||
if text == "" {
|
||||
return "N/A"
|
||||
}
|
||||
// Escape special Markdown characters
|
||||
replacer := strings.NewReplacer(
|
||||
"*", "\\*",
|
||||
"_", "\\_",
|
||||
"`", "\\`",
|
||||
"[", "\\[",
|
||||
"]", "\\]",
|
||||
"(", "\\(",
|
||||
")", "\\)",
|
||||
"~", "\\~",
|
||||
">", "\\>",
|
||||
"#", "\\#",
|
||||
"+", "\\+",
|
||||
"-", "\\-",
|
||||
"=", "\\=",
|
||||
"|", "\\|",
|
||||
"{", "\\{",
|
||||
"}", "\\}",
|
||||
".", "\\.",
|
||||
"!", "\\!",
|
||||
)
|
||||
return replacer.Replace(text)
|
||||
}
|
||||
|
||||
// getClientIP extracts the client IP address from the request
|
||||
func getClientIP(r *http.Request) string {
|
||||
// Check X-Forwarded-For header first (for reverse proxies)
|
||||
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||
// X-Forwarded-For can contain multiple IPs, take the first one
|
||||
if idx := strings.Index(xff, ","); idx != -1 {
|
||||
return strings.TrimSpace(xff[:idx])
|
||||
}
|
||||
return strings.TrimSpace(xff)
|
||||
}
|
||||
|
||||
// Check X-Real-IP header
|
||||
if xri := r.Header.Get("X-Real-IP"); xri != "" {
|
||||
return strings.TrimSpace(xri)
|
||||
}
|
||||
|
||||
// Fall back to RemoteAddr
|
||||
if idx := strings.LastIndex(r.RemoteAddr, ":"); idx != -1 {
|
||||
return r.RemoteAddr[:idx]
|
||||
}
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
||||
// healthHandler provides a simple health check endpoint
|
||||
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
Reference in New Issue
Block a user