1
0
Fork 0

Adding upstream version 0.8.9.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-22 10:16:14 +02:00
parent 3b2c48b5e4
commit c0c4addb85
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
285 changed files with 25880 additions and 0 deletions

View file

@ -0,0 +1,215 @@
package telegram
import (
"errors"
"fmt"
"io"
"os"
"os/signal"
"slices"
"strconv"
"syscall"
"github.com/nicholas-fedor/shoutrrr/pkg/format"
"github.com/nicholas-fedor/shoutrrr/pkg/types"
"github.com/nicholas-fedor/shoutrrr/pkg/util/generator"
)
// UpdatesLimit defines the number of updates to retrieve per API call.
const (
UpdatesLimit = 10 // Number of updates to retrieve per call
UpdatesTimeout = 120 // Timeout in seconds for long polling
)
// ErrNoChatsSelected indicates that no chats were selected during generation.
var (
ErrNoChatsSelected = errors.New("no chats were selected")
)
// Generator facilitates Telegram-specific URL generation via user interaction.
type Generator struct {
userDialog *generator.UserDialog
client *Client
chats []string
chatNames []string
chatTypes []string
done bool
botName string
Reader io.Reader
Writer io.Writer
}
// Generate creates a Telegram Shoutrrr configuration from user dialog input.
func (g *Generator) Generate(
_ types.Service,
props map[string]string,
_ []string,
) (types.ServiceConfig, error) {
var config Config
if g.Reader == nil {
g.Reader = os.Stdin
}
if g.Writer == nil {
g.Writer = os.Stdout
}
g.userDialog = generator.NewUserDialog(g.Reader, g.Writer, props)
userDialog := g.userDialog
userDialog.Writelnf(
"To start we need your bot token. If you haven't created a bot yet, you can use this link:",
)
userDialog.Writelnf(" %v", format.ColorizeLink("https://t.me/botfather?start"))
userDialog.Writelnf("")
token := userDialog.QueryString(
"Enter your bot token:",
generator.ValidateFormat(IsTokenValid),
"token",
)
userDialog.Writelnf("Fetching bot info...")
g.client = &Client{token: token}
botInfo, err := g.client.GetBotInfo()
if err != nil {
return &Config{}, err
}
g.botName = botInfo.Username
userDialog.Writelnf("")
userDialog.Writelnf(
"Okay! %v will listen for any messages in PMs and group chats it is invited to.",
format.ColorizeString("@", g.botName),
)
g.done = false
lastUpdate := 0
signals := make(chan os.Signal, 1)
// Subscribe to system signals
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
for !g.done {
userDialog.Writelnf("Waiting for messages to arrive...")
updates, err := g.client.GetUpdates(lastUpdate, UpdatesLimit, UpdatesTimeout, nil)
if err != nil {
panic(err)
}
// If no updates were retrieved, prompt user to continue
promptDone := len(updates) == 0
for _, update := range updates {
lastUpdate = update.UpdateID + 1
switch {
case update.Message != nil || update.ChannelPost != nil:
message := update.Message
if update.ChannelPost != nil {
message = update.ChannelPost
}
chat := message.Chat
source := message.Chat.Name()
if message.From != nil {
source = "@" + message.From.Username
}
userDialog.Writelnf("Got Message '%v' from %v in %v chat %v",
format.ColorizeString(message.Text),
format.ColorizeProp(source),
format.ColorizeEnum(chat.Type),
format.ColorizeNumber(chat.ID))
userDialog.Writelnf(g.addChat(chat))
// Another chat was added, prompt user to continue
promptDone = true
case update.ChatMemberUpdate != nil:
cmu := update.ChatMemberUpdate
oldStatus := cmu.OldChatMember.Status
newStatus := cmu.NewChatMember.Status
userDialog.Writelnf(
"Got a bot chat member update for %v, status was changed from %v to %v",
format.ColorizeProp(cmu.Chat.Name()),
format.ColorizeEnum(oldStatus),
format.ColorizeEnum(newStatus),
)
default:
userDialog.Writelnf("Got unknown Update. Ignored!")
}
}
if promptDone {
userDialog.Writelnf("")
g.done = !userDialog.QueryBool(
fmt.Sprintf("Got %v chat ID(s) so far. Want to add some more?",
format.ColorizeNumber(len(g.chats))),
"",
)
}
}
userDialog.Writelnf("")
userDialog.Writelnf("Cleaning up the bot session...")
// Notify API that we got the updates
if _, err = g.client.GetUpdates(lastUpdate, 0, 0, nil); err != nil {
g.userDialog.Writelnf(
"Failed to mark last updates as received: %v",
format.ColorizeError(err),
)
}
if len(g.chats) < 1 {
return nil, ErrNoChatsSelected
}
userDialog.Writelnf("Selected chats:")
for i, id := range g.chats {
name := g.chatNames[i]
chatType := g.chatTypes[i]
userDialog.Writelnf(
" %v (%v) %v",
format.ColorizeNumber(id),
format.ColorizeEnum(chatType),
format.ColorizeString(name),
)
}
userDialog.Writelnf("")
config = Config{
Notification: true,
Token: token,
Chats: g.chats,
}
return &config, nil
}
// addChat adds a chat to the generator's list if its not already present.
func (g *Generator) addChat(chat *Chat) string {
chatID := strconv.FormatInt(chat.ID, 10)
name := chat.Name()
if slices.Contains(g.chats, chatID) {
return fmt.Sprintf("chat %v is already selected!", format.ColorizeString(name))
}
g.chats = append(g.chats, chatID)
g.chatNames = append(g.chatNames, name)
g.chatTypes = append(g.chatTypes, chat.Type)
return fmt.Sprintf("Added new chat %v!", format.ColorizeString(name))
}