Compare commits
2 Commits
v1.0.0-bet
...
v1.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
4dc172a3b5
|
|||
|
f42d47af53
|
15
Makefile
Normal file
15
Makefile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Проверка наличия golangci-lint
|
||||||
|
GO_LINT := $(shell command -v golangci-lint 2>/dev/null)
|
||||||
|
|
||||||
|
# Цель: запуск всех проверок кода
|
||||||
|
check:
|
||||||
|
@echo "🔍 Running code checks..."
|
||||||
|
@go mod tidy -v
|
||||||
|
@go vet ./...
|
||||||
|
@if [ -n "$(GO_LINT)" ]; then \
|
||||||
|
echo "✅ golangci-lint found, running..." && \
|
||||||
|
golangci-lint run --timeout=5m --verbose; \
|
||||||
|
else \
|
||||||
|
echo "⚠️ golangci-lint not installed. Install with: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.57.2"; \
|
||||||
|
fi
|
||||||
|
@go test -race -v ./... 2>/dev/null || echo "⚠️ Tests skipped or failed (run manually with 'go test -race ./...')"
|
||||||
@@ -16,7 +16,7 @@ func generateBotCommand[T any](cmd Command[T]) tgapi.BotCommand {
|
|||||||
var descArgs []string
|
var descArgs []string
|
||||||
for _, a := range cmd.args {
|
for _, a := range cmd.args {
|
||||||
if a.required {
|
if a.required {
|
||||||
descArgs = append(descArgs, fmt.Sprintf("%s", a.text))
|
descArgs = append(descArgs, a.text)
|
||||||
} else {
|
} else {
|
||||||
descArgs = append(descArgs, fmt.Sprintf("[%s]", a.text))
|
descArgs = append(descArgs, fmt.Sprintf("[%s]", a.text))
|
||||||
}
|
}
|
||||||
|
|||||||
21
drafts.go
21
drafts.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type draftIdGenerator interface {
|
type draftIdGenerator interface {
|
||||||
@@ -55,7 +56,8 @@ type Draft struct {
|
|||||||
func NewRandomDraftProvider(api *tgapi.API) *DraftProvider {
|
func NewRandomDraftProvider(api *tgapi.API) *DraftProvider {
|
||||||
return &DraftProvider{
|
return &DraftProvider{
|
||||||
api: api, generator: &RandomDraftIdGenerator{},
|
api: api, generator: &RandomDraftIdGenerator{},
|
||||||
drafts: make(map[uint64]*Draft),
|
parseMode: tgapi.ParseMDV2,
|
||||||
|
drafts: make(map[uint64]*Draft),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func NewLinearDraftProvider(api *tgapi.API, startValue uint64) *DraftProvider {
|
func NewLinearDraftProvider(api *tgapi.API, startValue uint64) *DraftProvider {
|
||||||
@@ -84,9 +86,12 @@ func (d *DraftProvider) NewDraft() *Draft {
|
|||||||
d.drafts[id] = draft
|
d.drafts[id] = draft
|
||||||
return draft
|
return draft
|
||||||
}
|
}
|
||||||
|
func (d *Draft) push(text string, escapeMd bool) error {
|
||||||
func (d *Draft) Push(newText string) error {
|
if escapeMd {
|
||||||
d.Message += newText
|
text += utils.EscapeMarkdownV2(text)
|
||||||
|
} else {
|
||||||
|
text += text
|
||||||
|
}
|
||||||
params := tgapi.SendMessageDraftP{
|
params := tgapi.SendMessageDraftP{
|
||||||
ChatID: d.chatID,
|
ChatID: d.chatID,
|
||||||
DraftID: d.ID,
|
DraftID: d.ID,
|
||||||
@@ -100,6 +105,14 @@ func (d *Draft) Push(newText string) error {
|
|||||||
_, err := d.api.SendMessageDraft(params)
|
_, err := d.api.SendMessageDraft(params)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Draft) Push(text string) error {
|
||||||
|
return d.push(text, true)
|
||||||
|
}
|
||||||
|
func (d *Draft) PushMarkdown(text string) error {
|
||||||
|
return d.push(text, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Draft) Clear() {
|
func (d *Draft) Clear() {
|
||||||
d.Message = ""
|
d.Message = ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,12 +99,15 @@ func (m *AnswerMessage) EditCaptionKeyboard(text string, kb *InlineKeyboard) *An
|
|||||||
return m.ctx.editPhotoText(m.MessageID, text, kb)
|
return m.ctx.editPhotoText(m.MessageID, text, kb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, escapeMd bool) *AnswerMessage {
|
||||||
params := tgapi.SendMessageP{
|
params := tgapi.SendMessageP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
ChatID: ctx.Msg.Chat.ID,
|
||||||
Text: text,
|
Text: text,
|
||||||
ParseMode: tgapi.ParseMDV2,
|
ParseMode: tgapi.ParseMDV2,
|
||||||
}
|
}
|
||||||
|
if escapeMd {
|
||||||
|
params.Text = utils.EscapeMarkdownV2(text)
|
||||||
|
}
|
||||||
if keyboard != nil {
|
if keyboard != nil {
|
||||||
params.ReplyMarkup = keyboard.Get()
|
params.ReplyMarkup = keyboard.Get()
|
||||||
}
|
}
|
||||||
@@ -129,23 +132,35 @@ func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMess
|
|||||||
MessageID: msg.MessageID, ctx: ctx, IsMedia: false, Text: text,
|
MessageID: msg.MessageID, ctx: ctx, IsMedia: false, Text: text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (ctx *MsgContext) AnswerMarkdown(text string) *AnswerMessage {
|
||||||
|
return ctx.answer(text, nil, false)
|
||||||
|
}
|
||||||
func (ctx *MsgContext) Answer(text string) *AnswerMessage {
|
func (ctx *MsgContext) Answer(text string) *AnswerMessage {
|
||||||
return ctx.answer(text, nil)
|
return ctx.answer(text, nil, true)
|
||||||
|
}
|
||||||
|
func (ctx *MsgContext) AnswerfMarkdown(template string, args ...any) *AnswerMessage {
|
||||||
|
return ctx.answer(fmt.Sprintf(template, args...), nil, false)
|
||||||
}
|
}
|
||||||
func (ctx *MsgContext) Answerf(template string, args ...any) *AnswerMessage {
|
func (ctx *MsgContext) Answerf(template string, args ...any) *AnswerMessage {
|
||||||
return ctx.answer(fmt.Sprintf(template, args...), nil)
|
return ctx.answer(fmt.Sprintf(template, args...), nil, true)
|
||||||
|
}
|
||||||
|
func (ctx *MsgContext) KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
||||||
|
return ctx.answer(text, keyboard, false)
|
||||||
}
|
}
|
||||||
func (ctx *MsgContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
func (ctx *MsgContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||||
return ctx.answer(text, kb)
|
return ctx.answer(text, kb, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, escapeMd bool) *AnswerMessage {
|
||||||
params := tgapi.SendPhotoP{
|
params := tgapi.SendPhotoP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
ChatID: ctx.Msg.Chat.ID,
|
||||||
Caption: text,
|
Caption: text,
|
||||||
ParseMode: tgapi.ParseMD,
|
ParseMode: tgapi.ParseMDV2,
|
||||||
Photo: photoId,
|
Photo: photoId,
|
||||||
}
|
}
|
||||||
|
if escapeMd {
|
||||||
|
params.Caption = utils.EscapeMarkdownV2(text)
|
||||||
|
}
|
||||||
if kb != nil {
|
if kb != nil {
|
||||||
params.ReplyMarkup = kb.Get()
|
params.ReplyMarkup = kb.Get()
|
||||||
}
|
}
|
||||||
@@ -165,10 +180,24 @@ func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard) *An
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (ctx *MsgContext) AnswerPhoto(photoId, text string) *AnswerMessage {
|
func (ctx *MsgContext) AnswerPhoto(photoId, text string) *AnswerMessage {
|
||||||
return ctx.answerPhoto(photoId, text, nil)
|
return ctx.answerPhoto(photoId, text, nil, true)
|
||||||
}
|
}
|
||||||
|
func (ctx *MsgContext) AnswerPhotoMarkdown(photoId, text string) *AnswerMessage {
|
||||||
|
return ctx.answerPhoto(photoId, text, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *MsgContext) AnswerPhotoKeyboard(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
func (ctx *MsgContext) AnswerPhotoKeyboard(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
||||||
return ctx.answerPhoto(photoId, text, kb)
|
return ctx.answerPhoto(photoId, text, kb, true)
|
||||||
|
}
|
||||||
|
func (ctx *MsgContext) AnswerPhotoKeyboardMarkdown(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
||||||
|
return ctx.answerPhoto(photoId, text, kb, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *MsgContext) AnswerPhotof(photoId, template string, args ...any) *AnswerMessage {
|
||||||
|
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, true)
|
||||||
|
}
|
||||||
|
func (ctx *MsgContext) AnswerPhotofMarkdown(photoId, template string, args ...any) *AnswerMessage {
|
||||||
|
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *MsgContext) delete(messageId int) {
|
func (ctx *MsgContext) delete(messageId int) {
|
||||||
@@ -214,12 +243,12 @@ func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *MsgContext) error(err error) {
|
func (ctx *MsgContext) error(err error) {
|
||||||
text := fmt.Sprintf(ctx.errorTemplate, utils.EscapeMarkdown(err.Error()))
|
text := fmt.Sprintf(ctx.errorTemplate, err.Error())
|
||||||
|
|
||||||
if ctx.CallbackQueryId != "" {
|
if ctx.CallbackQueryId != "" {
|
||||||
ctx.answerCallbackQuery("", text, false)
|
ctx.answerCallbackQuery("", text, false)
|
||||||
} else {
|
} else {
|
||||||
ctx.answer(text, nil)
|
ctx.answer(text, nil, true)
|
||||||
}
|
}
|
||||||
ctx.botLogger.Errorln(err)
|
ctx.botLogger.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
CommandRegexInt = regexp.MustCompile("\\d+")
|
CommandRegexInt = regexp.MustCompile(`\d+`)
|
||||||
CommandRegexString = regexp.MustCompile(".+")
|
CommandRegexString = regexp.MustCompile(".+")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,9 @@ func (api *API) DeclineChatJoinRequest(params DeclineChatJoinRequestP) (bool, er
|
|||||||
|
|
||||||
func (api *API) SetChatPhoto() {
|
func (api *API) SetChatPhoto() {
|
||||||
uploader := NewUploader(api)
|
uploader := NewUploader(api)
|
||||||
defer uploader.Close()
|
defer func() {
|
||||||
|
_ = uploader.Close()
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteChatPhotoP struct {
|
type DeleteChatPhotoP struct {
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ func (api *API) GetFileByLink(link string) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
return io.ReadAll(res.Body)
|
return io.ReadAll(res.Body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,20 +261,20 @@ type Gifts struct {
|
|||||||
Gifts []Gift `json:"gifts"`
|
Gifts []Gift `json:"gifts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OwnedGiftType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OwnedGiftRegularType OwnedGiftType = "regular"
|
OwnedGiftRegularType OwnedGiftType = "regular"
|
||||||
OwnedGiftUniqueType OwnedGiftType = "unique"
|
OwnedGiftUniqueType OwnedGiftType = "unique"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OwnedGiftType string
|
type OwnedGift struct {
|
||||||
type BaseOwnedGift struct {
|
|
||||||
Type OwnedGiftType `json:"type"`
|
Type OwnedGiftType `json:"type"`
|
||||||
OwnerGiftID *string `json:"owner_gift_id,omitempty"`
|
OwnerGiftID *string `json:"owner_gift_id,omitempty"`
|
||||||
SendDate *int `json:"send_date,omitempty"`
|
SendDate *int `json:"send_date,omitempty"`
|
||||||
IsSaved *bool `json:"is_saved,omitempty"`
|
IsSaved *bool `json:"is_saved,omitempty"`
|
||||||
}
|
|
||||||
type OwnedGiftRegular struct {
|
// Поля, характерные для "regular"
|
||||||
BaseOwnedGift
|
|
||||||
Gift Gift `json:"gift"`
|
Gift Gift `json:"gift"`
|
||||||
SenderUser User `json:"sender_user,omitempty"`
|
SenderUser User `json:"sender_user,omitempty"`
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
@@ -286,18 +286,15 @@ type OwnedGiftRegular struct {
|
|||||||
PrepaidUpgradeStarCount *int `json:"prepaid_upgrade_star_count,omitempty"`
|
PrepaidUpgradeStarCount *int `json:"prepaid_upgrade_star_count,omitempty"`
|
||||||
IsUpgradeSeparate *bool `json:"is_upgrade_separate,omitempty"`
|
IsUpgradeSeparate *bool `json:"is_upgrade_separate,omitempty"`
|
||||||
UniqueGiftNumber *int `json:"unique_gift_number,omitempty"`
|
UniqueGiftNumber *int `json:"unique_gift_number,omitempty"`
|
||||||
}
|
|
||||||
type OwnedGiftUnique struct {
|
// Поля, характерные для "unique"
|
||||||
BaseOwnedGift
|
|
||||||
CanBeTransferred *bool `json:"can_be_transferred,omitempty"`
|
CanBeTransferred *bool `json:"can_be_transferred,omitempty"`
|
||||||
TransferStarCount *int `json:"transfer_star_count,omitempty"`
|
TransferStarCount *int `json:"transfer_star_count,omitempty"`
|
||||||
NextTransferDate *int `json:"next_transfer_date,omitempty"`
|
NextTransferDate *int `json:"next_transfer_date,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OwnedGifts struct {
|
type OwnedGifts struct {
|
||||||
TotalCount int `json:"total_count"`
|
TotalCount int `json:"total_count"`
|
||||||
Gifts []struct {
|
Gifts []OwnedGift `json:"gifts"`
|
||||||
OwnedGiftRegular
|
NextOffset string `json:"next_offset"`
|
||||||
OwnedGiftUnique
|
|
||||||
} `json:"gifts"`
|
|
||||||
NextOffset string `json:"next_offset"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
|||||||
|
|
||||||
body, err := readBody(resp.Body)
|
body, err := readBody(resp.Body)
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
up.logger.Debugln("UPLOADER RES", r.method, string(body))
|
up.logger.Debugln("UPLOADER RES", r.method, string(body))
|
||||||
|
|
||||||
response, err := parseBody[R](body)
|
response, err := parseBody[R](body)
|
||||||
@@ -145,7 +148,7 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
|||||||
func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader) (R, error) {
|
func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader) (R, error) {
|
||||||
var zero R
|
var zero R
|
||||||
|
|
||||||
result, err := up.api.pool.Submit(ctx, func(ctx context.Context) (any, error) {
|
result, err := up.api.pool.submit(ctx, func(ctx context.Context) (any, error) {
|
||||||
return r.doRequest(ctx, up)
|
return r.doRequest(ctx, up)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -156,10 +159,10 @@ func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader)
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return zero, ctx.Err()
|
return zero, ctx.Err()
|
||||||
case res := <-result:
|
case res := <-result:
|
||||||
if res.Err != nil {
|
if res.err != nil {
|
||||||
return zero, res.Err
|
return zero, res.err
|
||||||
}
|
}
|
||||||
if val, ok := res.Value.(R); ok {
|
if val, ok := res.value.(R); ok {
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
return zero, ErrPoolUnexpected
|
return zero, ErrPoolUnexpected
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionString = "1.0.0-beta.9"
|
VersionString = "1.0.0-beta.11"
|
||||||
VersionMajor = 1
|
VersionMajor = 1
|
||||||
VersionMinor = 0
|
VersionMinor = 0
|
||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
Beta = 9
|
Beta = 11
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user