Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
7a6f135487
|
|||
|
2e9e82d43b
|
|||
|
55d4065259
|
|||
| b89f27574f | |||
| 689eb8a5e2 |
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Laniakea
|
||||||
|
|
||||||
|
A lightweight, easy to use and performance Telegram API wrapper for bot development.
|
||||||
15
bot.go
15
bot.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/extypes"
|
||||||
"git.nix13.pw/scuroneko/slog"
|
"git.nix13.pw/scuroneko/slog"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"github.com/vinovest/sqlx"
|
"github.com/vinovest/sqlx"
|
||||||
@@ -39,7 +40,7 @@ type Bot struct {
|
|||||||
|
|
||||||
updateOffset int
|
updateOffset int
|
||||||
updateTypes []string
|
updateTypes []string
|
||||||
updateQueue *Queue[*Update]
|
updateQueue *extypes.Queue[*Update]
|
||||||
}
|
}
|
||||||
|
|
||||||
type BotSettings struct {
|
type BotSettings struct {
|
||||||
@@ -73,7 +74,7 @@ func LoadPrefixesFromEnv() []string {
|
|||||||
return strings.Split(prefixesS, ";")
|
return strings.Split(prefixesS, ";")
|
||||||
}
|
}
|
||||||
func NewBot(settings *BotSettings) *Bot {
|
func NewBot(settings *BotSettings) *Bot {
|
||||||
updateQueue := CreateQueue[*Update](256)
|
updateQueue := extypes.CreateQueue[*Update](256)
|
||||||
bot := &Bot{
|
bot := &Bot{
|
||||||
updateOffset: 0, plugins: make([]Plugin, 0), debug: settings.Debug, errorTemplate: "%s",
|
updateOffset: 0, plugins: make([]Plugin, 0), debug: settings.Debug, errorTemplate: "%s",
|
||||||
prefixes: settings.Prefixes, updateTypes: make([]string, 0), runners: make([]Runner, 0),
|
prefixes: settings.Prefixes, updateTypes: make([]string, 0), runners: make([]Runner, 0),
|
||||||
@@ -117,6 +118,12 @@ func NewBot(settings *BotSettings) *Bot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u, err := bot.GetMe()
|
||||||
|
if err != nil {
|
||||||
|
bot.logger.Fatal(err)
|
||||||
|
}
|
||||||
|
bot.logger.Infof("Authorized as %s\n", u.FirstName)
|
||||||
|
|
||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,9 +251,7 @@ func (b *Bot) Run() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := &MsgContext{
|
ctx := &MsgContext{Bot: b, Update: u}
|
||||||
Bot: b, Update: u,
|
|
||||||
}
|
|
||||||
for _, middleware := range b.middlewares {
|
for _, middleware := range b.middlewares {
|
||||||
middleware.Execute(ctx, b.dbContext)
|
middleware.Execute(ctx, b.dbContext)
|
||||||
}
|
}
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -1,8 +1,9 @@
|
|||||||
module git.nix13.pw/scuroneko/laniakea
|
module git.nix13.pw/scuroneko/laniakea
|
||||||
|
|
||||||
go 1.25
|
go 1.25.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.nix13.pw/scuroneko/extypes v1.0.2
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2
|
git.nix13.pw/scuroneko/slog v1.0.2
|
||||||
github.com/redis/go-redis/v9 v9.17.3
|
github.com/redis/go-redis/v9 v9.17.3
|
||||||
github.com/vinovest/sqlx v1.7.1
|
github.com/vinovest/sqlx v1.7.1
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,5 +1,7 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
git.nix13.pw/scuroneko/extypes v1.0.2 h1:Qz1InLccaB9crXY33oGrSetPHePKfQAUqW/p/iYXmJk=
|
||||||
|
git.nix13.pw/scuroneko/extypes v1.0.2/go.mod h1:uZVs8Yo3RrYAG9dMad6qR6lsYY67t+459D9c65QAYAw=
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g=
|
git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g=
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI=
|
git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ func (b *Bot) handleMessage(update *Update, ctx *MsgContext) {
|
|||||||
ctx.Text = strings.TrimSpace(text[len(cmd):])
|
ctx.Text = strings.TrimSpace(text[len(cmd):])
|
||||||
ctx.Args = strings.Split(ctx.Text, " ")
|
ctx.Args = strings.Split(ctx.Text, " ")
|
||||||
|
|
||||||
|
if !plugin.executeMiddlewares(ctx, b.dbContext) {
|
||||||
|
return
|
||||||
|
}
|
||||||
go plugin.Execute(cmd, ctx, b.dbContext)
|
go plugin.Execute(cmd, ctx, b.dbContext)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -65,6 +68,10 @@ func (b *Bot) handleCallback(update *Update, ctx *MsgContext) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !plugin.executeMiddlewares(ctx, b.dbContext) {
|
||||||
|
return
|
||||||
|
}
|
||||||
go plugin.ExecutePayload(data.Command, ctx, b.dbContext)
|
go plugin.ExecutePayload(data.Command, ctx, b.dbContext)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
46
methods.go
46
methods.go
@@ -195,3 +195,49 @@ func (b *Bot) SendChatAction(params SendChatActionP) (bool, error) {
|
|||||||
}
|
}
|
||||||
return *res, err
|
return *res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SetMessageReactionP struct {
|
||||||
|
ChatId int `json:"chat_id"`
|
||||||
|
MessageId int `json:"message_id"`
|
||||||
|
IsBig bool `json:"is_big,omitempty"`
|
||||||
|
}
|
||||||
|
type SetMessageReactionEmojiP struct {
|
||||||
|
SetMessageReactionP
|
||||||
|
Reaction []ReactionTypeEmoji `json:"reaction"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) SetMessageReactionEmoji(params SetMessageReactionEmojiP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMessageReaction", params)
|
||||||
|
res, err := req.Do(b)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return *res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetMessageReactionCustomEmojiP struct {
|
||||||
|
SetMessageReactionP
|
||||||
|
Reaction []ReactionTypeCustomEmoji `json:"reaction"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) SetMessageReactionCustom(params SetMessageReactionCustomEmojiP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMessageReaction", params)
|
||||||
|
res, err := req.Do(b)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return *res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetMessageReactionPaidP struct {
|
||||||
|
SetMessageReactionP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) SetMessageReactionPaid(params SetMessageReactionPaidP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMessageReaction", params)
|
||||||
|
res, err := req.Do(b)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return *res, err
|
||||||
|
}
|
||||||
|
|||||||
60
plugins.go
60
plugins.go
@@ -1,6 +1,10 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/extypes"
|
||||||
|
)
|
||||||
|
|
||||||
type CommandExecutor func(ctx *MsgContext, dbContext *DatabaseContext)
|
type CommandExecutor func(ctx *MsgContext, dbContext *DatabaseContext)
|
||||||
|
|
||||||
@@ -9,6 +13,7 @@ type PluginBuilder struct {
|
|||||||
commands map[string]*CommandExecutor
|
commands map[string]*CommandExecutor
|
||||||
payloads map[string]*CommandExecutor
|
payloads map[string]*CommandExecutor
|
||||||
updateListener *CommandExecutor
|
updateListener *CommandExecutor
|
||||||
|
middlewares extypes.Slice[*PluginMiddleware]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
@@ -16,6 +21,7 @@ type Plugin struct {
|
|||||||
Commands map[string]*CommandExecutor
|
Commands map[string]*CommandExecutor
|
||||||
Payloads map[string]*CommandExecutor
|
Payloads map[string]*CommandExecutor
|
||||||
UpdateListener *CommandExecutor
|
UpdateListener *CommandExecutor
|
||||||
|
Middlewares extypes.Slice[*PluginMiddleware]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlugin(name string) *PluginBuilder {
|
func NewPlugin(name string) *PluginBuilder {
|
||||||
@@ -45,6 +51,11 @@ func (p *PluginBuilder) UpdateListener(listener CommandExecutor) *PluginBuilder
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PluginBuilder) Middleware(middleware *PluginMiddleware) *PluginBuilder {
|
||||||
|
p.middlewares = p.middlewares.Push(middleware)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PluginBuilder) Build() Plugin {
|
func (p *PluginBuilder) Build() Plugin {
|
||||||
if len(p.commands) == 0 && len(p.payloads) == 0 {
|
if len(p.commands) == 0 && len(p.payloads) == 0 {
|
||||||
log.Println("no command or payloads")
|
log.Println("no command or payloads")
|
||||||
@@ -54,6 +65,7 @@ func (p *PluginBuilder) Build() Plugin {
|
|||||||
Commands: p.commands,
|
Commands: p.commands,
|
||||||
Payloads: p.payloads,
|
Payloads: p.payloads,
|
||||||
UpdateListener: p.updateListener,
|
UpdateListener: p.updateListener,
|
||||||
|
Middlewares: p.middlewares,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +77,15 @@ func (p *Plugin) ExecutePayload(payload string, ctx *MsgContext, dbContext *Data
|
|||||||
(*p.Payloads[payload])(ctx, dbContext)
|
(*p.Payloads[payload])(ctx, dbContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Plugin) executeMiddlewares(ctx *MsgContext, db *DatabaseContext) bool {
|
||||||
|
for _, m := range p.Middlewares {
|
||||||
|
if !m.Execute(ctx, db) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
Name string
|
Name string
|
||||||
Executor CommandExecutor
|
Executor CommandExecutor
|
||||||
@@ -78,8 +99,8 @@ type MiddlewareBuilder struct {
|
|||||||
async bool
|
async bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMiddleware(name string) *MiddlewareBuilder {
|
func NewMiddleware(name string, executor CommandExecutor) *MiddlewareBuilder {
|
||||||
return &MiddlewareBuilder{name: name, async: false}
|
return &MiddlewareBuilder{name: name, executor: executor, order: 0, async: false}
|
||||||
}
|
}
|
||||||
func (m *MiddlewareBuilder) SetName(name string) *MiddlewareBuilder {
|
func (m *MiddlewareBuilder) SetName(name string) *MiddlewareBuilder {
|
||||||
m.name = name
|
m.name = name
|
||||||
@@ -112,3 +133,36 @@ func (m Middleware) Execute(ctx *MsgContext, db *DatabaseContext) {
|
|||||||
m.Execute(ctx, db)
|
m.Execute(ctx, db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PluginMiddlewareExecutor func(ctx *MsgContext, db *DatabaseContext) bool
|
||||||
|
|
||||||
|
// PluginMiddleware
|
||||||
|
// When async, returned value ignored
|
||||||
|
type PluginMiddleware struct {
|
||||||
|
executor PluginMiddlewareExecutor
|
||||||
|
order int
|
||||||
|
async bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPluginMiddleware(executor PluginMiddlewareExecutor) *PluginMiddleware {
|
||||||
|
return &PluginMiddleware{
|
||||||
|
executor: executor,
|
||||||
|
order: 0,
|
||||||
|
async: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *PluginMiddleware) SetOrder(order int) *PluginMiddleware {
|
||||||
|
m.order = order
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
func (m *PluginMiddleware) SetAsync(async bool) *PluginMiddleware {
|
||||||
|
m.async = async
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
func (m *PluginMiddleware) Execute(ctx *MsgContext, db *DatabaseContext) bool {
|
||||||
|
if m.async {
|
||||||
|
go m.executor(ctx, db)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return m.executor(ctx, db)
|
||||||
|
}
|
||||||
|
|||||||
71
queue.go
71
queue.go
@@ -1,71 +0,0 @@
|
|||||||
package laniakea
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var QueueFullErr = errors.New("queue full")
|
|
||||||
|
|
||||||
type Queue[T any] struct {
|
|
||||||
size uint64
|
|
||||||
mu sync.RWMutex
|
|
||||||
queue []T
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateQueue[T any](size uint64) *Queue[T] {
|
|
||||||
return &Queue[T]{
|
|
||||||
queue: make([]T, 0),
|
|
||||||
size: size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) Enqueue(el T) error {
|
|
||||||
if q.IsFull() {
|
|
||||||
return QueueFullErr
|
|
||||||
}
|
|
||||||
q.queue = append(q.queue, el)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) Peak() T {
|
|
||||||
q.mu.RLock()
|
|
||||||
defer q.mu.RUnlock()
|
|
||||||
return q.queue[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) IsEmpty() bool {
|
|
||||||
return q.Length() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) IsFull() bool {
|
|
||||||
return q.Length() == q.size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) Length() uint64 {
|
|
||||||
q.mu.RLock()
|
|
||||||
defer q.mu.RUnlock()
|
|
||||||
return uint64(len(q.queue))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) Dequeue() T {
|
|
||||||
q.mu.RLock()
|
|
||||||
el := q.queue[0]
|
|
||||||
q.mu.RUnlock()
|
|
||||||
|
|
||||||
if q.Length() == 1 {
|
|
||||||
q.mu.Lock()
|
|
||||||
q.queue = make([]T, 0)
|
|
||||||
q.mu.Unlock()
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
|
|
||||||
q.mu.Lock()
|
|
||||||
q.queue = q.queue[1:]
|
|
||||||
q.mu.Unlock()
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queue[T]) Raw() []T {
|
|
||||||
return q.queue
|
|
||||||
}
|
|
||||||
70
slice.go
70
slice.go
@@ -1,70 +0,0 @@
|
|||||||
package laniakea
|
|
||||||
|
|
||||||
type Slice[T any] []T
|
|
||||||
|
|
||||||
func NewSliceFrom[T any](slice []T) Slice[T] {
|
|
||||||
s := make(Slice[T], len(slice))
|
|
||||||
copy(s[:], slice)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Len() int {
|
|
||||||
return len(s)
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Cap() int {
|
|
||||||
return cap(s)
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Get(index int) T {
|
|
||||||
return s[index]
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Last() T {
|
|
||||||
return s.Get(s.Len() - 1)
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Swap(i, j int) Slice[T] {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Filter(f func(e T) bool) Slice[T] {
|
|
||||||
out := make(Slice[T], 0)
|
|
||||||
for _, v := range s {
|
|
||||||
if f(v) {
|
|
||||||
out = append(out, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Map(f func(e T) T) Slice[T] {
|
|
||||||
out := make(Slice[T], s.Len())
|
|
||||||
for i, v := range s {
|
|
||||||
out[i] = f(v)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Pop(index int) Slice[T] {
|
|
||||||
if index == 0 {
|
|
||||||
return s[1:]
|
|
||||||
}
|
|
||||||
out := make(Slice[T], s.Len()-index)
|
|
||||||
for i, e := range s {
|
|
||||||
if i == index {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
out[i] = e
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
func (s Slice[T]) Push(e T) Slice[T] {
|
|
||||||
return append(s, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Slice[T]) ToArray() []T {
|
|
||||||
out := make([]T, len(s))
|
|
||||||
copy(out, s)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
func (s Slice[T]) ToAnyArray() []any {
|
|
||||||
out := make([]any, len(s))
|
|
||||||
for i, v := range s {
|
|
||||||
out[i] = v
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
8
types.go
8
types.go
@@ -1,5 +1,7 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
|
import "git.nix13.pw/scuroneko/extypes"
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
UpdateID int `json:"update_id"`
|
UpdateID int `json:"update_id"`
|
||||||
Message *Message `json:"message"`
|
Message *Message `json:"message"`
|
||||||
@@ -54,9 +56,9 @@ type Message struct {
|
|||||||
Chat *Chat `json:"chat,omitempty"`
|
Chat *Chat `json:"chat,omitempty"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
|
|
||||||
Photo Slice[*PhotoSize] `json:"photo,omitempty"`
|
Photo extypes.Slice[*PhotoSize] `json:"photo,omitempty"`
|
||||||
Caption string `json:"caption,omitempty"`
|
Caption string `json:"caption,omitempty"`
|
||||||
ReplyToMessage *Message `json:"reply_to_message"`
|
ReplyToMessage *Message `json:"reply_to_message"`
|
||||||
|
|
||||||
ReplyMarkup *MessageReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *MessageReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionString = "0.3.7"
|
VersionString = "0.3.9"
|
||||||
VersionMajor = 0
|
VersionMajor = 0
|
||||||
VersionMinor = 3
|
VersionMinor = 3
|
||||||
VersionPatch = 7
|
VersionPatch = 9
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user