v1.0.0 beta 12
This commit is contained in:
291
msg_context.go
291
msg_context.go
@@ -1,3 +1,22 @@
|
||||
// Package laniakea provides a high-level context-based API for handling Telegram
|
||||
// bot interactions, including message responses, callback queries, inline keyboards,
|
||||
// localization, and message drafting. It wraps tgapi and adds convenience methods
|
||||
// with built-in rate limiting, error handling, and i18n support.
|
||||
//
|
||||
// The core type is MsgContext, which encapsulates the state of a Telegram update
|
||||
// and provides methods to respond, edit, delete, and translate messages.
|
||||
//
|
||||
// # Markdown Safety Warning
|
||||
//
|
||||
// All methods that accept MarkdownV2 formatting (e.g., AnswerMarkdown, EditCallbackfMarkdown)
|
||||
// require that user-provided text be escaped using laniakea.EscapeMarkdownV2().
|
||||
// Failure to escape user input may result in Telegram API errors, malformed messages,
|
||||
// or security issues.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// text := laniakea.EscapeMarkdownV2(userInput)
|
||||
// ctx.AnswerMarkdown("You said: " + text)
|
||||
package laniakea
|
||||
|
||||
import (
|
||||
@@ -8,9 +27,11 @@ import (
|
||||
"git.nix13.pw/scuroneko/slog"
|
||||
)
|
||||
|
||||
// MsgContext holds the context for handling a Telegram message or callback query.
|
||||
// It provides methods to respond, edit, delete, and translate messages, as well as
|
||||
// manage inline keyboards and message drafts.
|
||||
type MsgContext struct {
|
||||
Api *tgapi.API
|
||||
|
||||
Api *tgapi.API
|
||||
Msg *tgapi.Message
|
||||
Update tgapi.Update
|
||||
From *tgapi.User
|
||||
@@ -27,22 +48,23 @@ type MsgContext struct {
|
||||
draftProvider *DraftProvider
|
||||
}
|
||||
|
||||
// AnswerMessage represents a message sent or edited via MsgContext.
|
||||
// It holds metadata to allow further editing or deletion.
|
||||
type AnswerMessage struct {
|
||||
MessageID int
|
||||
Text string
|
||||
IsMedia bool
|
||||
ctx *MsgContext
|
||||
ctx *MsgContext // internal back-reference
|
||||
}
|
||||
|
||||
func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard, escapeMd bool) *AnswerMessage {
|
||||
// edit is an internal helper to edit a message's text with optional keyboard and parse mode.
|
||||
// Used by Edit, EditMarkdown, EditCallback, etc.
|
||||
func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||
params := tgapi.EditMessageTextP{
|
||||
MessageID: messageId,
|
||||
ChatID: ctx.Msg.Chat.ID,
|
||||
Text: text,
|
||||
ParseMode: tgapi.ParseMDV2,
|
||||
}
|
||||
if escapeMd {
|
||||
params.Text = EscapeMarkdownV2(text)
|
||||
ParseMode: parseMode,
|
||||
}
|
||||
if keyboard != nil {
|
||||
params.ReplyMarkup = keyboard.Get()
|
||||
@@ -56,38 +78,67 @@ func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard
|
||||
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Edit replaces the text of the message without changing the keyboard or parse mode.
|
||||
// Uses ParseNone (plain text).
|
||||
func (m *AnswerMessage) Edit(text string) *AnswerMessage {
|
||||
return m.ctx.edit(m.MessageID, text, nil, true)
|
||||
return m.ctx.edit(m.MessageID, text, nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// EditMarkdown replaces the text of the message using MarkdownV2 formatting.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
// Unescaped input may cause Telegram API errors or broken formatting.
|
||||
func (m *AnswerMessage) EditMarkdown(text string) *AnswerMessage {
|
||||
return m.ctx.edit(m.MessageID, text, nil, false)
|
||||
return m.ctx.edit(m.MessageID, text, nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// editCallback is an internal helper to edit the message associated with a callback query.
|
||||
// Returns nil if CallbackMsgId is 0 (not a callback context).
|
||||
func (ctx *MsgContext) editCallback(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||
if ctx.CallbackMsgId == 0 {
|
||||
ctx.botLogger.Errorln("Can't edit non-callback update message")
|
||||
return nil
|
||||
}
|
||||
return ctx.edit(ctx.CallbackMsgId, text, keyboard, parseMode)
|
||||
}
|
||||
|
||||
// EditCallback edits the callback message using plain text (ParseNone).
|
||||
func (ctx *MsgContext) EditCallback(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
||||
if ctx.CallbackMsgId == 0 {
|
||||
ctx.botLogger.Errorln("Can't edit non-callback update message")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctx.edit(ctx.CallbackMsgId, text, keyboard, true)
|
||||
return ctx.editCallback(text, keyboard, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// EditCallbackMarkdown edits the callback message using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) EditCallbackMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
||||
if ctx.CallbackMsgId == 0 {
|
||||
ctx.botLogger.Errorln("Can't edit non-callback update message")
|
||||
return ctx.editCallback(text, keyboard, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// EditCallbackf formats a string using fmt.Sprintf and edits the callback message with plain text.
|
||||
func (ctx *MsgContext) EditCallbackf(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage {
|
||||
return ctx.editCallback(fmt.Sprintf(format, args...), keyboard, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// EditCallbackfMarkdown formats a string using fmt.Sprintf and edits the callback message with MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) EditCallbackfMarkdown(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage {
|
||||
return ctx.editCallback(fmt.Sprintf(format, args...), keyboard, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// editPhotoText edits the caption of a photo/video message.
|
||||
// Returns nil if messageId is 0.
|
||||
func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||
if messageId == 0 {
|
||||
ctx.botLogger.Errorln("Can't edit caption message, message ID zero")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctx.edit(ctx.CallbackMsgId, text, keyboard, false)
|
||||
}
|
||||
func (ctx *MsgContext) EditCallbackf(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage {
|
||||
return ctx.EditCallback(fmt.Sprintf(format, args...), keyboard)
|
||||
}
|
||||
|
||||
func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
params := tgapi.EditMessageCaptionP{
|
||||
ChatID: ctx.Msg.Chat.ID,
|
||||
MessageID: messageId,
|
||||
Caption: text,
|
||||
ParseMode: tgapi.ParseMD,
|
||||
ParseMode: parseMode,
|
||||
}
|
||||
if kb != nil {
|
||||
params.ReplyMarkup = kb.Get()
|
||||
@@ -101,25 +152,38 @@ func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeybo
|
||||
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: true,
|
||||
}
|
||||
}
|
||||
|
||||
// EditCaption edits the caption of a media message using plain text.
|
||||
func (m *AnswerMessage) EditCaption(text string) *AnswerMessage {
|
||||
if m.MessageID == 0 {
|
||||
m.ctx.botLogger.Errorln("Can't edit caption message, message id is zero")
|
||||
return m
|
||||
}
|
||||
return m.ctx.editPhotoText(m.MessageID, text, nil)
|
||||
}
|
||||
func (m *AnswerMessage) EditCaptionKeyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return m.ctx.editPhotoText(m.MessageID, text, kb)
|
||||
return m.ctx.editPhotoText(m.MessageID, text, nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, escapeMd bool) *AnswerMessage {
|
||||
// EditCaptionMarkdown edits the caption of a media message using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (m *AnswerMessage) EditCaptionMarkdown(text string) *AnswerMessage {
|
||||
return m.ctx.editPhotoText(m.MessageID, text, nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// EditCaptionKeyboard edits the caption of a media message with a new inline keyboard (plain text).
|
||||
func (m *AnswerMessage) EditCaptionKeyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return m.ctx.editPhotoText(m.MessageID, text, kb, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// EditCaptionKeyboardMarkdown edits the caption of a media message with a new inline keyboard using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (m *AnswerMessage) EditCaptionKeyboardMarkdown(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return m.ctx.editPhotoText(m.MessageID, text, kb, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// answer sends a new message with optional keyboard and parse mode.
|
||||
// Uses API limiter to respect Telegram rate limits per chat.
|
||||
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||
params := tgapi.SendMessageP{
|
||||
ChatID: ctx.Msg.Chat.ID,
|
||||
Text: text,
|
||||
ParseMode: tgapi.ParseMDV2,
|
||||
}
|
||||
if escapeMd {
|
||||
params.Text = EscapeMarkdownV2(text)
|
||||
ParseMode: parseMode,
|
||||
}
|
||||
if keyboard != nil {
|
||||
params.ReplyMarkup = keyboard.Get()
|
||||
@@ -145,35 +209,51 @@ func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, escapeMd bo
|
||||
MessageID: msg.MessageID, ctx: ctx, IsMedia: false, Text: text,
|
||||
}
|
||||
}
|
||||
func (ctx *MsgContext) AnswerMarkdown(text string) *AnswerMessage {
|
||||
return ctx.answer(text, nil, false)
|
||||
}
|
||||
|
||||
// Answer sends a plain text message (ParseNone).
|
||||
func (ctx *MsgContext) Answer(text string) *AnswerMessage {
|
||||
return ctx.answer(text, nil, true)
|
||||
}
|
||||
func (ctx *MsgContext) AnswerfMarkdown(template string, args ...any) *AnswerMessage {
|
||||
return ctx.answer(fmt.Sprintf(template, args...), nil, false)
|
||||
}
|
||||
func (ctx *MsgContext) Answerf(template string, args ...any) *AnswerMessage {
|
||||
return ctx.answer(fmt.Sprintf(template, args...), nil, true)
|
||||
}
|
||||
func (ctx *MsgContext) KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answer(text, keyboard, false)
|
||||
}
|
||||
func (ctx *MsgContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answer(text, kb, true)
|
||||
return ctx.answer(text, nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, escapeMd bool) *AnswerMessage {
|
||||
// AnswerMarkdown sends a message using MarkdownV2 formatting.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) AnswerMarkdown(text string) *AnswerMessage {
|
||||
return ctx.answer(text, nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// Answerf formats a string using fmt.Sprintf and sends it as a plain text message.
|
||||
func (ctx *MsgContext) Answerf(template string, args ...any) *AnswerMessage {
|
||||
return ctx.answer(fmt.Sprintf(template, args...), nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// AnswerfMarkdown formats a string using fmt.Sprintf and sends it using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) AnswerfMarkdown(template string, args ...any) *AnswerMessage {
|
||||
return ctx.answer(fmt.Sprintf(template, args...), nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// Keyboard sends a message with an inline keyboard (plain text).
|
||||
func (ctx *MsgContext) Keyboard(text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answer(text, kb, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// KeyboardMarkdown sends a message with an inline keyboard using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answer(text, keyboard, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// answerPhoto sends a photo with optional caption and keyboard.
|
||||
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||
params := tgapi.SendPhotoP{
|
||||
ChatID: ctx.Msg.Chat.ID,
|
||||
Caption: text,
|
||||
ParseMode: tgapi.ParseMDV2,
|
||||
ParseMode: parseMode,
|
||||
Photo: photoId,
|
||||
}
|
||||
if escapeMd {
|
||||
params.Caption = EscapeMarkdownV2(text)
|
||||
}
|
||||
if kb != nil {
|
||||
params.ReplyMarkup = kb.Get()
|
||||
}
|
||||
@@ -184,35 +264,50 @@ func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, esc
|
||||
msg, err := ctx.Api.SendPhoto(params)
|
||||
if err != nil {
|
||||
ctx.botLogger.Errorln(err)
|
||||
return &AnswerMessage{
|
||||
ctx: ctx, Text: text, IsMedia: true,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return &AnswerMessage{
|
||||
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: true,
|
||||
}
|
||||
}
|
||||
|
||||
// AnswerPhoto sends a photo with plain text caption.
|
||||
func (ctx *MsgContext) AnswerPhoto(photoId, text string) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, text, nil, true)
|
||||
return ctx.answerPhoto(photoId, text, nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// AnswerPhotoMarkdown sends a photo with MarkdownV2 caption.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) AnswerPhotoMarkdown(photoId, text string) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, text, nil, false)
|
||||
return ctx.answerPhoto(photoId, text, nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// AnswerPhotoKeyboard sends a photo with caption and inline keyboard (plain text).
|
||||
func (ctx *MsgContext) AnswerPhotoKeyboard(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, text, kb, true)
|
||||
return ctx.answerPhoto(photoId, text, kb, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// AnswerPhotoKeyboardMarkdown sends a photo with caption and inline keyboard using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) AnswerPhotoKeyboardMarkdown(photoId, text string, kb *InlineKeyboard) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, text, kb, false)
|
||||
return ctx.answerPhoto(photoId, text, kb, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// AnswerPhotof formats a string and sends it as a photo caption (plain text).
|
||||
func (ctx *MsgContext) AnswerPhotof(photoId, template string, args ...any) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, true)
|
||||
}
|
||||
func (ctx *MsgContext) AnswerPhotofMarkdown(photoId, template string, args ...any) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, false)
|
||||
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, tgapi.ParseNone)
|
||||
}
|
||||
|
||||
// AnswerPhotofMarkdown formats a string and sends it as a photo caption using MarkdownV2.
|
||||
//
|
||||
// ⚠️ WARNING: User input must be escaped with laniakea.EscapeMarkdownV2() before passing here.
|
||||
func (ctx *MsgContext) AnswerPhotofMarkdown(photoId, template string, args ...any) *AnswerMessage {
|
||||
return ctx.answerPhoto(photoId, fmt.Sprintf(template, args...), nil, tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// delete removes a message by ID.
|
||||
func (ctx *MsgContext) delete(messageId int) {
|
||||
_, err := ctx.Api.DeleteMessage(tgapi.DeleteMessageP{
|
||||
ChatID: ctx.Msg.Chat.ID,
|
||||
@@ -222,9 +317,15 @@ func (ctx *MsgContext) delete(messageId int) {
|
||||
ctx.botLogger.Errorln(err)
|
||||
}
|
||||
}
|
||||
func (m *AnswerMessage) Delete() { m.ctx.delete(m.MessageID) }
|
||||
|
||||
// Delete removes the message associated with this AnswerMessage.
|
||||
func (m *AnswerMessage) Delete() { m.ctx.delete(m.MessageID) }
|
||||
|
||||
// CallbackDelete deletes the message that triggered the callback query.
|
||||
func (ctx *MsgContext) CallbackDelete() { ctx.delete(ctx.CallbackMsgId) }
|
||||
|
||||
// answerCallbackQuery sends a response to a callback query (optional text/alert/url).
|
||||
// Does nothing if CallbackQueryId is empty.
|
||||
func (ctx *MsgContext) answerCallbackQuery(url, text string, showAlert bool) {
|
||||
if len(ctx.CallbackQueryId) == 0 {
|
||||
return
|
||||
@@ -237,11 +338,20 @@ func (ctx *MsgContext) answerCallbackQuery(url, text string, showAlert bool) {
|
||||
ctx.botLogger.Errorln(err)
|
||||
}
|
||||
}
|
||||
func (ctx *MsgContext) AnswerCbQuery() { ctx.answerCallbackQuery("", "", false) }
|
||||
func (ctx *MsgContext) AnswerCbQueryText(text string) { ctx.answerCallbackQuery("", text, false) }
|
||||
func (ctx *MsgContext) AnswerCbQueryAlert(text string) { ctx.answerCallbackQuery("", text, true) }
|
||||
func (ctx *MsgContext) AnswerCbQueryUrl(u string) { ctx.answerCallbackQuery(u, "", false) }
|
||||
|
||||
// AnswerCbQuery answers the callback query with no text or alert.
|
||||
func (ctx *MsgContext) AnswerCbQuery() { ctx.answerCallbackQuery("", "", false) }
|
||||
|
||||
// AnswerCbQueryText answers the callback query with a text notification.
|
||||
func (ctx *MsgContext) AnswerCbQueryText(text string) { ctx.answerCallbackQuery("", text, false) }
|
||||
|
||||
// AnswerCbQueryAlert answers the callback query with a user-visible alert.
|
||||
func (ctx *MsgContext) AnswerCbQueryAlert(text string) { ctx.answerCallbackQuery("", text, true) }
|
||||
|
||||
// AnswerCbQueryUrl answers the callback query with a URL redirect.
|
||||
func (ctx *MsgContext) AnswerCbQueryUrl(u string) { ctx.answerCallbackQuery(u, "", false) }
|
||||
|
||||
// SendAction sends a chat action (typing, uploading_photo, etc.) to indicate bot activity.
|
||||
func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) {
|
||||
params := tgapi.SendChatActionP{
|
||||
ChatID: ctx.Msg.Chat.ID, Action: action,
|
||||
@@ -255,30 +365,47 @@ func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) {
|
||||
}
|
||||
}
|
||||
|
||||
// error sends an error message to the user and logs it.
|
||||
// Uses errorTemplate to format the message.
|
||||
// For callbacks: sends as callback answer (no alert).
|
||||
// For regular messages: sends as plain text.
|
||||
func (ctx *MsgContext) error(err error) {
|
||||
text := fmt.Sprintf(ctx.errorTemplate, err.Error())
|
||||
|
||||
if ctx.CallbackQueryId != "" {
|
||||
ctx.answerCallbackQuery("", text, false)
|
||||
} else {
|
||||
ctx.answer(text, nil, true)
|
||||
ctx.answer(text, nil, tgapi.ParseNone)
|
||||
}
|
||||
ctx.botLogger.Errorln(err)
|
||||
}
|
||||
|
||||
// Error is an alias for error().
|
||||
func (ctx *MsgContext) Error(err error) { ctx.error(err) }
|
||||
|
||||
func (ctx *MsgContext) NewDraft() *Draft {
|
||||
func (ctx *MsgContext) newDraft(parseMode tgapi.ParseMode) *Draft {
|
||||
c := context.Background()
|
||||
if err := ctx.Api.Limiter.Wait(c, ctx.Msg.Chat.ID); err != nil {
|
||||
ctx.botLogger.Errorln(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
draft := ctx.draftProvider.NewDraft()
|
||||
draft.chatID = ctx.Msg.Chat.ID
|
||||
draft.messageThreadID = ctx.Msg.MessageThreadID
|
||||
draft := ctx.draftProvider.NewDraft(parseMode).SetChat(ctx.Msg.Chat.ID, ctx.Msg.MessageThreadID)
|
||||
return draft
|
||||
}
|
||||
|
||||
// NewDraft creates a new message draft associated with the current chat.
|
||||
// Uses the API limiter to avoid rate limiting.
|
||||
func (ctx *MsgContext) NewDraft() *Draft {
|
||||
return ctx.newDraft(tgapi.ParseNone)
|
||||
}
|
||||
|
||||
func (ctx *MsgContext) NewDraftMarkdown() *Draft {
|
||||
return ctx.newDraft(tgapi.ParseMDV2)
|
||||
}
|
||||
|
||||
// Translate looks up a key in the current user's language.
|
||||
// Falls back to the bot's default language if user's language is unknown or unsupported.
|
||||
func (ctx *MsgContext) Translate(key string) string {
|
||||
if ctx.From == nil {
|
||||
return key
|
||||
|
||||
Reference in New Issue
Block a user