|
|
|
@ -0,0 +1,192 @@
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"mime/multipart"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"slices"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/carlmjohnson/requests"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type TelegramMessage struct {
|
|
|
|
|
MessageID int `json:"message_id,omitempty"`
|
|
|
|
|
From TelegramUser `json:"from,omitempty"`
|
|
|
|
|
Chat TelegramChat `json:"chat,omitempty"`
|
|
|
|
|
Voice *TelegramVoice `json:"voice,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TelegramUser struct {
|
|
|
|
|
ID int `json:"id,omitempty"`
|
|
|
|
|
Username string `json:"username,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TelegramChat struct {
|
|
|
|
|
ID int `json:"id,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TelegramVoice struct {
|
|
|
|
|
FileID string `json:"file_id,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TelegramSendMessage struct {
|
|
|
|
|
ChatID int `json:"chat_id,omitempty"`
|
|
|
|
|
Text string `json:"text,omitempty"`
|
|
|
|
|
ReplyParameters *TelegramReplyParameters `json:"reply_parameters,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TelegramReplyParameters struct {
|
|
|
|
|
MessageID int `json:"message_id,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
// Retrieve bot token from environment variable
|
|
|
|
|
botToken := os.Getenv("BOT_TOKEN")
|
|
|
|
|
if botToken == "" {
|
|
|
|
|
log.Fatal("BOT_TOKEN environment variable is not set")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve OpenAI API key
|
|
|
|
|
openaiApiKey := os.Getenv("OPENAI_KEY")
|
|
|
|
|
if openaiApiKey == "" {
|
|
|
|
|
log.Fatal("OPENAI_KEY environment variable is not set")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retrieve allowed users
|
|
|
|
|
allowedUsers := strings.Split(os.Getenv("ALLOWED_USERS"), ",")
|
|
|
|
|
if len(allowedUsers) == 0 {
|
|
|
|
|
log.Fatal("ALLOWED_USERS environment variable is not set")
|
|
|
|
|
}
|
|
|
|
|
log.Println("Allowed users:", allowedUsers)
|
|
|
|
|
|
|
|
|
|
// Set up the HTTP server
|
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
io.WriteString(w, "Hi!")
|
|
|
|
|
})
|
|
|
|
|
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
// Decode the incoming message
|
|
|
|
|
var update struct {
|
|
|
|
|
Message *TelegramMessage `json:"message"`
|
|
|
|
|
}
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&update); err != nil {
|
|
|
|
|
log.Println("Error decoding update:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if update.Message == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check allowed users
|
|
|
|
|
if !(slices.Contains(allowedUsers, update.Message.From.Username) || slices.Contains(allowedUsers, strconv.Itoa(update.Message.From.ID))) {
|
|
|
|
|
sendMessage(botToken, update.Message.Chat.ID, "Sorry, you are not allowed to use this bot!", update.Message.MessageID)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if update.Message.Voice != nil {
|
|
|
|
|
transcript, err := transcriptVoiceMessage(botToken, openaiApiKey, update.Message.Voice.FileID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println("Error transcribing voice message:", err)
|
|
|
|
|
sendMessage(botToken, update.Message.Chat.ID, "Failed to transcribe voice message", update.Message.MessageID)
|
|
|
|
|
} else {
|
|
|
|
|
sendMessage(botToken, update.Message.Chat.ID, transcript, update.Message.MessageID)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sendMessage(botToken, update.Message.Chat.ID, "Please send a voice message", update.Message.MessageID)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Start the HTTP server
|
|
|
|
|
port := os.Getenv("PORT")
|
|
|
|
|
if port == "" {
|
|
|
|
|
port = "8080"
|
|
|
|
|
}
|
|
|
|
|
log.Printf("Starting server on :%s", port)
|
|
|
|
|
log.Fatal(http.ListenAndServe(":"+port, nil))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sendMessage(botToken string, chatID int, text string, replyTo int) {
|
|
|
|
|
msg := &TelegramSendMessage{
|
|
|
|
|
ChatID: chatID,
|
|
|
|
|
Text: text,
|
|
|
|
|
}
|
|
|
|
|
if replyTo != 0 {
|
|
|
|
|
msg.ReplyParameters = &TelegramReplyParameters{
|
|
|
|
|
MessageID: replyTo,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err := requests.URL(fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken)).
|
|
|
|
|
Method(http.MethodPost).
|
|
|
|
|
BodyJSON(msg).
|
|
|
|
|
Fetch(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Println("Error sending message:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func transcriptVoiceMessage(botToken, openaiApiKey, fileID string) (string, error) {
|
|
|
|
|
// Get file path
|
|
|
|
|
var fileResponse struct {
|
|
|
|
|
OK bool `json:"ok"`
|
|
|
|
|
Result struct {
|
|
|
|
|
FilePath string `json:"file_path"`
|
|
|
|
|
} `json:"result"`
|
|
|
|
|
}
|
|
|
|
|
err := requests.URL(fmt.Sprintf("https://api.telegram.org/bot%s/getFile", botToken)).
|
|
|
|
|
Param("file_id", fileID).
|
|
|
|
|
ToJSON(&fileResponse).
|
|
|
|
|
Fetch(context.Background())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Download the file
|
|
|
|
|
voiceDownload := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", botToken, fileResponse.Result.FilePath)
|
|
|
|
|
voiceReader, voiceWriter := io.Pipe()
|
|
|
|
|
go func() {
|
|
|
|
|
voiceWriter.CloseWithError(
|
|
|
|
|
requests.URL(voiceDownload).
|
|
|
|
|
ToWriter(voiceWriter).
|
|
|
|
|
Fetch(context.Background()),
|
|
|
|
|
)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// Transcribe message
|
|
|
|
|
multipartReader, multipartWriter := io.Pipe()
|
|
|
|
|
mpw := multipart.NewWriter(multipartWriter)
|
|
|
|
|
go func() {
|
|
|
|
|
defer multipartWriter.Close()
|
|
|
|
|
mpw.WriteField("model", "whisper-1")
|
|
|
|
|
fileWriter, err := mpw.CreateFormFile("file", "file.oga")
|
|
|
|
|
if err != nil {
|
|
|
|
|
multipartWriter.CloseWithError(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
_, err = io.Copy(fileWriter, voiceReader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
multipartWriter.CloseWithError(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
multipartWriter.CloseWithError(mpw.Close())
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
var transcription struct {
|
|
|
|
|
Text string `json:"text"`
|
|
|
|
|
}
|
|
|
|
|
err = requests.URL("https://api.openai.com/v1/audio/transcriptions").
|
|
|
|
|
Method(http.MethodPost).
|
|
|
|
|
Header("Authorization", fmt.Sprintf("Bearer %s", openaiApiKey)).
|
|
|
|
|
ContentType(mpw.FormDataContentType()).
|
|
|
|
|
BodyReader(multipartReader).
|
|
|
|
|
ToJSON(&transcription).
|
|
|
|
|
Fetch(context.Background())
|
|
|
|
|
|
|
|
|
|
return transcription.Text, err
|
|
|
|
|
}
|