diff --git a/api.go b/api.go deleted file mode 100644 index 6fa7458..0000000 --- a/api.go +++ /dev/null @@ -1,91 +0,0 @@ -package laniakea - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - - "git.nix13.pw/scuroneko/slog" -) - -type Api struct { - token string - logger *slog.Logger -} - -func NewAPI(token string) *Api { - l := slog.CreateLogger().Level(GetLoggerLevel()).Prefix("API") - l.AddWriter(l.CreateJsonStdoutWriter()) - return &Api{token, l} -} -func (api *Api) CloseApi() error { - return api.logger.Close() -} - -type ApiResponse[R any] struct { - Ok bool `json:"ok"` - Description string `json:"description,omitempty"` - Result R `json:"result,omitempty"` - ErrorCode int `json:"error_code,omitempty"` -} - -type TelegramRequest[R, P any] struct { - method string - params P -} - -func NewRequest[R, P any](method string, params P) TelegramRequest[R, P] { - return TelegramRequest[R, P]{method: method, params: params} -} -func (r TelegramRequest[R, P]) Do(api *Api) (*R, error) { - buf := bytes.NewBuffer(nil) - err := json.NewEncoder(buf).Encode(r.params) - if err != nil { - return nil, err - } - - u := fmt.Sprintf("https://api.telegram.org/bot%s/%s", api.token, r.method) - if api.logger != nil { - api.logger.Debugln(strings.ReplaceAll(fmt.Sprintf( - "POST %s %s", u, buf.String(), - ), api.token, "")) - } - - res, err := http.Post(u, "application/json", buf) - if err != nil { - return nil, err - } - defer res.Body.Close() - data, err := io.ReadAll(res.Body) - if err != nil { - return nil, err - } - - if api.logger != nil { - api.logger.Debugln(fmt.Sprintf("RES %s %s", r.method, string(data))) - } - - response := new(ApiResponse[R]) - err = json.Unmarshal(data, &response) - if err != nil { - return nil, err - } - - if !response.Ok { - return nil, fmt.Errorf("[%d] %s", response.ErrorCode, response.Description) - } - return &response.Result, nil -} - -func (b *Bot) GetFileByLink(link string) ([]byte, error) { - u := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", b.token, link) - res, err := http.Get(u) - if err != nil { - return nil, err - } - defer res.Body.Close() - return io.ReadAll(res.Body) -} diff --git a/bot.go b/bot.go index f17db42..e92db57 100644 --- a/bot.go +++ b/bot.go @@ -9,27 +9,20 @@ import ( "time" "git.nix13.pw/scuroneko/extypes" + "git.nix13.pw/scuroneko/laniakea/tgapi" "git.nix13.pw/scuroneko/slog" "github.com/redis/go-redis/v9" "github.com/vinovest/sqlx" "go.mongodb.org/mongo-driver/v2/mongo" ) -type ParseMode string - -const ( - ParseMDV2 ParseMode = "MarkdownV2" - ParseHTML ParseMode = "HTML" - ParseMD ParseMode = "Markdown" -) - type Bot struct { token string debug bool errorTemplate string logger *slog.Logger - requestLogger *slog.Logger + RequestLogger *slog.Logger plugins []Plugin middlewares []Middleware @@ -37,15 +30,20 @@ type Bot struct { runners []Runner dbContext *DatabaseContext - api *Api + api *tgapi.Api dbWriterRequested extypes.Slice[*slog.Logger] updateOffset int updateTypes []string - updateQueue *extypes.Queue[*Update] + updateQueue *extypes.Queue[*tgapi.Update] } +func (b *Bot) GetUpdateOffset() int { return b.updateOffset } +func (b *Bot) SetUpdateOffset(offset int) { b.updateOffset = offset } +func (b *Bot) GetUpdateTypes() []string { return b.updateTypes } +func (b *Bot) GetQueue() *extypes.Queue[*tgapi.Update] { return b.updateQueue } + type BotSettings struct { Token string Debug bool @@ -77,15 +75,15 @@ func LoadPrefixesFromEnv() []string { return strings.Split(prefixesS, ";") } func NewBot(settings *BotSettings) *Bot { - updateQueue := extypes.CreateQueue[*Update](256) - api := NewAPI(settings.Token) + updateQueue := extypes.CreateQueue[*tgapi.Update](256) + api := tgapi.NewAPI(settings.Token) bot := &Bot{ updateOffset: 0, plugins: make([]Plugin, 0), debug: settings.Debug, errorTemplate: "%s", prefixes: settings.Prefixes, updateTypes: make([]string, 0), runners: make([]Runner, 0), updateQueue: updateQueue, api: api, dbWriterRequested: make([]*slog.Logger, 0), token: settings.Token, } - bot.dbWriterRequested = bot.dbWriterRequested.Push(api.logger) + bot.dbWriterRequested = bot.dbWriterRequested.Push(api.Logger) if len(settings.ErrorTemplate) > 0 { bot.errorTemplate = settings.ErrorTemplate @@ -111,15 +109,15 @@ func NewBot(settings *BotSettings) *Bot { } if settings.UseRequestLogger { - bot.requestLogger = slog.CreateLogger().Level(level).Prefix("REQUESTS") - bot.requestLogger.AddWriter(bot.requestLogger.CreateJsonStdoutWriter()) + bot.RequestLogger = slog.CreateLogger().Level(level).Prefix("REQUESTS") + bot.RequestLogger.AddWriter(bot.RequestLogger.CreateJsonStdoutWriter()) if settings.WriteToFile { path := fmt.Sprintf("%s/requests.log", strings.TrimRight(settings.LoggerBasePath, "/")) - fileWriter, err := bot.requestLogger.CreateTextFileWriter(path) + fileWriter, err := bot.RequestLogger.CreateTextFileWriter(path) if err != nil { bot.logger.Fatal(err) } - bot.requestLogger.AddWriter(fileWriter) + bot.RequestLogger.AddWriter(fileWriter) } } @@ -137,7 +135,7 @@ func (b *Bot) Close() { if err != nil { log.Println(err) } - err = b.requestLogger.Close() + err = b.RequestLogger.Close() if err != nil { log.Println(err) } @@ -152,8 +150,8 @@ type DatabaseContext struct { func (b *Bot) AddDatabaseLogger(writer func(db *DatabaseContext) slog.LoggerWriter) *Bot { w := writer(b.dbContext) b.logger.AddWriter(w) - if b.requestLogger != nil { - b.requestLogger.AddWriter(w) + if b.RequestLogger != nil { + b.RequestLogger.AddWriter(w) } for _, l := range b.dbWriterRequested { l.AddWriter(w) diff --git a/handler.go b/handler.go index 487843d..464161c 100644 --- a/handler.go +++ b/handler.go @@ -3,9 +3,11 @@ package laniakea import ( "encoding/json" "strings" + + "git.nix13.pw/scuroneko/laniakea/tgapi" ) -func (b *Bot) handleMessage(update *Update, ctx *MsgContext) { +func (b *Bot) handleMessage(update *tgapi.Update, ctx *MsgContext) { if update.Message == nil { return } @@ -60,7 +62,7 @@ func (b *Bot) handleMessage(update *Update, ctx *MsgContext) { } } -func (b *Bot) handleCallback(update *Update, ctx *MsgContext) { +func (b *Bot) handleCallback(update *tgapi.Update, ctx *MsgContext) { data := new(CallbackData) err := json.Unmarshal([]byte(update.CallbackQuery.Data), data) if err != nil { diff --git a/keyboard.go b/keyboard.go index 85d1095..01b400e 100644 --- a/keyboard.go +++ b/keyboard.go @@ -5,18 +5,19 @@ import ( "fmt" "git.nix13.pw/scuroneko/extypes" + "git.nix13.pw/scuroneko/laniakea/tgapi" ) const ( - ButtonStyleDanger KeyboardButtonStyle = "danger" - ButtonStyleSuccess KeyboardButtonStyle = "success" - ButtonStylePrimary KeyboardButtonStyle = "primary" + ButtonStyleDanger tgapi.KeyboardButtonStyle = "danger" + ButtonStyleSuccess tgapi.KeyboardButtonStyle = "success" + ButtonStylePrimary tgapi.KeyboardButtonStyle = "primary" ) type InlineKbButtonBuilder struct { text string iconCustomEmojiID string - style KeyboardButtonStyle + style tgapi.KeyboardButtonStyle url string callbackData string } @@ -28,7 +29,7 @@ func (b InlineKbButtonBuilder) SetIconCustomEmojiId(id string) InlineKbButtonBui b.iconCustomEmojiID = id return b } -func (b InlineKbButtonBuilder) SetStyle(style KeyboardButtonStyle) InlineKbButtonBuilder { +func (b InlineKbButtonBuilder) SetStyle(style tgapi.KeyboardButtonStyle) InlineKbButtonBuilder { b.style = style return b } @@ -41,8 +42,8 @@ func (b InlineKbButtonBuilder) SetCallbackData(cmd string, args ...any) InlineKb return b } -func (b InlineKbButtonBuilder) build() InlineKeyboardButton { - return InlineKeyboardButton{ +func (b InlineKbButtonBuilder) build() tgapi.InlineKeyboardButton { + return tgapi.InlineKeyboardButton{ Text: b.text, URL: b.url, Style: b.style, @@ -52,20 +53,20 @@ func (b InlineKbButtonBuilder) build() InlineKeyboardButton { } type InlineKeyboard struct { - CurrentLine extypes.Slice[InlineKeyboardButton] - Lines [][]InlineKeyboardButton + CurrentLine extypes.Slice[tgapi.InlineKeyboardButton] + Lines [][]tgapi.InlineKeyboardButton maxRow int } func NewInlineKeyboard(maxRow int) *InlineKeyboard { return &InlineKeyboard{ - CurrentLine: make(extypes.Slice[InlineKeyboardButton], 0), - Lines: make([][]InlineKeyboardButton, 0), + CurrentLine: make(extypes.Slice[tgapi.InlineKeyboardButton], 0), + Lines: make([][]tgapi.InlineKeyboardButton, 0), maxRow: maxRow, } } -func (in *InlineKeyboard) append(button InlineKeyboardButton) *InlineKeyboard { +func (in *InlineKeyboard) append(button tgapi.InlineKeyboardButton) *InlineKeyboard { if in.CurrentLine.Len() == in.maxRow { in.AddLine() } @@ -74,18 +75,18 @@ func (in *InlineKeyboard) append(button InlineKeyboardButton) *InlineKeyboard { } func (in *InlineKeyboard) AddUrlButton(text, url string) *InlineKeyboard { - return in.append(InlineKeyboardButton{Text: text, URL: url}) + return in.append(tgapi.InlineKeyboardButton{Text: text, URL: url}) } -func (in *InlineKeyboard) AddUrlButtonStyle(text string, style KeyboardButtonStyle, url string) *InlineKeyboard { - return in.append(InlineKeyboardButton{Text: text, Style: style, URL: url}) +func (in *InlineKeyboard) AddUrlButtonStyle(text string, style tgapi.KeyboardButtonStyle, url string) *InlineKeyboard { + return in.append(tgapi.InlineKeyboardButton{Text: text, Style: style, URL: url}) } func (in *InlineKeyboard) AddCallbackButton(text string, cmd string, args ...any) *InlineKeyboard { - return in.append(InlineKeyboardButton{ + return in.append(tgapi.InlineKeyboardButton{ Text: text, CallbackData: NewCallbackData(cmd, args...).ToJson(), }) } -func (in *InlineKeyboard) AddCallbackButtonStyle(text string, style KeyboardButtonStyle, cmd string, args ...any) *InlineKeyboard { - return in.append(InlineKeyboardButton{ +func (in *InlineKeyboard) AddCallbackButtonStyle(text string, style tgapi.KeyboardButtonStyle, cmd string, args ...any) *InlineKeyboard { + return in.append(tgapi.InlineKeyboardButton{ Text: text, Style: style, CallbackData: NewCallbackData(cmd, args...).ToJson(), }) @@ -99,14 +100,14 @@ func (in *InlineKeyboard) AddLine() *InlineKeyboard { return in } in.Lines = append(in.Lines, in.CurrentLine) - in.CurrentLine = make(extypes.Slice[InlineKeyboardButton], 0) + in.CurrentLine = make(extypes.Slice[tgapi.InlineKeyboardButton], 0) return in } -func (in *InlineKeyboard) Get() *InlineKeyboardMarkup { +func (in *InlineKeyboard) Get() *tgapi.ReplyMarkup { if in.CurrentLine.Len() > 0 { in.Lines = append(in.Lines, in.CurrentLine) } - return &InlineKeyboardMarkup{InlineKeyboard: in.Lines} + return &tgapi.ReplyMarkup{InlineKeyboard: in.Lines} } type CallbackData struct { diff --git a/methods.go b/methods.go index 48a35ef..f2b240b 100644 --- a/methods.go +++ b/methods.go @@ -2,359 +2,51 @@ package laniakea import ( "encoding/json" + "fmt" + "io" + "net/http" + + "git.nix13.pw/scuroneko/laniakea/tgapi" ) -type EmptyParams struct{} - -var NoParams = EmptyParams{} - -type UpdateParams struct { - Offset int `json:"offset"` - Timeout int `json:"timeout"` - AllowedUpdates []string `json:"allowed_updates"` -} - -func (b *Bot) Updates() ([]*Update, error) { - params := UpdateParams{ - Offset: b.updateOffset, +func (b *Bot) Updates() ([]*tgapi.Update, error) { + offset := b.GetUpdateOffset() + params := tgapi.UpdateParams{ + Offset: offset, Timeout: 30, - AllowedUpdates: b.updateTypes, + AllowedUpdates: b.GetUpdateTypes(), } - req := NewRequest[[]*Update]("getUpdates", params) + req := tgapi.NewRequest[[]*tgapi.Update]("getUpdates", params) res, err := req.Do(b.api) if err != nil { return nil, err } for _, u := range *res { - b.updateOffset = u.UpdateID + 1 - err = b.updateQueue.Enqueue(u) + b.SetUpdateOffset(u.UpdateID + 1) + err = b.GetQueue().Enqueue(u) if err != nil { return nil, err } - if b.requestLogger != nil { + if b.RequestLogger != nil { j, err := json.Marshal(u) if err != nil { - b.logger.Error(err) + b.Logger().Error(err) } - b.requestLogger.Debugf("UPDATE %s\n", j) + b.RequestLogger.Debugf("UPDATE %s\n", j) } } return *res, err } -func (api *Api) GetMe() (*User, error) { - req := NewRequest[User, EmptyParams]("getMe", NoParams) - return req.Do(api) -} -func (api *Api) LogOut() (bool, error) { - req := NewRequest[bool, EmptyParams]("logOut", NoParams) - res, err := req.Do(api) +func (b *Bot) GetFileByLink(link string) ([]byte, error) { + u := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", b.token, link) + res, err := http.Get(u) if err != nil { - return false, err + return nil, err } - return *res, nil -} -func (api *Api) Close() (bool, error) { - req := NewRequest[bool, EmptyParams]("close", NoParams) - res, err := req.Do(api) - if err != nil { - return false, err - } - return *res, nil -} - -type SendMessageP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessageTopicID int `json:"direct_message_topic_id,omitempty"` - - Text string `json:"text"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - Entities []*MessageEntity `json:"entities,omitempty"` - LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"` - DisableNotifications bool `json:"disable_notifications,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` - MessageEffectID string `json:"message_effect_id,omitempty"` - - SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` - ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -func (api *Api) SendMessage(params *SendMessageP) (*Message, error) { - req := NewRequest[Message, SendMessageP]("sendMessage", *params) - return req.Do(api) -} - -type ForwardMessageP struct { - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessageTopicID int `json:"direct_message_topic_id,omitempty"` - - MessageID int `json:"message_id,omitempty"` - FromChatID int `json:"from_chat_id,omitempty"` - VideoStartTimestamp int `json:"video_start_timestamp,omitempty"` - DisableNotification bool `json:"disable_notification,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - - MessageEffectID string `json:"message_effect_id,omitempty"` - SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` -} - -func (api *Api) ForwardMessage(params ForwardMessageP) (*Message, error) { - req := NewRequest[Message]("forwardMessage", params) - return req.Do(api) -} - -type ForwardMessagesP struct { - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessageTopicID int `json:"direct_message_topic_id,omitempty"` - - FromChatID int `json:"from_chat_id,omitempty"` - MessageIDs []int `json:"message_ids,omitempty"` - DisableNotification bool `json:"disable_notification,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` -} - -func (api *Api) ForwardMessages(params ForwardMessagesP) ([]int, error) { - req := NewRequest[[]int]("forwardMessages", params) - res, err := req.Do(api) - if err != nil { - return []int{}, err - } - return *res, nil -} - -type CopyMessageP struct { - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessageTopicID int `json:"direct_message_topic_id,omitempty"` - - FromChatID int `json:"from_chat_id"` - MessageID int `json:"message_id"` - VideoStartTimestamp int `json:"video_start_timestamp,omitempty"` - Caption string `json:"caption,omitempty"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - - CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` - ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` - DisableNotification bool `json:"disable_notification,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` - MessageEffectID string `json:"message_effect_id,omitempty"` - - SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` - ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -func (api *Api) CopyMessage(params CopyMessageP) (int, error) { - req := NewRequest[int]("copyMessage", params) - res, err := req.Do(api) - if err != nil { - return 0, err - } - return *res, nil -} - -type CopyMessagesP struct { - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessageTopicID int `json:"direct_message_topic_id,omitempty"` - - FromChatID int `json:"from_chat_id,omitempty"` - MessageIDs []int `json:"message_ids,omitempty"` - DisableNotification bool `json:"disable_notification,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - RemoveCaption bool `json:"remove_caption,omitempty"` -} - -func (api *Api) CopyMessages(params CopyMessagesP) ([]int, error) { - req := NewRequest[[]int]("copyMessages", params) - res, err := req.Do(api) - if err != nil { - return []int{}, err - } - return *res, nil -} - -type SendPhotoBaseP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - Caption string `json:"caption,omitempty"` - CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` - ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` - HasSpoiler bool `json:"has_spoiler,omitempty"` - DisableNotifications bool `json:"disable_notifications,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` - MessageEffectID string `json:"message_effect_id,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} -type SendPhotoP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` - - Photo string `json:"photo"` - Caption string `json:"caption,omitempty"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` - - ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` - HasSpoiler bool `json:"has_spoiler,omitempty"` - DisableNotifications bool `json:"disable_notifications,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` - MessageEffectID string `json:"message_effect_id,omitempty"` - - SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` - ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -func (api *Api) SendPhoto(params *SendPhotoP) (*Message, error) { - req := NewRequest[Message]("sendPhoto", params) - return req.Do(api) -} - -type EditMessageTextP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id,omitempty"` - MessageID int `json:"message_id,omitempty"` - InlineMessageID string `json:"inline_message_id,omitempty"` - Text string `json:"text"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -func (api *Api) EditMessageText(params *EditMessageTextP) (*Message, error) { - req := NewRequest[Message]("editMessageText", params) - return req.Do(api) -} - -type EditMessageCaptionP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id,omitempty"` - MessageID int `json:"message_id,omitempty"` - InlineMessageID string `json:"inline_message_id,omitempty"` - Caption string `json:"caption"` - ParseMode ParseMode `json:"parse_mode,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -func (api *Api) EditMessageCaption(params *EditMessageCaptionP) (*Message, error) { - req := NewRequest[Message]("editMessageCaption", params) - return req.Do(api) -} - -type DeleteMessageP struct { - ChatID int `json:"chat_id"` - MessageID int `json:"message_id"` -} - -func (api *Api) DeleteMessage(params *DeleteMessageP) (bool, error) { - req := NewRequest[bool]("deleteMessage", params) - ok, err := req.Do(api) - if err != nil { - return false, err - } - return *ok, err -} - -type AnswerCallbackQueryP struct { - CallbackQueryID string `json:"callback_query_id"` - Text string `json:"text,omitempty"` - ShowAlert bool `json:"show_alert,omitempty"` - URL string `json:"url,omitempty"` - CacheTime int `json:"cache_time,omitempty"` -} - -func (api *Api) AnswerCallbackQuery(params *AnswerCallbackQueryP) (bool, error) { - req := NewRequest[bool]("answerCallbackQuery", params) - ok, err := req.Do(api) - if err != nil { - return false, err - } - return *ok, err -} - -type GetFileP struct { - FileId string `json:"file_id"` -} - -func (api *Api) GetFile(params *GetFileP) (*File, error) { - req := NewRequest[File]("getFile", params) - return req.Do(api) -} - -type SendChatActionP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - Action ChatActions `json:"action"` -} - -func (api *Api) SendChatAction(params SendChatActionP) (bool, error) { - req := NewRequest[bool]("sendChatAction", params) - res, err := req.Do(api) - if err != nil { - return false, 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 (api *Api) SetMessageReactionEmoji(params SetMessageReactionEmojiP) (bool, error) { - req := NewRequest[bool]("setMessageReaction", params) - res, err := req.Do(api) - if err != nil { - return false, err - } - return *res, err -} - -type SetMessageReactionCustomEmojiP struct { - SetMessageReactionP - Reaction []ReactionTypeCustomEmoji `json:"reaction"` -} - -func (api *Api) SetMessageReactionCustom(params SetMessageReactionCustomEmojiP) (bool, error) { - req := NewRequest[bool]("setMessageReaction", params) - res, err := req.Do(api) - if err != nil { - return false, err - } - return *res, err -} - -type SetMessageReactionPaidP struct { - SetMessageReactionP -} - -func (api *Api) SetMessageReactionPaid(params SetMessageReactionPaidP) (bool, error) { - req := NewRequest[bool]("setMessageReaction", params) - res, err := req.Do(api) - if err != nil { - return false, err - } - return *res, err + defer res.Body.Close() + return io.ReadAll(res.Body) } diff --git a/msg_context.go b/msg_context.go index fa9f2c0..922ba4f 100644 --- a/msg_context.go +++ b/msg_context.go @@ -1,14 +1,19 @@ package laniakea -import "fmt" +import ( + "fmt" + + "git.nix13.pw/scuroneko/laniakea/tgapi" + "git.nix13.pw/scuroneko/laniakea/utils" +) type MsgContext struct { Bot *Bot - Api *Api + Api *tgapi.Api - Msg *Message - Update *Update - From *User + Msg *tgapi.Message + Update *tgapi.Update + From *tgapi.User CallbackMsgId int CallbackQueryId string FromID int @@ -26,18 +31,18 @@ type AnswerMessage struct { } func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard) *AnswerMessage { - params := &EditMessageTextP{ + params := &tgapi.EditMessageTextP{ MessageID: messageId, ChatID: ctx.Msg.Chat.ID, Text: text, - ParseMode: ParseMD, + ParseMode: tgapi.ParseMD, } if keyboard != nil { params.ReplyMarkup = keyboard.Get() } msg, err := ctx.Api.EditMessageText(params) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) return nil } return &AnswerMessage{ @@ -49,7 +54,7 @@ func (m *AnswerMessage) Edit(text string) *AnswerMessage { } func (ctx *MsgContext) EditCallback(text string, keyboard *InlineKeyboard) *AnswerMessage { if ctx.CallbackMsgId == 0 { - ctx.Api.logger.Errorln("Can't edit non-callback update message") + ctx.Api.Logger.Errorln("Can't edit non-callback update message") return nil } @@ -60,18 +65,18 @@ func (ctx *MsgContext) EditCallbackf(format string, keyboard *InlineKeyboard, ar } func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeyboard) *AnswerMessage { - params := &EditMessageCaptionP{ + params := &tgapi.EditMessageCaptionP{ ChatID: ctx.Msg.Chat.ID, MessageID: messageId, Caption: text, - ParseMode: ParseMD, + ParseMode: tgapi.ParseMD, } if kb != nil { params.ReplyMarkup = kb.Get() } msg, err := ctx.Api.EditMessageCaption(params) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) } return &AnswerMessage{ MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: true, @@ -79,7 +84,7 @@ func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeybo } func (m *AnswerMessage) EditCaption(text string) *AnswerMessage { if m.MessageID == 0 { - m.ctx.Api.logger.Errorln("Can't edit caption message, message id is zero") + m.ctx.Api.Logger.Errorln("Can't edit caption message, message id is zero") return m } return m.ctx.editPhotoText(m.MessageID, text, nil) @@ -89,10 +94,10 @@ func (m *AnswerMessage) EditCaptionKeyboard(text string, kb *InlineKeyboard) *An } func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMessage { - params := &SendMessageP{ + params := &tgapi.SendMessageP{ ChatID: ctx.Msg.Chat.ID, Text: text, - ParseMode: ParseMD, + ParseMode: tgapi.ParseMD, } if keyboard != nil { params.ReplyMarkup = keyboard.Get() @@ -100,7 +105,7 @@ func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMess msg, err := ctx.Api.SendMessage(params) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) return nil } return &AnswerMessage{ @@ -118,10 +123,10 @@ func (ctx *MsgContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage } func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard) *AnswerMessage { - params := &SendPhotoP{ + params := &tgapi.SendPhotoP{ ChatID: ctx.Msg.Chat.ID, Caption: text, - ParseMode: ParseMD, + ParseMode: tgapi.ParseMD, Photo: photoId, } if kb != nil { @@ -129,7 +134,7 @@ func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard) *An } msg, err := ctx.Api.SendPhoto(params) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) return &AnswerMessage{ ctx: ctx, Text: text, IsMedia: true, } @@ -146,12 +151,12 @@ func (ctx *MsgContext) AnswerPhotoKeyboard(photoId, text string, kb *InlineKeybo } func (ctx *MsgContext) delete(messageId int) { - _, err := ctx.Api.DeleteMessage(&DeleteMessageP{ + _, err := ctx.Api.DeleteMessage(&tgapi.DeleteMessageP{ ChatID: ctx.Msg.Chat.ID, MessageID: messageId, }) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) } } func (m *AnswerMessage) Delete() { @@ -165,12 +170,12 @@ func (ctx *MsgContext) answerCallbackQuery(url, text string, showAlert bool) { if len(ctx.CallbackQueryId) == 0 { return } - _, err := ctx.Api.AnswerCallbackQuery(&AnswerCallbackQueryP{ + _, err := ctx.Api.AnswerCallbackQuery(&tgapi.AnswerCallbackQueryP{ CallbackQueryID: ctx.CallbackQueryId, Text: text, ShowAlert: showAlert, URL: url, }) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) } } func (ctx *MsgContext) AnswerCbQuery() { @@ -186,24 +191,24 @@ func (ctx *MsgContext) AnswerCbQueryUrl(u string) { ctx.answerCallbackQuery(u, "", false) } -func (ctx *MsgContext) SendAction(action ChatActions) { - _, err := ctx.Api.SendChatAction(SendChatActionP{ +func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) { + _, err := ctx.Api.SendChatAction(tgapi.SendChatActionP{ ChatID: ctx.Msg.Chat.ID, Action: action, }) if err != nil { - ctx.Api.logger.Errorln(err) + ctx.Api.Logger.Errorln(err) } } func (ctx *MsgContext) error(err error) { - text := fmt.Sprintf(ctx.Bot.errorTemplate, EscapeMarkdown(err.Error())) + text := fmt.Sprintf(ctx.Bot.errorTemplate, utils.EscapeMarkdown(err.Error())) if ctx.CallbackQueryId != "" { ctx.answerCallbackQuery("", text, false) } else { ctx.answer(text, nil) } - ctx.Bot.logger.Errorln(err) + ctx.Bot.Logger().Errorln(err) } func (ctx *MsgContext) Error(err error) { ctx.error(err) diff --git a/tgapi/api.go b/tgapi/api.go new file mode 100644 index 0000000..bb3ba9f --- /dev/null +++ b/tgapi/api.go @@ -0,0 +1,105 @@ +package tgapi + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + "time" + + "git.nix13.pw/scuroneko/laniakea/utils" + "git.nix13.pw/scuroneko/slog" +) + +type Api struct { + token string + client *http.Client + Logger *slog.Logger +} + +func NewAPI(token string) *Api { + l := slog.CreateLogger().Level(utils.GetLoggerLevel()).Prefix("API") + l.AddWriter(l.CreateJsonStdoutWriter()) + client := &http.Client{Timeout: time.Second * 10} + return &Api{token, client, l} +} +func (api *Api) CloseApi() error { + return api.Logger.Close() +} + +type ApiResponse[R any] struct { + Ok bool `json:"ok"` + Description string `json:"description,omitempty"` + Result R `json:"result,omitempty"` + ErrorCode int `json:"error_code,omitempty"` +} + +type TelegramRequest[R, P any] struct { + method string + params P +} + +func NewRequest[R, P any](method string, params P) TelegramRequest[R, P] { + return TelegramRequest[R, P]{method: method, params: params} +} +func (r TelegramRequest[R, P]) DoWithContext(ctx context.Context, api *Api) (R, error) { + var zero R + data, err := json.Marshal(r.params) + if err != nil { + return zero, err + } + buf := bytes.NewBuffer(data) + + u := fmt.Sprintf("https://api.telegram.org/bot%s/%s", api.token, r.method) + if api.Logger != nil { + api.Logger.Debugln(strings.ReplaceAll(fmt.Sprintf( + "POST %s %s", u, buf.String(), + ), api.token, "")) + } + + req, err := http.NewRequestWithContext(ctx, "POST", u, buf) + if err != nil { + return zero, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString)) + + res, err := api.client.Do(req) + if err != nil { + return zero, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return zero, fmt.Errorf("unexpected status code: %d", res.StatusCode) + } + + reader := io.LimitReader(res.Body, 10<<20) + data, err = io.ReadAll(reader) + if err != nil { + return zero, err + } + + if api.Logger != nil { + api.Logger.Debugln(fmt.Sprintf("RES %s %s", r.method, string(data))) + } + + var resp ApiResponse[R] + err = json.Unmarshal(data, &resp) + if err != nil { + return zero, err + } + + if !resp.Ok { + return zero, fmt.Errorf("[%d] %s", resp.ErrorCode, resp.Description) + } + return resp.Result, nil + +} +func (r TelegramRequest[R, P]) Do(api *Api) (R, error) { + ctx := context.Background() + return r.DoWithContext(ctx, api) +} diff --git a/tgapi/attachments_methods.go b/tgapi/attachments_methods.go new file mode 100644 index 0000000..4dc7c3d --- /dev/null +++ b/tgapi/attachments_methods.go @@ -0,0 +1,262 @@ +package tgapi + +type SendPhotoP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Photo string `json:"photo"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` + DisableNotifications bool `json:"disable_notifications,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendPhoto(params SendPhotoP) (Message, error) { + req := NewRequest[Message]("sendPhoto", params) + return req.Do(api) +} + +type SendAudioP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Audio string `json:"audio"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + Duration int `json:"duration,omitempty"` + Performer string `json:"performer,omitempty"` + Title string `json:"title,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendAudio(params SendAudioP) (Message, error) { + req := NewRequest[Message]("sendAudio", params) + return req.Do(api) +} + +type SendDocumentP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Document string `json:"document"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendDocument(params SendDocumentP) (Message, error) { + req := NewRequest[Message]("sendDocument", params) + return req.Do(api) +} + +type SendVideoP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Video string `json:"video"` + Duration int `json:"duration,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Cover string `json:"cover,omitempty"` + + StartTimestamp int `json:"start_timestamp,omitempty"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` + SupportsStreaming bool `json:"supports_streaming,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendVideo(params SendVideoP) (Message, error) { + req := NewRequest[Message]("sendVideo", params) + return req.Do(api) +} + +type SendAnimationP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Animation string `json:"animation"` + Duration int `json:"duration,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendAnimation(p SendAnimationP) (Message, error) { + req := NewRequest[Message]("sendAnimation", p) + return req.Do(api) +} + +type SendVoiceP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Voice string `json:"voice"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + Duration int `json:"duration,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendVoice(params *SendVoiceP) (Message, error) { + req := NewRequest[Message]("sendVoice", params) + return req.Do(api) +} + +type SendVideoNoteP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + VideoNote string `json:"video_note"` + Duration int `json:"duration,omitempty"` + Length int `json:"length,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendVideoNote(params SendVideoNoteP) (Message, error) { + req := NewRequest[Message]("sendVideoNote", params) + return req.Do(api) +} + +type SendPaidMediaP[T InputPaidMedia] struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + StarCount int `json:"star_count,omitempty"` + + Media []T `json:"media"` + Payload string `json:"payload,omitempty"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendPaidMediaPhoto(params SendPaidMediaP[InputPaidMediaPhoto]) (Message, error) { + req := NewRequest[Message]("sendPaidMedia", params) + return req.Do(api) +} +func (api *Api) SendPaidMediaVideo(params SendPaidMediaP[InputPaidMediaVideo]) (Message, error) { + req := NewRequest[Message]("sendPaidMedia", params) + return req.Do(api) +} + +type SendMediaGroupP[T InputMedia] struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Media []T `json:"media"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` +} + +func (api *Api) SendMediaGroupAudio(p SendMediaGroupP[InputMediaAudio]) (Message, error) { + req := NewRequest[Message]("sendMediaGroupAudio", p) + return req.Do(api) +} +func (api *Api) SendMediaGroupDocument(p SendMediaGroupP[InputMediaDocument]) (Message, error) { + req := NewRequest[Message]("sendMediaGroupDocument", p) + return req.Do(api) +} +func (api *Api) SendMediaGroupPhoto(p SendMediaGroupP[InputMediaPhoto]) (Message, error) { + req := NewRequest[Message]("sendMediaGroupPhoto", p) + return req.Do(api) +} +func (api *Api) SendMediaGroupVideo(p SendMediaGroupP[InputMediaVideo]) (Message, error) { + req := NewRequest[Message]("sendMediaGroupVideo", p) + return req.Do(api) +} diff --git a/tgapi/attachments_types.go b/tgapi/attachments_types.go new file mode 100644 index 0000000..2fa8072 --- /dev/null +++ b/tgapi/attachments_types.go @@ -0,0 +1,109 @@ +package tgapi + +type InputMedia interface { + InputMediaPhoto | InputMediaVideo | InputMediaAudio | InputMediaDocument +} + +type InputMediaType string + +const ( + InputMediaTypeAnimation InputMediaType = "animation" + InputMediaTypeDocument InputMediaType = "document" + InputMediaTypePhoto InputMediaType = "photo" + InputMediaTypeVideo InputMediaType = "video" + InputMediaTypeAudio InputMediaType = "audio" +) + +type InputMediaPhoto struct { + Type InputMediaType `json:"type"` + Media string `json:"media"` + + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` +} +type InputMediaVideo struct { + Type InputMediaType `json:"type"` + Media string `json:"media"` + + Cover string `json:"cover"` + StartTimestamp int `json:"start_timestamp"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Duration int `json:"duration,omitempty"` + SupportsStreaming bool `json:"supports_streaming,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` +} +type InputMediaAnimation struct { + Type InputMediaType `json:"type"` + Media string `json:"media"` + + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Duration int `json:"duration,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` +} +type InputMediaAudio struct { + Type InputMediaType `json:"type"` + Media string `json:"media"` + + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + Duration int `json:"duration,omitempty"` + Performer string `json:"performer,omitempty"` + Title string `json:"title,omitempty"` +} +type InputMediaDocument struct { + Type InputMediaType `json:"type"` + Media string `json:"media"` + + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` +} + +type InputPaidMediaType string + +const ( + InputPaidMediaTypeVideo InputPaidMediaType = "video" + InputPaidMediaTypePhoto InputPaidMediaType = "photo" +) + +type InputPaidMedia interface { + InputPaidMediaVideo | InputPaidMediaPhoto +} +type InputPaidMediaPhoto struct { + Type InputPaidMediaType `json:"type"` + Media string `json:"media"` +} +type InputPaidMediaVideo struct { + Type InputPaidMediaType `json:"type"` + Media string `json:"media"` + + Cover string `json:"cover"` + StartTimestamp int64 `json:"start_timestamp"` + Width int `json:"width"` + Height int `json:"height"` + Duration int `json:"duration"` + SupportsStreaming bool `json:"supports_streaming"` +} + +type PhotoSize struct { + FileID string `json:"file_id"` + FileUniqueID string `json:"file_unique_id"` + Width int `json:"width"` + Height int `json:"height"` + FileSize int `json:"file_size,omitempty"` +} diff --git a/tgapi/bot_methods.go b/tgapi/bot_methods.go new file mode 100644 index 0000000..cdc887b --- /dev/null +++ b/tgapi/bot_methods.go @@ -0,0 +1,186 @@ +package tgapi + +type SetMyCommandsP struct { + Commands []BotCommand `json:"commands"` + Scope *BaseBotCommandScope `json:"scope,omitempty"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) SetMyCommands(p SetMyCommandsP) (bool, error) { + req := NewRequest[bool]("setMyCommands", p) + return req.Do(api) +} + +type DeleteMyCommandsP struct { + Scope *BaseBotCommandScope `json:"scope,omitempty"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) DeleteMyCommands(p DeleteMyCommandsP) (bool, error) { + req := NewRequest[bool]("deleteMyCommands", p) + return req.Do(api) +} + +type GetMyCommands struct { + Scope *BaseBotCommandScope `json:"scope,omitempty"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) GetMyCommands(p GetMyCommands) ([]BotCommand, error) { + req := NewRequest[[]BotCommand]("getMyCommands", p) + return req.Do(api) +} + +type SetMyName struct { + Name string `json:"name"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) SetMyName(p SetMyName) (bool, error) { + req := NewRequest[bool]("setMyName", p) + return req.Do(api) +} + +type GetMyName struct { + Language string `json:"language_code,omitempty"` +} + +func (api *Api) GetMyName(p GetMyName) (BotName, error) { + req := NewRequest[BotName]("getMyName", p) + return req.Do(api) +} + +type SetMyDescription struct { + Description string `json:"description"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) SetMyDescription(p SetMyDescription) (bool, error) { + req := NewRequest[bool]("setMyDescription", p) + return req.Do(api) +} + +type GetMyDescription struct { + Language string `json:"language_code,omitempty"` +} + +func (api *Api) GetMyDescription(p GetMyDescription) (BotDescription, error) { + req := NewRequest[BotDescription]("getMyDescription", p) + return req.Do(api) +} + +type SetMyShortDescription struct { + ShortDescription string `json:"short_description,omitempty"` + Language string `json:"language_code,omitempty"` +} + +func (api *Api) SetMyShortDescription(p SetMyShortDescription) (bool, error) { + req := NewRequest[bool]("setMyShortDescription", p) + return req.Do(api) +} + +type GetMyShortDescription struct { + Language string `json:"language_code,omitempty"` +} + +func (api *Api) GetMyShortDescription(p GetMyShortDescription) (BotShortDescription, error) { + req := NewRequest[BotShortDescription]("getMyShortDescription", p) + return req.Do(api) +} + +type SetMyProfilePhotoP[T InputProfilePhoto] struct { + Photo T `json:"photo"` +} + +func (api *Api) SetMyProfilePhotoStatic(p SetMyProfilePhotoP[InputProfilePhotoStatic]) (bool, error) { + req := NewRequest[bool]("setMyProfilePhoto", p) + return req.Do(api) +} +func (api *Api) SetMyProfilePhotoAnimated(p SetMyProfilePhotoP[InputProfilePhotoAnimated]) (bool, error) { + req := NewRequest[bool]("setMyProfilePhoto", p) + return req.Do(api) +} +func (api *Api) RemoveMyProfilePhoto() (bool, error) { + req := NewRequest[bool]("removeMyProfilePhoto", NoParams) + return req.Do(api) +} + +type SetChatMenuButtonP[T MenuButton] struct { + ChatID int `json:"chat_id"` + MenuButton T `json:"menu_button"` +} + +func (api *Api) SetChatMenuButtonCommands(p SetChatMenuButtonP[MenuButtonCommands]) (bool, error) { + req := NewRequest[bool]("setChatMenuButton", p) + return req.Do(api) +} +func (api *Api) SetChatMenuButtonWebApp(p SetChatMenuButtonP[MenuButtonWebApp]) (bool, error) { + req := NewRequest[bool]("setChatMenuButton", p) + return req.Do(api) +} +func (api *Api) SetChatMenuButtonDefault(p SetChatMenuButtonP[MenuButtonDefault]) (bool, error) { + req := NewRequest[bool]("setChatMenuButton", p) + return req.Do(api) +} + +type GetChatMenuButtonP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) GetChatMenuButton(p GetChatMenuButtonP) (BaseMenuButton, error) { + req := NewRequest[BaseMenuButton]("getChatMenuButton", p) + return req.Do(api) +} + +type SetMyDefaultAdministratorRightsP struct { + Rights *ChatAdministratorRights `json:"rights"` + ForChannels bool `json:"for_channels"` +} + +func (api *Api) SetMyDefaultAdministratorRights(p SetMyDefaultAdministratorRightsP) (bool, error) { + req := NewRequest[bool]("setMyDefaultAdministratorRights", p) + return req.Do(api) +} + +type GetMyDefaultAdministratorRightsP struct { + ForChannels bool `json:"for_channels"` +} + +func (api *Api) GetMyDefaultAdministratorRights(p GetMyDefaultAdministratorRightsP) (ChatAdministratorRights, error) { + req := NewRequest[ChatAdministratorRights]("getMyDefaultAdministratorRights", p) + return req.Do(api) +} + +func (api *Api) GetAvailableGifts() (Gifts, error) { + req := NewRequest[Gifts]("getAvailableGifts", NoParams) + return req.Do(api) +} + +type SendGiftP struct { + UserID int `json:"user_id,omitempty"` + ChatID int `json:"chat_id,omitempty"` + GiftID string `json:"gift_id"` + PayForUpgrade bool `json:"pay_for_upgrade"` + Text string `json:"text"` + TextParseMode ParseMode `json:"text_parse_mode,omitempty"` + TextEntities []MessageEntity `json:"text_entities,omitempty"` +} + +func (api *Api) SendGift(p SendGiftP) (bool, error) { + req := NewRequest[bool]("sendGift", p) + return req.Do(api) +} + +type GiftPremiumSubscriptionP struct { + UserID int `json:"user_id"` + MonthCount int `json:"month_count"` + StarCount int `json:"star_count"` + Text string `json:"text,omitempty"` + TextParseMode ParseMode `json:"text_parse_mode,omitempty"` + TextEntities []MessageEntity `json:"text_entities,omitempty"` +} + +func (api *Api) GiftPremiumSubscription(p GiftPremiumSubscriptionP) (bool, error) { + req := NewRequest[bool]("giftPremiumSubscription", p) + return req.Do(api) +} diff --git a/tgapi/bot_types.go b/tgapi/bot_types.go new file mode 100644 index 0000000..512bbdf --- /dev/null +++ b/tgapi/bot_types.go @@ -0,0 +1,99 @@ +package tgapi + +type BotCommand struct { + Command string `json:"command"` + Description string `json:"description"` +} +type BotCommandScopeType string + +const ( + BotCommandScopeDefaultType BotCommandScopeType = "default" + BotCommandScopePrivateType BotCommandScopeType = "all_private_chats" + BotCommandScopeGroupType BotCommandScopeType = "all_groups_chats" + BotCommandScopeAllChatAdministratorsType BotCommandScopeType = "all_chat_administrators" + BotCommandScopeChatType BotCommandScopeType = "chat" + BotCommandScopeChatAdministratorsType BotCommandScopeType = "chat_administrators" + BotCommandScopeChatMemberType BotCommandScopeType = "chat_member" +) + +type BaseBotCommandScope struct { + Type BotCommandScopeType `json:"type"` +} +type BotCommandScopeDefault struct { + BaseBotCommandScope +} +type BotCommandScopePrivateChats struct { + BaseBotCommandScope +} +type BotCommandScopeAllGroupChats struct { + BaseBotCommandScope +} +type BotCommandScopeAllChatAdministrators struct { + BaseBotCommandScope +} +type BotCommandScopeChat struct { + BaseBotCommandScope + ChatID int `json:"chat_id"` +} +type BotCommandScopeChatAdministrators struct { + BaseBotCommandScope + ChatID int `json:"chat_id"` +} +type BotCommandScopeChatMember struct { + BaseBotCommandScope + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` +} + +type BotName struct { + Name string `json:"name"` +} +type BotDescription struct { + Description string `json:"description"` +} +type BotShortDescription struct { + ShortDescription string `json:"short_description"` +} + +const ( + InputProfilePhotoStaticType InputProfilePhotoType = "static" + InputProfilePhotoAnimatedType InputProfilePhotoType = "animated" +) + +type InputProfilePhotoType string +type InputProfilePhoto interface { + InputProfilePhotoStatic | InputProfilePhotoAnimated +} +type BaseInputProfilePhoto struct { + Type InputProfilePhotoType `json:"type"` +} +type InputProfilePhotoStatic struct { + BaseInputProfilePhoto + Photo string `json:"photo"` +} +type InputProfilePhotoAnimated struct { + BaseInputProfilePhoto + Animation string `json:"animation"` + MainFrameTimestamp float64 `json:"main_frame_timestamp"` +} + +const ( + MenuButtonCommandsType MenuButtonType = "commands" + MenuButtonWebAppType MenuButtonType = "web_app" + MenuButtonDefaultType MenuButtonType = "default" +) + +type MenuButtonType string +type MenuButton interface { + MenuButtonCommands | MenuButtonDefault | MenuButtonWebApp +} +type BaseMenuButton struct { + Type MenuButtonType `json:"type"` +} +type MenuButtonCommands struct{ BaseMenuButton } +type MenuButtonDefault struct{ BaseMenuButton } +type MenuButtonWebApp struct { + BaseMenuButton + Text string `json:"text"` + WebApp WebAppInfo `json:"web_app"` +} diff --git a/tgapi/business_methods.go b/tgapi/business_methods.go new file mode 100644 index 0000000..2c9ee47 --- /dev/null +++ b/tgapi/business_methods.go @@ -0,0 +1,149 @@ +package tgapi + +type VerifyUserP struct { + UserID int `json:"user_id"` + CustomDescription string `json:"custom_description,omitempty"` +} + +func (api *Api) VerifyUser(p VerifyUserP) (bool, error) { + req := NewRequest[bool]("verifyUser", p) + return req.Do(api) +} + +type VerifyChatP struct { + ChatID int `json:"chat_id"` + CustomDescription string `json:"custom_description,omitempty"` +} + +func (api *Api) VerifyChat(p VerifyChatP) (bool, error) { + req := NewRequest[bool]("verifyChat", p) + return req.Do(api) +} + +type RemoveUserVerificationP struct { + UserID int `json:"user_id"` +} + +func (api *Api) RemoveUserVerification(p RemoveUserVerificationP) (bool, error) { + req := NewRequest[bool]("removeUserVerification", p) + return req.Do(api) +} + +type RemoveChatVerificationP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) RemoveChatVerification(p RemoveChatVerificationP) (bool, error) { + req := NewRequest[bool]("removeChatVerification", p) + return req.Do(api) +} + +type ReadBusinessMessageP struct { + BusinessConnectionID string `json:"business_connection_id"` + ChatID int `json:"chat_id"` + MessageID int `json:"message_id"` +} + +func (api *Api) ReadBusinessMessage(p ReadBusinessMessageP) (bool, error) { + req := NewRequest[bool]("readBusinessMessage", p) + return req.Do(api) +} + +type DeleteBusinessMessageP struct { + BusinessConnectionID string `json:"business_connection_id"` + MessageIDs []int `json:"message_ids"` +} + +func (api *Api) DeleteBusinessMessage(p DeleteBusinessMessageP) (bool, error) { + req := NewRequest[bool]("deleteBusinessMessage", p) + return req.Do(api) +} + +type SetBusinessAccountNameP struct { + BusinessConnectionID string `json:"business_connection_id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name,omitempty"` +} + +func (api *Api) SetBusinessAccountName(p SetBusinessAccountNameP) (bool, error) { + req := NewRequest[bool]("setBusinessAccountName", p) + return req.Do(api) +} + +type SetBusinessAccountUsernameP struct { + BusinessConnectionID string `json:"business_connection_id"` + Username string `json:"username,omitempty"` +} + +func (api *Api) SetBusinessAccountUsername(p SetBusinessAccountUsernameP) (bool, error) { + req := NewRequest[bool]("setBusinessAccountUsername", p) + return req.Do(api) +} + +type SetBusinessAccountBioP struct { + BusinessConnectionID string `json:"business_connection_id"` + Bio string `json:"bio,omitempty"` +} + +func (api *Api) SetBusinessAccountBio(p SetBusinessAccountBioP) (bool, error) { + req := NewRequest[bool]("setBusinessAccountBio", p) + return req.Do(api) +} + +type SetBusinessAccountProfilePhoto[T InputProfilePhoto] struct { + BusinessConnectionID string `json:"business_connection_id"` + Photo T `json:"photo,omitempty"` + IsPublic bool `json:"is_public,omitempty"` +} + +func (api *Api) SetBusinessAccountProfilePhotoStatic(p SetMyProfilePhotoP[InputProfilePhotoStatic]) (bool, error) { + req := NewRequest[bool]("setBusinessAccountProfilePhoto", p) + return req.Do(api) +} +func (api *Api) SetBusinessAccountProfilePhotoAnimated(p SetMyProfilePhotoP[InputProfilePhotoAnimated]) (bool, error) { + req := NewRequest[bool]("setBusinessAccountProfilePhoto", p) + return req.Do(api) +} + +type RemoveBusinessAccountProfilePhotoP struct { + BusinessConnectionID string `json:"business_connection_id"` + IsPublic bool `json:"is_public,omitempty"` +} + +func (api *Api) RemoveBusinessAccountProfilePhoto(p RemoveBusinessAccountProfilePhotoP) (bool, error) { + req := NewRequest[bool]("removeBusinessAccountProfilePhoto", p) + return req.Do(api) +} + +type SetBusinessAccountGiftSettingsP struct { + BusinessConnectionID string `json:"business_connection_id"` + ShowGiftButton bool `json:"show_gift_button"` + AcceptedGiftTypes AcceptedGiftTypes `json:"accepted_gift_types"` +} + +func (api *Api) SetBusinessAccountGiftSettings(p SetBusinessAccountGiftSettingsP) (bool, error) { + req := NewRequest[bool]("setBusinessAccountGiftSettings", p) + return req.Do(api) +} + +type GetBusinessAccountStarBalanceP struct { + BusinessConnectionID string `json:"business_connection_id"` +} + +func (api *Api) GetBusinessAccountStarBalance(p GetBusinessAccountStarBalanceP) (StarAmount, error) { + req := NewRequest[StarAmount]("getBusinessAccountGiftSettings", p) + return req.Do(api) +} + +type TransferBusinessAccountStartP struct { + BusinessConnectionID string `json:"business_connection_id"` + StarCount int `json:"star_count"` +} + +func (api *Api) TransferBusinessAccountStart(p TransferBusinessAccountStartP) (bool, error) { + req := NewRequest[bool]("transferBusinessAccountStart", p) + return req.Do(api) +} + +type GetBusinessAccountGiftsP struct { +} diff --git a/tgapi/business_types.go b/tgapi/business_types.go new file mode 100644 index 0000000..4f952de --- /dev/null +++ b/tgapi/business_types.go @@ -0,0 +1,44 @@ +package tgapi + +type BusinessIntro struct { + Title string `json:"title,omitempty"` + Message string `json:"message,omitempty"` + Sticker *Sticker `json:"sticker,omitempty"` +} +type BusinessLocation struct { + Address string `json:"address"` + Location *Location `json:"location,omitempty"` +} +type BusinessOpeningHoursInterval struct { + OpeningMinute int `json:"opening_minute"` + ClosingMinute int `json:"closing_minute"` +} +type BusinessOpeningHours struct { + TimeZoneName string `json:"time_zone_name"` + OpeningHours []Birthdate `json:"opening_hours"` +} + +type BusinessBotRights struct { + CanReply *bool `json:"can_reply,omitempty"` + CanReadMessages *bool `json:"can_read_messages,omitempty"` + CanDeleteSentMessages *bool `json:"can_delete_sent_messages,omitempty"` + CanDeleteAllMessages *bool `json:"can_delete_all_messages,omitempty"` + CanEditName *bool `json:"can_edit_name,omitempty"` + CanEditBio *bool `json:"can_edit_bio,omitempty"` + CanEditProfilePhoto *bool `json:"can_edit_profile_photo,omitempty"` + CanEditUsername *bool `json:"can_edit_username,omitempty"` + CanChangeGiftSettings *bool `json:"can_change_gift_settings,omitempty"` + CanViewGiftsAndStars *bool `json:"can_view_gifts_and_stars,omitempty"` + CanConvertGiftsToStars *bool `json:"can_convert_gifts_to_stars,omitempty"` + CanTransferAndUpgradeGifts *bool `json:"can_transfer_and_upgrade_gifts,omitempty"` + CanTransferStars *bool `json:"can_transfer_stars,omitempty"` + CanManageStories *bool `json:"can_manage_stories,omitempty"` +} +type BusinessConnection struct { + ID string `json:"id"` + User User `json:"user"` + UserChatID int `json:"user_chat_id"` + Date int `json:"date"` + Rights *BusinessBotRights `json:"rights,omitempty"` + IsEnabled bool `json:"id_enabled"` +} diff --git a/tgapi/chat_methods.go b/tgapi/chat_methods.go new file mode 100644 index 0000000..f6f4df5 --- /dev/null +++ b/tgapi/chat_methods.go @@ -0,0 +1,337 @@ +package tgapi + +type BanChatMemberP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` + UntilDate int64 `json:"until_date,omitempty"` + RevokeMessages bool `json:"revoke_messages,omitempty"` +} + +func (api *Api) BanChatMember(p BanChatMemberP) (bool, error) { + req := NewRequest[bool]("banChatMember", p) + return req.Do(api) +} + +type UnbanChatMemberP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` + OnlyIfBanned bool `json:"only_if_banned"` +} + +func (api *Api) UnbanChatMember(p UnbanChatMemberP) (bool, error) { + req := NewRequest[bool]("unbanChatMember", p) + return req.Do(api) +} + +type RestrictChatMemberP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` + Permissions ChatPermissions `json:"permissions"` + UseIndependentChatPermissions bool `json:"use_independent_chat_permissions,omitempty"` + UntilDate int `json:"until_date,omitempty"` +} + +func (api *Api) RestrictChatMember(p RestrictChatMemberP) (bool, error) { + req := NewRequest[bool]("restrictChatMember", p) + return req.Do(api) +} + +type PromoteChatMember struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` + IsAnonymous bool `json:"is_anonymous,omitempty"` + + CanManageChat bool `json:"can_manage_chat,omitempty"` + CanDeleteMessages bool `json:"can_delete_messages,omitempty"` + CanManageVideoChats bool `json:"can_manage_video_chats,omitempty"` + CanRestrictMembers bool `json:"can_restrict_members,omitempty"` + CanPromoteMembers bool `json:"can_promote_members,omitempty"` + CanChangeInfo bool `json:"can_change_info,omitempty"` + CanInviteUsers bool `json:"can_invite_users,omitempty"` + CanPostStories bool `json:"can_post_stories,omitempty"` + CanEditStories bool `json:"can_edit_stories,omitempty"` + CanDeleteStories bool `json:"can_delete_stories,omitempty"` + CanPostMessages bool `json:"can_post_messages,omitempty"` + CanEditMessages bool `json:"can_edit_messages,omitempty"` + CanPinMessages bool `json:"can_pin_messages,omitempty"` + CanManageTopics bool `json:"can_manage_topics,omitempty"` + CanManageDirectMessages bool `json:"can_manage_direct_messages,omitempty"` +} + +func (api *Api) PromoteChatMember(p PromoteChatMember) (bool, error) { + req := NewRequest[bool]("promoteChatMember", p) + return req.Do(api) +} + +type SetChatAdministratorCustomTitleP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` + CustomTitle string `json:"custom_title"` +} + +func (api *Api) SetChatAdministratorCustomTitle(p SetChatAdministratorCustomTitleP) (bool, error) { + req := NewRequest[bool]("setChatAdministratorCustomTitle", p) + return req.Do(api) +} + +type BanChatSenderChatP struct { + ChatID int `json:"chat_id"` + SenderChatID int `json:"sender_chat_id"` +} + +func (api *Api) BanChatSenderChat(p BanChatSenderChatP) (bool, error) { + req := NewRequest[bool]("banChatSenderChat", p) + return req.Do(api) +} + +type UnbanChatSenderChatP struct { + ChatID int `json:"chat_id"` + SenderChatID int `json:"sender_chat_id"` +} + +func (api *Api) UnbanChatSenderChat(p BanChatSenderChatP) (bool, error) { + req := NewRequest[bool]("unbanChatSenderChat", p) + return req.Do(api) +} + +type SetChatPermissionsP struct { + ChatID int `json:"chat_id"` + Permissions ChatPermissions `json:"permissions"` + UseIndependentChatPermissions bool `json:"use_independent_chat_permissions,omitempty"` +} + +func (api *Api) SetChatPermissions(p SetChatPermissionsP) (bool, error) { + req := NewRequest[bool]("setChatPermissions", p) + return req.Do(api) +} + +type ExportChatInviteLinkP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) ExportChatInviteLink(p ExportChatInviteLinkP) (string, error) { + req := NewRequest[string]("exportChatInviteLink", p) + return req.Do(api) +} + +type CreateChatInviteLinkP struct { + ChatID int `json:"chat_id"` + Name string `json:"name,omitempty"` + ExpireDate int `json:"expire_date,omitempty"` + MemberLimit int `json:"member_limit,omitempty"` + CreatesJoinRequest int `json:"creates_join_request,omitempty"` +} + +func (api *Api) CreateChatInviteLink(p CreateChatInviteLinkP) (ChatInviteLink, error) { + req := NewRequest[ChatInviteLink]("createChatInviteLink", p) + return req.Do(api) +} + +type EditChatInviteLinkP struct { + ChatID int `json:"chat_id"` + InviteLink string `json:"invite_link"` + + Name string `json:"name,omitempty"` + ExpireDate int `json:"expire_date,omitempty"` + MemberLimit int `json:"member_limit,omitempty"` + CreatesJoinRequest int `json:"creates_join_request,omitempty"` +} + +func (api *Api) EditChatInviteLink(p EditChatInviteLinkP) (ChatInviteLink, error) { + req := NewRequest[ChatInviteLink]("editChatInviteLink", p) + return req.Do(api) +} + +type CreateChatSubscriptionInviteLinkP struct { + ChatID int `json:"chat_id"` + Name string `json:"name,omitempty"` + SubscriptionPeriod int `json:"subscription_period,omitempty"` + SubscriptionPrice int `json:"subscription_price,omitempty"` +} + +func (api *Api) CreateChatSubscriptionInviteLink(p CreateChatSubscriptionInviteLinkP) (ChatInviteLink, error) { + req := NewRequest[ChatInviteLink]("createChatSubscriptionInviteLink", p) + return req.Do(api) +} + +type EditChatSubscriptionInviteLinkP struct { + ChatID int `json:"chat_id"` + InviteLink string `json:"invite_link"` + Name string `json:"name,omitempty"` +} + +func (api *Api) EditChatSubscriptionInviteLink(p EditChatSubscriptionInviteLinkP) (ChatInviteLink, error) { + req := NewRequest[ChatInviteLink]("editChatSubscriptionInviteLink", p) + return req.Do(api) +} + +type RevokeChatInviteLinkP struct { + ChatID int `json:"chat_id"` + InviteLink string `json:"invite_link"` +} + +func (api *Api) RevokeChatInviteLink(p RevokeChatInviteLinkP) (ChatInviteLink, error) { + req := NewRequest[ChatInviteLink]("revokeChatInviteLink", p) + return req.Do(api) +} + +type ApproveChatJoinRequestP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` +} + +func (api *Api) ApproveChatJoinRequest(p ApproveChatJoinRequestP) (bool, error) { + req := NewRequest[bool]("approveChatJoinRequest", p) + return req.Do(api) +} + +type DeclineChatJoinRequestP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` +} + +func (api *Api) DeclineChatJoinRequest(p DeclineChatJoinRequestP) (bool, error) { + req := NewRequest[bool]("declineChatJoinRequest", p) + return req.Do(api) +} + +func (api *Api) SetChatPhoto() { + uploader := NewUploader(api) + defer uploader.Close() +} + +type DeleteChatPhotoP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) DeleteChatPhoto(p DeleteChatPhotoP) (bool, error) { + req := NewRequest[bool]("deleteChatPhoto", p) + return req.Do(api) +} + +type SetChatTitleP struct { + ChatID int `json:"chat_id"` + Title string `json:"title"` +} + +func (api *Api) SetChatTitle(p SetChatTitleP) (bool, error) { + req := NewRequest[bool]("setChatTitle", p) + return req.Do(api) +} + +type SetChatDescriptionP struct { + ChatID int `json:"chat_id"` + Description string `json:"description"` +} + +func (api *Api) SetChatDescription(p SetChatDescriptionP) (bool, error) { + req := NewRequest[bool]("setChatDescription", p) + return req.Do(api) +} + +type PinChatMessageP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageID int `json:"message_id"` + DisableNotification bool `json:"disable_notification,omitempty"` +} + +func (api *Api) PinChatMessage(p PinChatMessageP) (bool, error) { + req := NewRequest[bool]("pinChatMessage", p) + return req.Do(api) +} + +type UnpinChatMessageP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageID int `json:"message_id"` +} + +func (api *Api) UnpinChatMessage(p UnpinChatMessageP) (bool, error) { + req := NewRequest[bool]("unpinChatMessage", p) + return req.Do(api) +} + +type UnpinAllChatMessagesP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) UnpinAllChatMessages(p UnpinAllChatMessagesP) (bool, error) { + req := NewRequest[bool]("unpinAllChatMessages", p) + return req.Do(api) +} + +type LeaveChatP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) LeaveChat(p LeaveChatP) (bool, error) { + req := NewRequest[bool]("leaveChatP", p) + return req.Do(api) +} + +type GetChatP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) GetChatP(p GetChatP) (ChatFullInfo, error) { + req := NewRequest[ChatFullInfo]("getChatP", p) + return req.Do(api) +} + +type GetChatAdministratorsP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) GetChatAdministrators(p GetChatAdministratorsP) ([]BaseChatMember, error) { + req := NewRequest[[]BaseChatMember]("getChatAdministrators", p) + return req.Do(api) +} + +type GetChatMembersCountP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) GetChatMemberCount(p GetChatMembersCountP) (int, error) { + req := NewRequest[int]("getChatMemberCount", p) + return req.Do(api) +} + +type GetChatMemberP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` +} + +func (api *Api) GetChatMember(p GetChatMemberP) (BaseChatMember, error) { + req := NewRequest[BaseChatMember]("getChatMember", p) + return req.Do(api) +} + +type SetChatStickerSetP struct { + ChatID int `json:"chat_id"` + StickerSetName string `json:"sticker_set_name"` +} + +func (api *Api) SetChatStickerSet(p SetChatStickerSetP) (bool, error) { + req := NewRequest[bool]("setChatStickerSet", p) + return req.Do(api) +} + +type DeleteChatStickerSetP struct { + ChatID int `json:"chat_id"` +} + +func (api *Api) DeleteChatStickerSet(p DeleteChatStickerSetP) (bool, error) { + req := NewRequest[bool]("deleteChatStickerSet", p) + return req.Do(api) +} + +type GetUserChatBoostsP struct { + ChatID int `json:"chat_id"` + UserID int `json:"user_id"` +} + +func (api *Api) GetUserChatBoosts(p GetUserChatBoostsP) (UserChatBoosts, error) { + req := NewRequest[UserChatBoosts]("getUserChatBoosts", p) + return req.Do(api) +} diff --git a/tgapi/chat_types.go b/tgapi/chat_types.go new file mode 100644 index 0000000..1983699 --- /dev/null +++ b/tgapi/chat_types.go @@ -0,0 +1,242 @@ +package tgapi + +type Chat struct { + ID int `json:"id"` + Type string `json:"type"` + Title *string `json:"title,omitempty"` + Username *string `json:"username,omitempty"` + FirstName *string `json:"first_name,omitempty"` + LastName *string `json:"last_name,omitempty"` + IsForum *bool `json:"is_forum,omitempty"` + IsDirectMessages *bool `json:"is_direct_messages,omitempty"` +} + +type ChatType string + +const ( + ChatTypePrivate ChatType = "private" + ChatTypeGroup ChatType = "group" + ChatTypeSupergroup ChatType = "supergroup" + ChatTypeChannel ChatType = "channel" +) + +type ChatFullInfo struct { + ID int `json:"id"` + Type ChatType `json:"type"` + Title string `json:"title"` + Username string `json:"username"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + IsForum bool `json:"is_forum"` + IsDirectMessages bool `json:"is_direct_messages"` + AccentColorID int `json:"accent_color_id"` + MaxReactionCount int `json:"max_reaction_count"` + Photo *ChatPhoto `json:"photo,omitempty"` + ActiveUsernames []string `json:"active_usernames,omitempty"` + Birthdate *Birthdate `json:"birthdate,omitempty"` + + BusinessIntro *BusinessIntro `json:"business_intro,omitempty"` + BusinessLocation *BusinessLocation `json:"business_location,omitempty"` + BusinessOpeningHours *BusinessOpeningHours `json:"business_opening_hours,omitempty"` + + PersonalChat *Chat `json:"personal_chat,omitempty"` + ParentChat *Chat `json:"parent_chat,omitempty"` + + AvailableReaction []BaseReaction `json:"available_reaction,omitempty"` + + BackgroundCustomEmojiID *string `json:"background_custom_emoji_id,omitempty"` + ProfileAccentColorID *int `json:"profile_accent_color_id,omitempty"` + ProfileBackgroundCustomEmojiID *string `json:"profile_background_custom_emoji_id,omitempty"` + EmojiStatusCustomEmojiID *string `json:"emoji_status_custom_emoji_id,omitempty"` + EmojiStatusExpirationDate *int `json:"emoji_status_expiration_date,omitempty"` + + Bio *string `json:"bio,omitempty"` + HasPrivateForwards *bool `json:"has_private_forwards,omitempty"` + HasRestrictedVoiceAndVideoMessages *bool `json:"has_restricted_voice_and_video_messages,omitempty"` + JoinToSendMessages *bool `json:"join_to_send_messages,omitempty"` + JoinByRequest *bool `json:"join_by_request,omitempty"` + + Description *string `json:"description,omitempty"` + InviteLink *string `json:"invite_link,omitempty"` + PinnedMessage *Message `json:"pinned_message,omitempty"` + Permissions *ChatPermissions `json:"permissions,omitempty"` + AcceptedGiftTypes *AcceptedGiftTypes `json:"accepted_gift_types,omitempty"` + + CanSendPaidMedia *bool `json:"can_send_paid_media,omitempty"` + SlowModeDelay *int `json:"slow_mode_delay,omitempty"` + UnrestrictedBoostCount *int `json:"unrestricted_boost_count,omitempty"` + MessageAutoDeleteTime *int `json:"message_auto_delete_time,omitempty"` + HasAggressiveAntiSpamEnabled *bool `json:"has_aggressive_anti_spam_enabled,omitempty"` + HasHiddenMembers *bool `json:"has_hidden_members,omitempty"` + HasProtectedContent *bool `json:"has_protected_content,omitempty"` + HasVisibleHistory *bool `json:"has_visible_history,omitempty"` + StickerSetName *string `json:"sticker_set_name,omitempty"` + CanSetStickerSet *bool `json:"can_set_sticker_set,omitempty"` + CustomEmojiStickerSetName *string `json:"custom_emoji_sticker_set_name,omitempty"` + LinkedChatID *int `json:"linked_chat_id,omitempty"` + + Location *ChatLocation `json:"location,omitempty"` + Rating *UserRating `json:"rating,omitempty"` + FirstProfileAudio *Audio `json:"first_profile_audio,omitempty"` + UniqueGiftColors *UniqueGiftColors `json:"unique_gift_colors,omitempty"` + PaidMessageStarCount *int `json:"paid_message_star_count,omitempty"` +} + +type ChatPhoto struct { + SmallFileID string `json:"small_file_id"` + SmallFileUniqueID string `json:"small_file_unique_id"` + BigFileID string `json:"big_file_id"` + BigFileUniqueID string `json:"big_file_unique_id"` +} + +type ChatPermissions struct { + CanSendMessages bool `json:"can_send_messages"` + CanSendAudios bool `json:"can_send_audios"` + CanSendDocuments bool `json:"can_send_documents"` + CanSendPhotos bool `json:"can_send_photos"` + CanSendVideoNotes bool `json:"can_send_video_notes"` + CanSendVoiceNotes bool `json:"can_send_voice_notes"` + CanSendPolls bool `json:"can_send_polls"` + CanSendOtherMessages bool `json:"can_send_other_messages"` + CanAddWebPagePreview bool `json:"can_add_web_page_preview"` + CanChangeInfo bool `json:"can_change_info"` + CanInviteUsers bool `json:"can_invite_users"` + CanPinMessages bool `json:"can_pin_messages"` + CanManageTopics bool `json:"can_manage_topics"` +} +type ChatLocation struct { + Location Location `json:"location"` + Address string `json:"address"` +} +type ChatInviteLink struct { + InviteLink string `json:"invite_link"` + Creator User `json:"creator"` + CreateJoinRequest bool `json:"create_join_request"` + IsPrimary bool `json:"is_primary"` + IsRevoked bool `json:"is_revoked"` + + Name *string `json:"name,omitempty"` + ExpireDate *int `json:"expire_date,omitempty"` + MemberLimit *int `json:"member_limit,omitempty"` + PendingJoinRequestCount *int `json:"pending_join_request_count,omitempty"` + SubscriptionPeriod *int `json:"subscription_period,omitempty"` + SubscriptionPrice *int `json:"subscription_price,omitempty"` +} + +type ChatMemberStatusType string + +const ( + ChatMemberStatusOwner ChatMemberStatusType = "owner" + ChatMemberStatusAdministrator ChatMemberStatusType = "administrator" + ChatMemberStatusMember ChatMemberStatusType = "member" + ChatMemberStatusRestricted ChatMemberStatusType = "restricted" + ChatMemberStatusLeft ChatMemberStatusType = "left" + ChatMemberStatusBanned ChatMemberStatusType = "kicked" +) + +type ChatMember interface { + ChatMemberOwner | ChatMemberAdministrator | ChatMemberMember | ChatMemberRestricted | ChatMemberLeft | ChatMemberBanned +} +type BaseChatMember struct { + Status ChatMemberStatusType `json:"status"` + User User `json:"user"` +} +type ChatMemberOwner struct { + BaseChatMember + + IsAnonymous bool `json:"is_anonymous"` + CustomTitle *string `json:"custom_title,omitempty"` +} +type ChatMemberAdministrator struct { + BaseChatMember + CanBeEdited bool `json:"can_be_edited"` + IsAnonymous bool `json:"is_anonymous"` + CanManageChat bool `json:"can_manage_chat"` + CanDeleteMessages bool `json:"can_delete_messages"` + CanManageVideoChats bool `json:"can_manage_video_chats"` + CanRestrictMembers bool `json:"can_restrict_members"` + CanPromoteMembers bool `json:"can_promote_members"` + CanChangeInfo bool `json:"can_change_info"` + CanInviteUsers bool `json:"can_invite_users"` + CanPostStories bool `json:"can_post_stories"` + CanEditStories bool `json:"can_edit_stories"` + CanDeleteStories bool `json:"can_delete_stories"` + + CanPostMessages *bool `json:"can_post_messages,omitempty"` + CanEditMessages *bool `json:"can_edit_messages,omitempty"` + CanPinMessages *bool `json:"can_pin_messages,omitempty"` + CanManageTopics *bool `json:"can_manage_topics,omitempty"` + CanManageDirectMessages *bool `json:"can_manage_direct_messages,omitempty"` + CustomTitle *string `json:"custom_title,omitempty"` +} +type ChatMemberMember struct { + BaseChatMember + UntilDate *int `json:"until_date,omitempty"` +} +type ChatMemberRestricted struct { + BaseChatMember + IsMember bool `json:"is_member"` + CanSendMessages bool `json:"can_send_messages"` + CanSendAudios bool `json:"can_send_audios"` + CanSendDocuments bool `json:"can_send_documents"` + CanSendVideos bool `json:"can_send_videos"` + CanSendVideoNotes bool `json:"can_send_video_notes"` + CanSendVoiceNotes bool `json:"can_send_voice_notes"` + CanSendPolls bool `json:"can_send_polls"` + CanSendOtherMessages bool `json:"can_send_other_messages"` + CanAddWebPagePreview bool `json:"can_add_web_page_preview"` + CanChangeInfo bool `json:"can_change_info"` + CanInviteUsers bool `json:"can_invite_users"` + CanPinMessages bool `json:"can_pin_messages"` + CanManageTopics bool `json:"can_manage_topics"` + UntilDate int `json:"until_date"` +} +type ChatMemberLeft struct { + BaseChatMember +} +type ChatMemberBanned struct { + BaseChatMember + UntilDate int `json:"until_date"` +} + +type BaseChatBoostSource struct { + Source string `json:"source"` + User User `json:"user"` +} +type ChatBoostSourcePremium struct{ BaseChatBoostSource } +type ChatBoostSourceGiftCode struct{ BaseChatBoostSource } +type ChatBoostSourceGiveaway struct { + BaseChatBoostSource + GiveawayMessageID int `json:"giveaway_message_id"` + PrizeStarCount *int `json:"prize_star_count,omitempty"` + IsUnclaimed bool `json:"is_unclaimed"` +} +type ChatBoost[T BaseChatBoostSource] struct { + BoostID int `json:"boost_id"` + AddDate int `json:"add_date"` + ExpirationDate int `json:"expiration_date"` + Source T `json:"source"` +} +type UserChatBoosts struct { + Boosts []ChatBoost[BaseChatBoostSource] `json:"boosts"` +} + +type ChatAdministratorRights struct { + IsAnonymous bool `json:"is_anonymous"` + CanManageChat bool `json:"can_manage_chat"` + CanDeleteMessages bool `json:"can_delete_messages"` + CanManageVideoChats bool `json:"can_manage_video_chats"` + CanRestrictMembers bool `json:"can_restrict_members"` + CanPromoteMembers bool `json:"can_promote_members"` + CanChangeInfo bool `json:"can_change_info"` + CanInviteUsers bool `json:"can_invite_users"` + CanPostStories bool `json:"can_post_stories"` + CanEditStories bool `json:"can_edit_stories"` + CanDeleteStories bool `json:"can_delete_stories"` + + CanPostMessages *bool `json:"can_post_messages,omitempty"` + CanEditMessages *bool `json:"can_edit_messages,omitempty"` + CanPinMessages *bool `json:"can_pin_messages,omitempty"` + CanManageTopics *bool `json:"can_manage_topics,omitempty"` + CanManageDirectMessages *bool `json:"can_manage_direct_messages,omitempty"` +} diff --git a/tgapi/forum_methods.go b/tgapi/forum_methods.go new file mode 100644 index 0000000..48e31e2 --- /dev/null +++ b/tgapi/forum_methods.go @@ -0,0 +1,86 @@ +package tgapi + +type BaseForumTopicP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id"` +} + +func (api *Api) GetForumTopicIconSet() ([]Sticker, error) { + req := NewRequest[[]Sticker]("getForumTopicIconSet", NoParams) + return req.Do(api) +} + +type CreateForumTopicP struct { + ChatID int `json:"chat_id"` + Name string `json:"name"` + IconColor ForumTopicIconColor `json:"icon_color"` + IconCustomEmojiID string `json:"icon_custom_emoji_id"` +} + +func (api *Api) CreateForumTopic(p CreateForumTopicP) (ForumTopic, error) { + req := NewRequest[ForumTopic]("createForumTopic", p) + return req.Do(api) +} + +type EditForumTopicP struct { + BaseForumTopicP + Name string `json:"name"` + IconCustomEmojiID string `json:"icon_custom_emoji_id"` +} + +func (api *Api) EditForumTopic(p EditForumTopicP) (bool, error) { + req := NewRequest[bool]("editForumTopic", p) + return req.Do(api) +} + +func (api *Api) CloseForumTopic(p BaseForumTopicP) (bool, error) { + req := NewRequest[bool]("closeForumTopic", p) + return req.Do(api) +} +func (api *Api) ReopenForumTopic(p BaseForumTopicP) (bool, error) { + req := NewRequest[bool]("reopenForumTopic", p) + return req.Do(api) +} +func (api *Api) DeleteForumTopic(p BaseForumTopicP) (bool, error) { + req := NewRequest[bool]("deleteForumTopic", p) + return req.Do(api) +} +func (api *Api) UnpinAllForumTopicMessages(p BaseForumTopicP) (bool, error) { + req := NewRequest[bool]("unpinAllForumTopicMessages", p) + return req.Do(api) +} + +type BaseGeneralForumTopicP struct { + ChatID int `json:"chat_id"` +} + +type EditGeneralForumTopicP struct { + ChatID int `json:"chat_id"` + Name string `json:"name"` +} + +func (api *Api) EditGeneralForumTopic(p EditGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("editGeneralForumTopic", p) + return req.Do(api) +} + +func (api *Api) CloseGeneralForumTopic(p BaseGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("closeGeneralForumTopic", p) + return req.Do(api) +} +func (api *Api) ReopenGeneralForumTopic(p BaseGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("reopenGeneralForumTopic", p) + return req.Do(api) +} +func (api *Api) HideGeneralForumTopic(p BaseGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("hideGeneralForumTopic", p) + return req.Do(api) +} +func (api *Api) UnhideGeneralForumTopic(p BaseGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("unhideGeneralForumTopic", p) + return req.Do(api) +} +func (api *Api) UnpinAllGeneralForumTopicMessages(p BaseGeneralForumTopicP) (bool, error) { + req := NewRequest[bool]("unpinAllGeneralForumTopicMessages", p) + return req.Do(api) +} diff --git a/tgapi/forum_types.go b/tgapi/forum_types.go new file mode 100644 index 0000000..45a4661 --- /dev/null +++ b/tgapi/forum_types.go @@ -0,0 +1,15 @@ +package tgapi + +type ForumTopic struct { + MessageThreadID int `json:"message_thread_id"` + Name string `json:"name"` + IconColor int `json:"icon_color"` + IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"` + IsNameImplicit bool `json:"is_name_implicit,omitempty"` +} + +type ForumTopicIconColor int + +const ( + ForumTopicIconColorBlue ForumTopicIconColor = 7322096 +) diff --git a/tgapi/messages_methods.go b/tgapi/messages_methods.go new file mode 100644 index 0000000..e76d382 --- /dev/null +++ b/tgapi/messages_methods.go @@ -0,0 +1,367 @@ +package tgapi + +type SendMessageP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Text string `json:"text"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + Entities []*MessageEntity `json:"entities,omitempty"` + LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"` + DisableNotifications bool `json:"disable_notifications,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendMessage(params *SendMessageP) (Message, error) { + req := NewRequest[Message, SendMessageP]("sendMessage", *params) + return req.Do(api) +} + +type ForwardMessageP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + MessageID int `json:"message_id,omitempty"` + FromChatID int `json:"from_chat_id,omitempty"` + VideoStartTimestamp int `json:"video_start_timestamp,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + + MessageEffectID string `json:"message_effect_id,omitempty"` + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` +} + +func (api *Api) ForwardMessage(params ForwardMessageP) (Message, error) { + req := NewRequest[Message]("forwardMessage", params) + return req.Do(api) +} + +type ForwardMessagesP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + FromChatID int `json:"from_chat_id,omitempty"` + MessageIDs []int `json:"message_ids,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` +} + +func (api *Api) ForwardMessages(params ForwardMessagesP) ([]int, error) { + req := NewRequest[[]int]("forwardMessages", params) + return req.Do(api) +} + +type CopyMessageP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + FromChatID int `json:"from_chat_id"` + MessageID int `json:"message_id"` + VideoStartTimestamp int `json:"video_start_timestamp,omitempty"` + Caption string `json:"caption,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) CopyMessage(params CopyMessageP) (int, error) { + req := NewRequest[int]("copyMessage", params) + return req.Do(api) +} + +type CopyMessagesP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + FromChatID int `json:"from_chat_id,omitempty"` + MessageIDs []int `json:"message_ids,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + RemoveCaption bool `json:"remove_caption,omitempty"` +} + +func (api *Api) CopyMessages(params CopyMessagesP) ([]int, error) { + req := NewRequest[[]int]("copyMessages", params) + return req.Do(api) +} + +type SendLocationP struct { + BusinessConnectionID int `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` + LivePeriod int `json:"live_period,omitempty"` + Heading int `json:"heading,omitempty"` + ProximityAlertRadius int `json:"proximity_alert_radius,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendLocation(params SendLocationP) (Message, error) { + req := NewRequest[Message]("sendLocation", params) + return req.Do(api) +} + +type SendVenueP struct { + BusinessConnectionID int `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Title string `json:"title"` + Address string `json:"address"` + FoursquareID string `json:"foursquare_id,omitempty"` + FoursquareType string `json:"foursquare_type,omitempty"` + GooglePlaceID string `json:"google_place_id,omitempty"` + GooglePlaceType string `json:"google_place_type,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendVenue(params SendVenueP) (Message, error) { + req := NewRequest[Message]("sendVenue", params) + return req.Do(api) +} + +type SendContactP struct { + BusinessConnectionID int `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + PhoneNumber string `json:"phone_number"` + FirstName string `json:"first_name"` + LastName string `json:"last_name,omitempty"` + Vcard string `json:"vcard"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendContact(params SendContactP) (Message, error) { + req := NewRequest[Message]("sendContact", params) + return req.Do(api) +} + +type SendPollP struct { + BusinessConnectionID int `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + + Question string `json:"question"` + QuestionParseMode ParseMode `json:"question_mode,omitempty"` + QuestionEntities []*MessageEntity `json:"question_entities,omitempty"` + Options []InputPollOption `json:"options"` + IsAnonymous bool `json:"is_anonymous,omitempty"` + Type PollType `json:"type"` + AllowsMultipleAnswers bool `json:"allows_multiple_answers,omitempty"` + CorrectOptionID int `json:"correct_option_id,omitempty"` + Explanation string `json:"explanation,omitempty"` + ExplanationParseMode ParseMode `json:"explanation_parse_mode,omitempty"` + ExplanationEntities []*MessageEntity `json:"explanation_entities,omitempty"` + OpenPeriod int `json:"open_period,omitempty"` + CloseDate int `json:"close_date"` + IsClosed bool `json:"is_closed,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendPoll(params SendPollP) (Message, error) { + req := NewRequest[Message]("sendPoll", params) + return req.Do(api) +} + +type SendChecklistP struct { + BusinessConnectionID int `json:"business_connection_id"` + ChatID int `json:"chat_id"` + Checklist InputChecklist `json:"checklist"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendChecklist(params SendChecklistP) (Message, error) { + req := NewRequest[Message]("sendChecklist", params) + return req.Do(api) +} + +type SendDiceP struct { + BusinessConnectionID int `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Emoji string `json:"emoji,omitempty"` + + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + + SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"` + ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) SendDice(params SendDiceP) (Message, error) { + req := NewRequest[Message]("sendDice", params) + return req.Do(api) +} + +type SendMessageDraftP struct { + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DraftID int `json:"draft_id"` + Text string `json:"text"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + Entities []*MessageEntity `json:"entities,omitempty"` +} + +func (api *Api) SendMessageDraft(params SendMessageDraftP) (bool, error) { + req := NewRequest[bool]("sendMessageDraft", params) + return req.Do(api) +} + +type SendChatActionP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + Action ChatActionType `json:"action"` +} + +func (api *Api) SendChatAction(params SendChatActionP) (bool, error) { + req := NewRequest[bool]("sendChatAction", params) + return req.Do(api) +} + +type SetMessageReactionP[T ReactionType] struct { + ChatId int `json:"chat_id"` + MessageId int `json:"message_id"` + Reaction []T `json:"reaction"` + IsBig bool `json:"is_big,omitempty"` +} + +func (api *Api) SetMessageReactionEmoji(params SetMessageReactionP[ReactionTypeEmoji]) (bool, error) { + req := NewRequest[bool]("setMessageReaction", params) + return req.Do(api) +} +func (api *Api) SetMessageReactionCustom(params SetMessageReactionP[ReactionTypeCustomEmoji]) (bool, error) { + req := NewRequest[bool]("setMessageReaction", params) + return req.Do(api) +} +func (api *Api) SetMessageReactionPaid(params SetMessageReactionP[ReactionTypePaid]) (bool, error) { + req := NewRequest[bool]("setMessageReaction", params) + return req.Do(api) +} + +// Message update methods + +type EditMessageTextP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id,omitempty"` + MessageID int `json:"message_id,omitempty"` + InlineMessageID string `json:"inline_message_id,omitempty"` + Text string `json:"text"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) EditMessageText(params *EditMessageTextP) (Message, error) { + req := NewRequest[Message]("editMessageText", params) + return req.Do(api) +} + +type EditMessageCaptionP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id,omitempty"` + MessageID int `json:"message_id,omitempty"` + InlineMessageID string `json:"inline_message_id,omitempty"` + Caption string `json:"caption"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"` +} + +func (api *Api) EditMessageCaption(params *EditMessageCaptionP) (Message, error) { + req := NewRequest[Message]("editMessageCaption", params) + return req.Do(api) +} + +type DeleteMessageP struct { + ChatID int `json:"chat_id"` + MessageID int `json:"message_id"` +} + +func (api *Api) DeleteMessage(params *DeleteMessageP) (bool, error) { + req := NewRequest[bool]("deleteMessage", params) + return req.Do(api) +} + +type AnswerCallbackQueryP struct { + CallbackQueryID string `json:"callback_query_id"` + Text string `json:"text,omitempty"` + ShowAlert bool `json:"show_alert,omitempty"` + URL string `json:"url,omitempty"` + CacheTime int `json:"cache_time,omitempty"` +} + +func (api *Api) AnswerCallbackQuery(params *AnswerCallbackQueryP) (bool, error) { + req := NewRequest[bool]("answerCallbackQuery", params) + return req.Do(api) +} diff --git a/types.go b/tgapi/messages_types.go similarity index 58% rename from types.go rename to tgapi/messages_types.go index 781b260..1bb7727 100644 --- a/types.go +++ b/tgapi/messages_types.go @@ -1,49 +1,4 @@ -package laniakea - -import "git.nix13.pw/scuroneko/extypes" - -type Update struct { - UpdateID int `json:"update_id"` - Message *Message `json:"message"` - EditedMessage *Message `json:"edited_message,omitempty"` - ChannelPost *Message `json:"channel_post,omitempty"` - EditedChannelPost *Message `json:"edited_channel_post,omitempty"` - BusinessConnection *BusinessConnection `json:"business_connection,omitempty"` - BusinessMessage *Message `json:"business_message,omitempty"` - EditedBusinessMessage *Message `json:"edited_business_message,omitempty"` - DeletedBusinessMessage *Message `json:"deleted_business_messages,omitempty"` - MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"` - MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"` - CallbackQuery *CallbackQuery `json:"callback_query,omitempty"` - InlineQuery int - ChosenInlineResult int -} - -type User struct { - ID int `json:"id"` - IsBot bool `json:"is_bot"` - FirstName string `json:"first_name"` - LastName string `json:"last_name,omitempty"` - Username string `json:"username,omitempty"` - LanguageCode string `json:"language_code,omitempty"` - IsPremium bool `json:"is_premium,omitempty"` - AddedToAttachmentMenu bool `json:"added_to_attachment_menu,omitempty"` - CanJoinGroups bool `json:"can_join_groups,omitempty"` - CanReadAllGroupMessages bool `json:"can_read_all_group_messages,omitempty"` - SupportsInlineQueries bool `json:"supports_inline_queries,omitempty"` - CanConnectToBusiness bool `json:"can_connect_to_business,omitempty"` - HasMainWebApp bool `json:"has_main_web_app,omitempty"` -} - -type Chat struct { - ID int `json:"id"` - Type string `json:"type"` - Title string `json:"title,omitempty"` - Username string `json:"username,omitempty"` - FirstName string `json:"first_name,omitempty"` - LastName string `json:"last_name,omitempty"` - IsForum bool `json:"is_forum,omitempty"` -} +package tgapi type MessageReplyMarkup struct { InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` @@ -71,9 +26,9 @@ type Message struct { Text string `json:"text"` - Photo extypes.Slice[*PhotoSize] `json:"photo,omitempty"` - Caption string `json:"caption,omitempty"` - CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + Photo []*PhotoSize `json:"photo,omitempty"` + Caption string `json:"caption,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` Date int `json:"date"` EditDate int `json:"edit_date"` @@ -87,14 +42,13 @@ type Message struct { EffectID string `json:"effect_id,omitempty"` } -type InaccessableMessage struct { - Chat *Chat `json:"chat"` - MessageID int `json:"message_id"` - Date int `json:"date"` +type InaccessibleMessage struct { + Chat Chat `json:"chat"` + MessageID int `json:"message_id"` + Date int `json:"date"` } -type MaybeInaccessibleMessage struct { -} +type MaybeInaccessibleMessage interface{ Message | InaccessibleMessage } type MessageEntityType string @@ -122,7 +76,7 @@ const ( type MessageEntity struct { Type MessageEntityType `json:"type"` - + Offset int `json:"offset"` Length int `json:"length"` URL string `json:"url,omitempty"` @@ -143,14 +97,6 @@ type ReplyParameters struct { ChecklistTaskID int `json:"checklist_task_id,omitempty"` } -type PhotoSize struct { - FileID string `json:"file_id"` - FileUniqueID string `json:"file_unique_id"` - Width int `json:"width"` - Height int `json:"height"` - FileSize int `json:"file_size,omitempty"` -} - type LinkPreviewOptions struct { IsDisabled bool `json:"is_disabled,omitempty"` URL string `json:"url,omitempty"` @@ -191,30 +137,61 @@ type ReplyKeyboardMarkup struct { } type CallbackQuery struct { - ID string `json:"id"` - From *User `json:"from"` - Message *Message `json:"message"` + ID string `json:"id"` + From User `json:"from"` + Message Message `json:"message"` Data string `json:"data"` } -type BusinessConnection struct { - ID string `json:"id"` - User *User `json:"user"` - UserChatID int `json:"user_chat_id"` - Date int `json:"date"` - CanReply bool `json:"can_reply"` - IsEnabled bool `json:"id_enabled"` +type InputPollOption struct { + Text string `json:"text"` + TextParseMode ParseMode `json:"text_parse_mode,omitempty"` + TextEntities []*MessageEntity `json:"text_entities,omitempty"` +} +type PollType string + +const ( + PollTypeRegular PollType = "regular" + PollTypeQuiz PollType = "quiz" +) + +type InputChecklistTask struct { + ID int `json:"id"` + Text string `json:"text"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + TextEntities []*MessageEntity `json:"text_entities,omitempty"` +} +type InputChecklist struct { + Title string `json:"title"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + TitleEntities []*MessageEntity `json:"title_entities,omitempty"` + Tasks []InputChecklistTask `json:"tasks"` + OtherCanAddTasks bool `json:"other_can_add_tasks,omitempty"` + OtherCanMarkTasksAsDone bool `json:"other_can_mark_tasks_as_done,omitempty"` } +type ChatActionType string + +const ( + ChatActionTyping ChatActionType = "typing" + ChatActionUploadPhoto ChatActionType = "upload_photo" + ChatActionUploadVideo ChatActionType = "upload_video" + ChatActionUploadVoice ChatActionType = "upload_voice" + ChatActionUploadDocument ChatActionType = "upload_document" + ChatActionChooseSticker ChatActionType = "choose_sticker" + ChatActionFindLocation ChatActionType = "find_location" + ChatActionUploadVideoNone ChatActionType = "upload_video_none" +) + type MessageReactionUpdated struct { - Chat *Chat `json:"chat"` - MessageID int `json:"message_id"` - User *User `json:"user,omitempty"` - ActorChat *Chat `json:"actor_chat"` - Date int `json:"date"` - OldReaction []*ReactionType `json:"old_reaction"` - NewReaction []*ReactionType `json:"new_reaction"` + Chat *Chat `json:"chat"` + MessageID int `json:"message_id"` + User *User `json:"user,omitempty"` + ActorChat *Chat `json:"actor_chat"` + Date int `json:"date"` + OldReaction []BaseReaction `json:"old_reaction"` + NewReaction []BaseReaction `json:"new_reaction"` } type MessageReactionCountUpdated struct { @@ -224,45 +201,28 @@ type MessageReactionCountUpdated struct { Reactions []*ReactionCount `json:"reactions"` } -type ReactionType struct { +type ReactionType interface { + ReactionTypeEmoji | ReactionTypeCustomEmoji | ReactionTypePaid +} +type BaseReaction struct { Type string `json:"type"` } type ReactionTypeEmoji struct { - ReactionType + Type string `json:"type"` Emoji string `json:"emoji"` } type ReactionTypeCustomEmoji struct { - ReactionType + Type string `json:"type"` CustomEmojiID string `json:"custom_emoji_id"` } type ReactionTypePaid struct { - ReactionType + Type string `json:"type"` } type ReactionCount struct { - Type *ReactionType `json:"type"` - TotalCount int `json:"total_count"` + Type BaseReaction `json:"type"` + TotalCount int `json:"total_count"` } -type File struct { - FileId string `json:"file_id"` - FileUniqueID string `json:"file_unique_id"` - FileSize int `json:"file_size,omitempty"` - FilePath string `json:"file_path,omitempty"` -} - -type ChatActions string - -const ( - ChatActionTyping ChatActions = "typing" - ChatActionUploadPhoto ChatActions = "upload_photo" - ChatActionUploadVideo ChatActions = "upload_video" - ChatActionUploadVoice ChatActions = "upload_voice" - ChatActionUploadDocument ChatActions = "upload_document" - ChatActionChooseSticker ChatActions = "choose_sticker" - ChatActionFindLocation ChatActions = "find_location" - ChatActionUploadVideoNone ChatActions = "upload_video_none" -) - type SuggestedPostPrice struct { Currency string `json:"currency"` Amount int `json:"amount"` diff --git a/tgapi/methods.go b/tgapi/methods.go new file mode 100644 index 0000000..b000089 --- /dev/null +++ b/tgapi/methods.go @@ -0,0 +1,41 @@ +package tgapi + +type ParseMode string + +const ( + ParseMDV2 ParseMode = "MarkdownV2" + ParseHTML ParseMode = "HTML" + ParseMD ParseMode = "Markdown" +) + +type EmptyParams struct{} + +var NoParams = EmptyParams{} + +type UpdateParams struct { + Offset int `json:"offset"` + Timeout int `json:"timeout"` + AllowedUpdates []string `json:"allowed_updates"` +} + +func (api *Api) GetMe() (User, error) { + req := NewRequest[User, EmptyParams]("getMe", NoParams) + return req.Do(api) +} +func (api *Api) LogOut() (bool, error) { + req := NewRequest[bool, EmptyParams]("logOut", NoParams) + return req.Do(api) +} +func (api *Api) Close() (bool, error) { + req := NewRequest[bool, EmptyParams]("close", NoParams) + return req.Do(api) +} + +type GetFileP struct { + FileId string `json:"file_id"` +} + +func (api *Api) GetFile(params GetFileP) (File, error) { + req := NewRequest[File]("getFile", params) + return req.Do(api) +} diff --git a/tgapi/stickers_methods.go b/tgapi/stickers_methods.go new file mode 100644 index 0000000..f86977d --- /dev/null +++ b/tgapi/stickers_methods.go @@ -0,0 +1,166 @@ +package tgapi + +type SendStickerP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` + + Sticker string `json:"sticker"` + Emoji string `json:"emoji,omitempty"` + DisableNotification bool `json:"disable_notification,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` +} + +func (api *Api) SendSticker(p SendStickerP) (Message, error) { + req := NewRequest[Message]("sendSticker", p) + return req.Do(api) +} + +type GetStickerSetP struct { + Name string `json:"name"` +} + +func (api *Api) GetStickerSet(p GetStickerSetP) (StickerSet, error) { + req := NewRequest[StickerSet]("getStickerSet", p) + return req.Do(api) +} + +type GetCustomEmojiStickersP struct { + CustomEmojiIDs []string `json:"custom_emoji_ids"` +} + +func (api *Api) GetCustomEmojiStickers(p GetCustomEmojiStickersP) ([]Sticker, error) { + req := NewRequest[[]Sticker]("getCustomEmojiStickers", p) + return req.Do(api) +} + +type CreateNewStickerSetP struct { + UserID int `json:"user_id"` + Name string `json:"name"` + Title string `json:"title"` + + Stickers []InputSticker `json:"stickers"` + StickerType StickerType `json:"sticker_type,omitempty"` + NeedsRepainting bool `json:"needs_repainting,omitempty"` +} + +func (api *Api) CreateNewStickerSet(p CreateNewStickerSetP) (bool, error) { + req := NewRequest[bool]("createNewStickerSet", p) + return req.Do(api) +} + +type AddStickerToSetP struct { + UserID int `json:"user_id"` + Name string `json:"name"` + Sticker InputSticker `json:"sticker"` +} + +func (api *Api) AddStickerToSet(p AddStickerToSetP) (bool, error) { + req := NewRequest[bool]("addStickerToSet", p) + return req.Do(api) +} + +type SetStickerPositionInSetP struct { + Sticker string `json:"sticker"` + Position int `json:"position"` +} + +func (api *Api) SetStickerPosition(p SetStickerPositionInSetP) (bool, error) { + req := NewRequest[bool]("setStickerPosition", p) + return req.Do(api) +} + +type DeleteStickerFromSetP struct { + Sticker string `json:"sticker"` +} + +func (api *Api) DeleteStickerFromSet(p DeleteStickerFromSetP) (bool, error) { + req := NewRequest[bool]("deleteStickerFromSet", p) + return req.Do(api) +} + +type ReplaceStickerInSetP struct { + UserID int `json:"user_id"` + Name string `json:"name"` + OldSticker string `json:"old_sticker"` + Sticker InputSticker `json:"sticker"` +} + +func (api *Api) ReplaceStickerInSet(p ReplaceStickerInSetP) (bool, error) { + req := NewRequest[bool]("replaceStickerInSet", p) + return req.Do(api) +} + +type SetStickerEmojiListP struct { + Sticker string `json:"sticker"` + EmojiList []string `json:"emoji_list"` +} + +func (api *Api) SetStickerEmojiList(p SetStickerEmojiListP) (bool, error) { + req := NewRequest[bool]("setStickerEmojiList", p) + return req.Do(api) +} + +type SetStickerKeywordsP struct { + Sticker string `json:"sticker"` + Keywords []string `json:"keywords"` +} + +func (api *Api) SetStickerKeywords(p SetStickerKeywordsP) (bool, error) { + req := NewRequest[bool]("setStickerKeywords", p) + return req.Do(api) +} + +type SetStickerMaskPositionP struct { + Sticker string `json:"sticker"` + MaskPosition *MaskPosition `json:"mask_position,omitempty"` +} + +func (api *Api) SetStickerMaskPosition(p SetStickerMaskPositionP) (bool, error) { + req := NewRequest[bool]("setStickerMaskPosition", p) + return req.Do(api) +} + +type SetStickerSetTitleP struct { + Name string `json:"name"` + Title string `json:"title"` +} + +func (api *Api) SetStickerSetTitle(p SetStickerSetTitleP) (bool, error) { + req := NewRequest[bool]("setStickerSetTitle", p) + return req.Do(api) +} + +type SetStickerSetThumbnailP struct { + Name string `json:"name"` + UserID int `json:"user_id"` + Thumbnail string `json:"thumbnail"` + Format InputStickerFormat `json:"format"` +} + +func (api *Api) SetStickerSetThumbnail(p SetStickerSetThumbnailP) (bool, error) { + req := NewRequest[bool]("setStickerSetThumbnail", p) + return req.Do(api) +} + +type SetCustomEmojiStickerSetThumbnailP struct { + Name string `json:"name"` + CustomEmojiID string `json:"custom_emoji_id,omitempty"` +} + +func (api *Api) SetCustomEmojiStickerSetThumbnail(p SetStickerSetThumbnailP) (bool, error) { + req := NewRequest[bool]("setCustomEmojiStickerSetThumbnail", p) + return req.Do(api) +} + +type DeleteStickerSetP struct { + Name string `json:"name"` +} + +func (api *Api) DeleteStickerSet(p DeleteStickerSetP) (bool, error) { + req := NewRequest[bool]("deleteStickerSet", p) + return req.Do(api) +} diff --git a/stickers.go b/tgapi/stickers_types.go similarity index 58% rename from stickers.go rename to tgapi/stickers_types.go index a7f33fe..299c492 100644 --- a/stickers.go +++ b/tgapi/stickers_types.go @@ -1,4 +1,4 @@ -package laniakea +package tgapi type MaskPositionPoint string @@ -34,12 +34,12 @@ type Sticker struct { IsVideo bool `json:"is_video"` Thumbnail *PhotoSize `json:"thumbnail,omitempty"` - Emoji string `json:"emoji,omitempty"` - SetName string `json:"set_name,omitempty"` + Emoji *string `json:"emoji,omitempty"` + SetName *string `json:"set_name,omitempty"` MaskPosition *MaskPosition `json:"mask_position,omitempty"` - CustomEmojiID string `json:"custom_emoji_id,omitempty"` - NeedRepainting bool `json:"need_repainting,omitempty"` - FileSize int `json:"file_size,omitempty"` + CustomEmojiID *string `json:"custom_emoji_id,omitempty"` + NeedRepainting *bool `json:"need_repainting,omitempty"` + FileSize *int `json:"file_size,omitempty"` } type StickerSet struct { Name string `json:"name"` @@ -63,31 +63,3 @@ type InputSticker struct { MaskPosition *MaskPosition `json:"mask_position,omitempty"` Keywords []string `json:"keywords,omitempty"` } - -type SendStickerP struct { - BusinessConnectionID string `json:"business_connection_id,omitempty"` - ChatID int `json:"chat_id"` - MessageThreadID int `json:"message_thread_id,omitempty"` - DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"` - - Sticker string `json:"sticker"` - Emoji string `json:"emoji,omitempty"` - DisableNotification bool `json:"disable_notification,omitempty"` - ProtectContent bool `json:"protect_content,omitempty"` - AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` - MessageEffectID string `json:"message_effect_id,omitempty"` -} - -func (api *Api) SendSticker(p SendStickerP) (*Message, error) { - req := NewRequest[Message]("sendSticker", p) - return req.Do(api) -} - -type GetStickerSetP struct { - Name string `json:"name"` -} - -func (api *Api) GetStickerSet(p GetStickerSetP) (*StickerSet, error) { - req := NewRequest[StickerSet]("getStickerSet", p) - return req.Do(api) -} diff --git a/tgapi/types.go b/tgapi/types.go new file mode 100644 index 0000000..33b25ba --- /dev/null +++ b/tgapi/types.go @@ -0,0 +1,113 @@ +package tgapi + +type Update struct { + UpdateID int `json:"update_id"` + Message *Message `json:"message"` + EditedMessage *Message `json:"edited_message,omitempty"` + ChannelPost *Message `json:"channel_post,omitempty"` + EditedChannelPost *Message `json:"edited_channel_post,omitempty"` + BusinessConnection *BusinessConnection `json:"business_connection,omitempty"` + BusinessMessage *Message `json:"business_message,omitempty"` + EditedBusinessMessage *Message `json:"edited_business_message,omitempty"` + DeletedBusinessMessage *Message `json:"deleted_business_messages,omitempty"` + MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"` + MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"` + CallbackQuery *CallbackQuery `json:"callback_query,omitempty"` + InlineQuery int + ChosenInlineResult int +} + +type File struct { + FileId string `json:"file_id"` + FileUniqueID string `json:"file_unique_id"` + FileSize int `json:"file_size,omitempty"` + FilePath string `json:"file_path,omitempty"` +} + +type Audio struct { + FileID string `json:"file_id"` + FileUniqueID string `json:"file_unique_id"` + Duration int `json:"duration"` + + Performer string `json:"performer,omitempty"` + Title string `json:"title,omitempty"` + FileName string `json:"file_name,omitempty"` + MimeType string `json:"mime_type,omitempty"` + FileSize int `json:"file_size,omitempty"` + Thumbnail *PhotoSize `json:"thumbnail,omitempty"` +} + +type Location struct { + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + HorizontalAccuracy float64 `json:"horizontal_accuracy"` + LivePeriod int `json:"live_period"` + Heading int `json:"heading"` + ProximityAlertRadius int `json:"proximity_alert_radius"` +} +type Venue struct { + Location Location `json:"location"` + Title string `json:"title"` + Address string `json:"address"` + FoursquareID string `json:"foursquare_id,omitempty"` + FoursquareType string `json:"foursquare_type,omitempty"` + GooglePlaceID string `json:"google_place_id,omitempty"` + GooglePlaceType string `json:"google_place_type,omitempty"` +} + +type WebAppInfo struct { + URL string `json:"url"` +} + +type StarAmount struct { + Amount int `json:"amount"` + NanostarAmount int `json:"nanostar_amount"` +} + +// Gifts + +type AcceptedGiftTypes struct { + UnlimitedGifts bool `json:"unlimited_gifts"` + LimitedGifts bool `json:"limited_gifts"` + UniqueGifts bool `json:"unique_gifts"` + PremiumSubscription bool `json:"premium_subscription"` + GiftsFromChannels bool `json:"gifts_from_channels"` +} + +type UniqueGiftColors struct { + ModelCustomEmojiID string `json:"model_custom_emoji_id"` + SymbolCustomEmojiID string `json:"symbol_custom_emoji_id"` + LightThemeMainColor int `json:"light_theme_main_color"` + LightThemeOtherColors []int `json:"light_theme_other_colors"` + DarkThemeMainColor int `json:"dark_theme_main_color"` + DarkThemeOtherColors []int `json:"dark_theme_other_colors"` +} + +type GiftBackground struct { + CenterColor int `json:"center_color"` + EdgeColor int `json:"edge_color"` + TextColor int `json:"text_color"` +} +type Gift struct { + ID string `json:"id"` + Sticker Sticker `json:"sticker"` + StarCount int `json:"star_count"` + UpdateStarCount *int `json:"update_star_count,omitempty"` + IsPremium *bool `json:"is_premium,omitempty"` + HasColors *bool `json:"has_colors,omitempty"` + TotalCount *int `json:"total_count,omitempty"` + RemainingCount *int `json:"remaining_count,omitempty"` + PersonalTotalCount *int `json:"personal_total_count,omitempty"` + PersonalRemainingCount *int `json:"personal_remaining_count,omitempty"` + Background GiftBackground `json:"background,omitempty"` + UniqueGiftVariantColor *int `json:"unique_gift_variant_color,omitempty"` + PublisherChat *Chat `json:"publisher_chat,omitempty"` +} +type Gifts struct { + Gifts []Gift `json:"gifts"` +} + +type OwnedGiftType string +type BaseOwnedGift struct { + Type OwnedGiftType `json:"type"` +} diff --git a/uploader.go b/tgapi/uploader.go similarity index 70% rename from uploader.go rename to tgapi/uploader.go index 8e76341..33b9b52 100644 --- a/uploader.go +++ b/tgapi/uploader.go @@ -1,4 +1,4 @@ -package laniakea +package tgapi import ( "bytes" @@ -9,6 +9,7 @@ import ( "net/http" "path/filepath" + "git.nix13.pw/scuroneko/laniakea/utils" "git.nix13.pw/scuroneko/slog" ) @@ -80,7 +81,7 @@ func (u UploaderRequest[R, P]) Do(up *Uploader) (*R, error) { w.Close() return nil, err } - err = Encode(w, u.params) + err = utils.Encode(w, u.params) if err != nil { w.Close() return nil, err @@ -122,11 +123,31 @@ func (u UploaderRequest[R, P]) Do(up *Uploader) (*R, error) { return response.Result, nil } -func (u *Uploader) UploadPhoto(file UploaderFile, params SendPhotoBaseP) (*Message, error) { +type UploadPhotoP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id"` + MessageThreadID int `json:"message_thread_id,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` + Caption string `json:"caption,omitempty"` + CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` + ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"` + HasSpoiler bool `json:"has_spoiler,omitempty"` + DisableNotifications bool `json:"disable_notifications,omitempty"` + ProtectContent bool `json:"protect_content,omitempty"` + AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` + MessageEffectID string `json:"message_effect_id,omitempty"` + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` +} + +func (u *Uploader) UploadPhoto(file UploaderFile, params UploadPhotoP) (*Message, error) { req := NewUploaderRequest[Message]("sendPhoto", file, params) return req.Do(u) } +// setChatPhoto https://core.telegram.org/bots/api#setchatphoto +type UploadChatPhotoP struct { +} + func uploaderTypeByExt(filename string) UploaderFileType { ext := filepath.Ext(filename) switch ext { diff --git a/tgapi/users_methods.go b/tgapi/users_methods.go new file mode 100644 index 0000000..d54d496 --- /dev/null +++ b/tgapi/users_methods.go @@ -0,0 +1,34 @@ +package tgapi + +type GetUserProfilePhotosP struct { + UserID int `json:"user_id"` + Offset int `json:"offset,omitempty"` + Limit int `json:"limit,omitempty"` +} + +func (api *Api) GetUserProfilePhotos(p GetUserProfilePhotosP) (UserProfilePhotos, error) { + req := NewRequest[UserProfilePhotos]("getUserProfilePhotos", p) + return req.Do(api) +} + +type GetUserProfileAudiosP struct { + UserID int `json:"user_id"` + Offset int `json:"offset,omitempty"` + Limit int `json:"limit,omitempty"` +} + +func (api *Api) GetUserProfileAudios(p GetUserProfileAudiosP) (UserProfileAudios, error) { + req := NewRequest[UserProfileAudios]("getUserProfileAudios", p) + return req.Do(api) +} + +type SetUserEmojiStatusP struct { + UserID int `json:"user_id"` + EmojiID string `json:"emoji_status_custom_emoji_id,omitempty"` + ExpirationDate int `json:"emoji_status_expiration_date,omitempty"` +} + +func (api *Api) SetUserEmojiStatus(p SetUserEmojiStatusP) (bool, error) { + req := NewRequest[bool]("setUserEmojiStatus", p) + return req.Do(api) +} diff --git a/tgapi/users_types.go b/tgapi/users_types.go new file mode 100644 index 0000000..9c674c1 --- /dev/null +++ b/tgapi/users_types.go @@ -0,0 +1,40 @@ +package tgapi + +type User struct { + ID int `json:"id"` + IsBot bool `json:"is_bot"` + FirstName string `json:"first_name"` + LastName string `json:"last_name,omitempty"` + Username string `json:"username,omitempty"` + LanguageCode string `json:"language_code,omitempty"` + IsPremium bool `json:"is_premium,omitempty"` + AddedToAttachmentMenu bool `json:"added_to_attachment_menu,omitempty"` + CanJoinGroups bool `json:"can_join_groups,omitempty"` + CanReadAllGroupMessages bool `json:"can_read_all_group_messages,omitempty"` + SupportsInlineQueries bool `json:"supports_inline_queries,omitempty"` + CanConnectToBusiness bool `json:"can_connect_to_business,omitempty"` + HasMainWebApp bool `json:"has_main_web_app,omitempty"` + HasTopicsEnabled bool `json:"has_topics_enabled,omitempty"` + AllowsUsersToCreateTopics bool `json:"allows_users_to_create_topics,omitempty"` +} + +type UserProfilePhotos struct { + TotalCount int `json:"total_count"` + Photos [][]PhotoSize `json:"photos"` +} +type UserProfileAudios struct { + TotalCount int `json:"total_count"` + Audios []Audio `json:"audios"` +} + +type UserRating struct { + Level int `json:"level"` + Rating int `json:"rating"` + CurrentLevelRating int `json:"current_level_rating"` + NextLevelRating int `json:"next_level_rating"` +} +type Birthdate struct { + Day int `json:"day"` + Month int `json:"month"` + Year int `json:"year"` +} diff --git a/tgapi/utils.go b/tgapi/utils.go new file mode 100644 index 0000000..a982ea3 --- /dev/null +++ b/tgapi/utils.go @@ -0,0 +1,15 @@ +package tgapi + +import ( + "os" + + "git.nix13.pw/scuroneko/slog" +) + +func GetLoggerLevel() slog.LogLevel { + level := slog.FATAL + if os.Getenv("DEBUG") == "true" { + level = slog.DEBUG + } + return level +} diff --git a/utils.go b/utils.go index 5b0f372..7dcedde 100644 --- a/utils.go +++ b/utils.go @@ -1,86 +1,10 @@ package laniakea -import ( - "encoding/json" - "fmt" - "os" - "strings" +func Ptr[T any](v T) *T { return &v } - "git.nix13.pw/scuroneko/slog" -) - -func GetLoggerLevel() slog.LogLevel { - level := slog.FATAL - if os.Getenv("DEBUG") == "true" { - level = slog.DEBUG +func Val[T any](p *T, def T) T { + if p != nil { + return *p } - return level -} - -// MapToStruct unsafe function -func MapToStruct(m map[string]any, s any) error { - data, err := json.Marshal(m) - if err != nil { - return err - } - err = json.Unmarshal(data, s) - return err -} - -// SliceToStruct unsafe function -func SliceToStruct(sl []any, s any) error { - data, err := json.Marshal(sl) - if err != nil { - return err - } - err = json.Unmarshal(data, s) - return err -} - -// AnyToStruct unsafe function -func AnyToStruct(src, dest any) error { - data, err := json.Marshal(src) - if err != nil { - return err - } - err = json.Unmarshal(data, dest) - return err -} - -func MapToJson(m map[string]any) (string, error) { - data, err := json.Marshal(m) - return string(data), err -} - -func StructToMap(s any) (map[string]any, error) { - data, err := json.Marshal(s) - if err != nil { - return nil, err - } - m := make(map[string]any) - err = json.Unmarshal(data, &m) - return m, err -} - -func Map[T, V any](ts []T, fn func(T) V) []V { - result := make([]V, len(ts)) - for i, t := range ts { - result[i] = fn(t) - } - return result -} - -func EscapeMarkdown(s string) string { - s = strings.ReplaceAll(s, "_", `\_`) - s = strings.ReplaceAll(s, "*", `\*`) - s = strings.ReplaceAll(s, "[", `\[`) - return strings.ReplaceAll(s, "`", "\\`") -} - -func EscapeMarkdownV2(s string) string { - symbols := []string{"_", "*", "[", "]", "(", ")", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"} - for _, symbol := range symbols { - s = strings.ReplaceAll(s, symbol, fmt.Sprintf("\\%s", symbol)) - } - return s + return def } diff --git a/multipart.go b/utils/multipart.go similarity index 99% rename from multipart.go rename to utils/multipart.go index 5fc75df..89d2fc7 100644 --- a/multipart.go +++ b/utils/multipart.go @@ -1,4 +1,4 @@ -package laniakea +package utils import ( "encoding/json" diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..c782c58 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,100 @@ +package utils + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "git.nix13.pw/scuroneko/slog" +) + +func GetLoggerLevel() slog.LogLevel { + level := slog.FATAL + if os.Getenv("DEBUG") == "true" { + level = slog.DEBUG + } + return level +} + +func Cast[A, B any](src A) (*B, error) { + m, err := StructToMap(src) + if err != nil { + return nil, err + } + + out := new(B) + err = MapToStruct(m, out) + if err != nil { + return nil, err + } + return out, nil +} + +// MapToStruct unsafe function +func MapToStruct(m map[string]any, s any) error { + data, err := json.Marshal(m) + if err != nil { + return err + } + err = json.Unmarshal(data, s) + return err +} + +// SliceToStruct unsafe function +func SliceToStruct(sl []any, s any) error { + data, err := json.Marshal(sl) + if err != nil { + return err + } + err = json.Unmarshal(data, s) + return err +} + +// AnyToStruct unsafe function +func AnyToStruct(src, dest any) error { + data, err := json.Marshal(src) + if err != nil { + return err + } + err = json.Unmarshal(data, dest) + return err +} + +func MapToJson(m map[string]any) (string, error) { + data, err := json.Marshal(m) + return string(data), err +} + +func StructToMap(s any) (map[string]any, error) { + data, err := json.Marshal(s) + if err != nil { + return nil, err + } + m := make(map[string]any) + err = json.Unmarshal(data, &m) + return m, err +} + +func Map[T, V any](ts []T, fn func(T) V) []V { + result := make([]V, len(ts)) + for i, t := range ts { + result[i] = fn(t) + } + return result +} + +func EscapeMarkdown(s string) string { + s = strings.ReplaceAll(s, "_", `\_`) + s = strings.ReplaceAll(s, "*", `\*`) + s = strings.ReplaceAll(s, "[", `\[`) + return strings.ReplaceAll(s, "`", "\\`") +} + +func EscapeMarkdownV2(s string) string { + symbols := []string{"_", "*", "[", "]", "(", ")", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"} + for _, symbol := range symbols { + s = strings.ReplaceAll(s, symbol, fmt.Sprintf("\\%s", symbol)) + } + return s +} diff --git a/utils/version.go b/utils/version.go new file mode 100644 index 0000000..adc8290 --- /dev/null +++ b/utils/version.go @@ -0,0 +1,8 @@ +package utils + +const ( + VersionString = "0.5.0" + VersionMajor = 0 + VersionMinor = 5 + VersionPatch = 0 +) diff --git a/version.go b/version.go deleted file mode 100644 index 2b17a5f..0000000 --- a/version.go +++ /dev/null @@ -1,8 +0,0 @@ -package laniakea - -const ( - VersionString = "0.4.4-1" - VersionMajor = 0 - VersionMinor = 4 - VersionPatch = 4 -)