REPOSITORY / ScuroNeko/Laniakea

Pull Requests

PULL REQUESTS REPOSITORY

v1.0.0 #9

Merged
ScuroNeko merged 101 commits from dev into main 2026-05-20 13:43:34 +03:00
4 changed files with 36 additions and 35 deletions
Showing only changes of commit 2fc171d9a3 - Show all commits
+21 -27
View File
@@ -2,6 +2,7 @@ package laniakea
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
@@ -174,7 +175,7 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
return bot return bot
} }
// Close gracefully shuts down the bot. // Close gracefully shuts down bot-owned resources.
// //
// Closes: // Closes:
// - Uploader (waits for pending uploads) // - Uploader (waits for pending uploads)
@@ -182,36 +183,31 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
// - RequestLogger (if enabled) // - RequestLogger (if enabled)
// - Main logger // - Main logger
// //
// Returns the first error encountered, if any. // RunWithContext does not call Close automatically. The caller is responsible
// for invoking Close after RunWithContext returns to release these resources.
//
// Returns a joined error containing all shutdown failures, if any.
func (bot *Bot[T]) Close() error { func (bot *Bot[T]) Close() error {
var firstErr error var e []error
if err := bot.uploader.Close(); err != nil { if err := bot.uploader.Close(); err != nil {
bot.logger.Errorln(err) bot.logger.Errorln(err)
if firstErr == nil { e = append(e, err)
firstErr = err
}
} }
if err := bot.api.CloseApi(); err != nil { if err := bot.api.CloseApi(); err != nil {
bot.logger.Errorln(err) bot.logger.Errorln(err)
if firstErr == nil { e = append(e, err)
firstErr = err
}
} }
if bot.RequestLogger != nil { if bot.RequestLogger != nil {
if err := bot.RequestLogger.Close(); err != nil { if err := bot.RequestLogger.Close(); err != nil {
bot.logger.Errorln(err) bot.logger.Errorln(err)
if firstErr == nil { e = append(e, err)
firstErr = err
}
} }
} }
if err := bot.logger.Close(); err != nil { if err := bot.logger.Close(); err != nil {
if firstErr == nil { e = append(e, err)
firstErr = err
}
} }
return firstErr return errors.Join(e...)
} }
// initLoggers configures the main and optional request loggers. // initLoggers configures the main and optional request loggers.
@@ -466,7 +462,10 @@ func (bot *Bot[T]) AddDatabaseLoggerWriter(writer DbLogger[T]) *Bot[T] {
// The context controls graceful shutdown. When canceled, the bot: // The context controls graceful shutdown. When canceled, the bot:
// - Stops polling for new updates // - Stops polling for new updates
// - Finishes processing currently queued updates // - Finishes processing currently queued updates
// - Closes all resources (API, uploader, loggers) // - Waits for registered runners to exit
//
// RunWithContext does not close API, uploader, or logger resources on return.
// The caller must invoke Close after RunWithContext finishes.
// //
// Example: // Example:
// //
@@ -474,13 +473,8 @@ func (bot *Bot[T]) AddDatabaseLoggerWriter(writer DbLogger[T]) *Bot[T] {
// go bot.RunWithContext(ctx) // go bot.RunWithContext(ctx)
// // ... later ... // // ... later ...
// cancel() // triggers graceful shutdown // cancel() // triggers graceful shutdown
// _ = bot.Close()
func (bot *Bot[T]) RunWithContext(ctx context.Context) { func (bot *Bot[T]) RunWithContext(ctx context.Context) {
defer func() {
if err := bot.Close(); err != nil {
bot.logger.Errorln(err)
}
}()
if len(bot.prefixes) == 0 { if len(bot.prefixes) == 0 {
bot.logger.Fatalln("no prefixes defined") bot.logger.Fatalln("no prefixes defined")
return return
@@ -508,15 +502,15 @@ func (bot *Bot[T]) RunWithContext(ctx context.Context) {
case <-ctx.Done(): case <-ctx.Done():
return return
default: default:
updates, err := bot.Updates() updates, err := bot.Updates(ctx)
if err != nil { if err != nil {
bot.logger.Errorln("failed to fetch updates:", err) bot.logger.Errorln("failed to fetch updates:", err)
time.Sleep(2 * time.Second) // exponential backoff time.Sleep(time.Second) // exponential backoff
continue continue
} }
for _, u := range updates { for _, update := range updates {
u := u // copy loop variable to avoid race condition u := update // copy loop variable to avoid race condition
select { select {
case bot.updateQueue <- &u: case bot.updateQueue <- &u:
case <-ctx.Done(): case <-ctx.Done():
+8 -6
View File
@@ -1,6 +1,7 @@
package laniakea package laniakea
import ( import (
"context"
"encoding/json" "encoding/json"
"git.nix13.pw/scuroneko/laniakea/tgapi" "git.nix13.pw/scuroneko/laniakea/tgapi"
@@ -12,7 +13,7 @@ import (
// through AllowedUpdates and includes optional request logging. // through AllowedUpdates and includes optional request logging.
// //
// Parameters: // Parameters:
// - None (uses bot's internal state for offset and allowed updates) // - ctx: request context used to cancel the in-flight long polling request
// //
// Returns: // Returns:
// - []tgapi.Update: slice of received updates (empty if none available) // - []tgapi.Update: slice of received updates (empty if none available)
@@ -26,19 +27,20 @@ import (
// 5. Automatically updates the offset to the last received update ID + 1 // 5. Automatically updates the offset to the last received update ID + 1
// 6. Returns all received updates (empty slice if none) // 6. Returns all received updates (empty slice if none)
// //
// Note: This is a blocking call that waits up to 30 seconds for new updates. // Note: This is a blocking call that waits up to 30 seconds for new updates,
// For non-blocking behavior, consider using webhooks instead. // unless ctx is canceled earlier. For non-blocking behavior, consider using
// webhooks instead.
// //
// Example: // Example:
// //
// updates, err := bot.Updates() // updates, err := bot.Updates(ctx)
// if err != nil { // if err != nil {
// log.Fatal(err) // log.Fatal(err)
// } // }
// for _, update := range updates { // for _, update := range updates {
// // process update // // process update
// } // }
func (bot *Bot[T]) Updates() ([]tgapi.Update, error) { func (bot *Bot[T]) Updates(ctx context.Context) ([]tgapi.Update, error) {
offset := bot.GetUpdateOffset() offset := bot.GetUpdateOffset()
params := tgapi.UpdateParams{ params := tgapi.UpdateParams{
Offset: Ptr(offset), Offset: Ptr(offset),
@@ -46,7 +48,7 @@ func (bot *Bot[T]) Updates() ([]tgapi.Update, error) {
AllowedUpdates: bot.GetUpdateTypes(), AllowedUpdates: bot.GetUpdateTypes(),
} }
updates, err := bot.api.GetUpdates(params) updates, err := bot.api.GetUpdatesWithContext(ctx, params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+5
View File
@@ -48,6 +48,11 @@ func (api *API) GetUpdates(params UpdateParams) ([]Update, error) {
return req.Do(api) return req.Do(api)
} }
func (api *API) GetUpdatesWithContext(ctx context.Context, params UpdateParams) ([]Update, error) {
req := NewRequest[[]Update]("getUpdates", params)
return req.DoWithContext(ctx, api)
}
// SetWebhookP holds parameters for the setWebhook method. // SetWebhookP holds parameters for the setWebhook method.
// See https://core.telegram.org/bots/api#setwebhook // See https://core.telegram.org/bots/api#setwebhook
type SetWebhookP struct { type SetWebhookP struct {
+2 -2
View File
@@ -1,9 +1,9 @@
package utils package utils
const ( const (
VersionString = "1.0.0-beta.22" VersionString = "1.0.0-rc.2"
VersionMajor = 1 VersionMajor = 1
VersionMinor = 0 VersionMinor = 0
VersionPatch = 0 VersionPatch = 0
VersionBeta = 22 VersionBeta = 2
) )