telegabber/telegram/commands.go
Bohdan Horbeshko 0e6fbc652f Merge branch 'muc' into calls
( and finished Caps support, sorry for the mess :-' )
2026-03-25 13:29:33 -04:00

1081 lines
28 KiB
Go

package telegram
import (
"fmt"
"github.com/pkg/errors"
"strconv"
"strings"
"time"
"unicode"
"dev.narayana.im/narayana/telegabber/persistence"
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
log "github.com/sirupsen/logrus"
"github.com/zelenin/go-tdlib/client"
)
const unknownCommand string = "Unknown command"
const notEnoughArguments string = "Not enough arguments"
const TelegramNotInitialized string = "Telegram connection is not initialized yet"
const TelegramAuthDone string = "Authorization is done already"
const notOnline string = "Not online"
var permissionsAdmin = client.ChatAdministratorRights{
CanChangeInfo: true,
CanPostMessages: true,
CanEditMessages: true,
CanDeleteMessages: true,
CanInviteUsers: true,
CanRestrictMembers: true,
CanPinMessages: true,
CanPromoteMembers: false,
}
var permissionsMember = client.ChatPermissions{
CanSendBasicMessages: true,
CanSendAudios: true,
CanSendDocuments: true,
CanSendPhotos: true,
CanSendVideos: true,
CanSendVideoNotes: true,
CanSendVoiceNotes: true,
CanSendPolls: true,
CanSendOtherMessages: true,
CanAddWebPagePreviews: true,
CanChangeInfo: true,
CanInviteUsers: true,
CanPinMessages: true,
CanManageTopics: true,
}
var permissionsReadonly = client.ChatPermissions{}
var transportConfigurationOptions = map[string]configurationOption{
"timezone": configurationOption{"<timezone>", "adjust timezone for Telegram user statuses (example: +02:00)"},
"keeponline": configurationOption{"<bool>", "always keep telegram session online and rely on jabber offline messages (true/false)"},
"rawmessages": configurationOption{"<bool>", "do not add additional info (message id, origin etc.) to incoming messages (true/false)"},
"asciiarrows": configurationOption{"<bool>", "replace some Unicode symbols with ASCII alternatives for better compatibility (true/false)"},
"muc": configurationOption{"<bool>", "use MUCs instead of the legacy PM representation of group chats (true/false)"},
"oobmode": configurationOption{"<bool>", "use XEP-0066 (OOB); pros: some modern clients won't show images without it, cons: very restricted, Tkabber would flood with popups (true/false)"},
"carbons": configurationOption{"<bool>", "send carbons to your another clients, will turn on only if supported by the server (true/false)"},
"hideids": configurationOption{"<bool>", "hide message IDs from message info (true/false)"},
"receipts": configurationOption{"<bool>", "if enabled, XMPP read receipts are synced to Telegram, otherwise, messages are marked as read automatically (true/false)"},
"nativeedits": configurationOption{"<bool>", "if possible, edit XMPP messages instead of showing Telegram edits as separate messages (true/false)"},
"ignoregroupdeletions": configurationOption{"<bool>", "suppress message deletion messages in group chats (true/false)"},
}
type configurationOption struct {
arguments string
description string
}
func (c *Client) helpString(typ gateway.CommandType, chatId int64) string {
var str strings.Builder
commandMap := gateway.GetCommands(typ)
chatType, _, _ := c.GetChatType(chatId, true)
str.WriteString("Available commands:\n")
if typ == gateway.CommandTypeTransport {
gateway.CommandsToHelpString(&str, chatType, gateway.OnlineFilterNotOnline, commandMap)
str.WriteString("\nOnline-only commands:\n")
gateway.CommandsToHelpString(&str, chatType, gateway.OnlineFilterOnline, commandMap)
str.WriteString("\nConfiguration options\n")
for _, name := range persistence.ConfigKeys {
option := transportConfigurationOptions[name]
str.WriteString(name)
str.WriteString(" ")
str.WriteString(option.arguments)
str.WriteString(" — ")
str.WriteString(option.description)
str.WriteString("\n")
}
} else if typ == gateway.CommandTypeChat {
gateway.CommandsToHelpString(&str, chatType, gateway.OnlineFilterAny, commandMap)
}
str.WriteString("\nYou may use ! instead of / if it conflicts with internal commands of a client")
return str.String()
}
func parseCommand(cmdline string) (string, []string) {
bodyFields := strings.Fields(cmdline)
return bodyFields[0][1:], bodyFields[1:]
}
func rawCmdArguments(cmdline string, start uint8) string {
var state uint
// /cmd ababa galamaga
// 01 2 3 45
startState := uint(3 + 2*start)
for i, r := range cmdline {
isOdd := state%2 == 1
isSpace := unicode.IsSpace(r)
if (!isOdd && !isSpace) || (isOdd && isSpace) {
state += 1
}
if state == startState {
return cmdline[i:]
}
}
return ""
}
func keyValueString(key, value string) string {
return fmt.Sprintf("%s: %s", key, value)
}
func (c *Client) usernameOrIDToID(username string) (int64, error) {
userID, err := strconv.ParseInt(username, 10, 64)
// couldn't parse the id, try to lookup as a username
if err != nil {
chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{
Username: username,
})
if err != nil {
return 0, err
}
userID = chat.Id
if userID <= 0 {
return 0, errors.New("Not a user")
}
}
return userID, nil
}
// ProcessTransportCommand executes a command sent directly to the component
// and returns a response and execution success result
func (c *Client) ProcessTransportCommand(cmdline string, resource string) (string, bool) {
cmd, args := parseCommand(cmdline)
command, ok := gateway.TransportCommands[cmd]
if !ok {
return unknownCommand, false
}
if len(args) < command.RequiredArgs {
return notEnoughArguments, false
}
if command.OnlineOnly && !c.Online() {
return notOnline, false
}
switch cmd {
case "login", "code", "password":
if cmd == "login" && c.Session.Login != "" {
return "Phone number already provided, use /cancelauth to start over", false
}
if cmd == "login" {
err := c.TryLogin(resource, args[0])
if err != nil {
return err.Error(), false
}
c.locks.authorizerWriteLock.Lock()
defer c.locks.authorizerWriteLock.Unlock()
c.authorizer.PhoneNumber <- args[0]
} else {
c.locks.authorizerWriteLock.Lock()
defer c.locks.authorizerWriteLock.Unlock()
if c.authorizer == nil {
return TelegramNotInitialized, false
}
if c.authorizer.isClosed {
return TelegramAuthDone, false
}
switch cmd {
// check auth code
case "code":
c.authorizer.Code <- args[0]
// check auth password
case "password":
c.authorizer.Password <- args[0]
}
}
return "", true
// sign out
case "logout":
if !c.Online() && !c.locks.loginFinish.IsPending() {
return notOnline, false
}
_, err := c.client.LogOut()
if err != nil {
return errors.Wrap(err, "Logout error").Error(), false
}
c.unsubscribeFromAll()
c.Session.Login = ""
c.wizardStageOrPrompt(LoginStageCancel, "")
c.online = false
// cleanup
case "cleanup":
c.unsubscribeFromAll()
// cancel auth
case "cancelauth":
if c.Online() {
return "Not allowed when online, use /logout instead", false
}
c.cancelAuth()
return "Cancelled", true
// set @username
case "setusername":
var username string
if len(args) > 0 {
username = args[0]
}
_, err := c.client.SetUsername(&client.SetUsernameRequest{
Username: username,
})
if err != nil {
return errors.Wrap(err, "Couldn't set username").Error(), false
}
// set My Name
case "setname":
firstname := args[0]
var lastname string
if firstname == "" {
return "The name should contain at least one character", false
}
if len(args) > 1 {
lastname = rawCmdArguments(cmdline, 1)
}
c.locks.authorizerWriteLock.Lock()
if c.authorizer != nil && !c.authorizer.isClosed {
c.authorizer.FirstName <- firstname
c.authorizer.LastName <- lastname
c.locks.authorizerWriteLock.Unlock()
} else {
c.locks.authorizerWriteLock.Unlock()
if !c.Online() {
return notOnline, false
}
_, err := c.client.SetName(&client.SetNameRequest{
FirstName: firstname,
LastName: lastname,
})
if err != nil {
return errors.Wrap(err, "Couldn't set name").Error(), false
}
}
// set About
case "setbio":
_, err := c.client.SetBio(&client.SetBioRequest{
Bio: rawCmdArguments(cmdline, 0),
})
if err != nil {
return errors.Wrap(err, "Couldn't set bio").Error(), false
}
// set password
case "setpassword":
var oldPassword string
var newPassword string
if len(args) > 0 {
oldPassword = args[0]
}
if len(args) > 1 {
newPassword = args[1]
}
_, err := c.client.SetPassword(&client.SetPasswordRequest{
OldPassword: oldPassword,
NewPassword: newPassword,
})
if err != nil {
return errors.Wrap(err, "Couldn't set password").Error(), false
}
case "config":
if len(args) > 1 {
if gateway.MessageOutgoingPermissionVersion == 0 && args[0] == "carbons" && args[1] == "true" {
return "The server did not allow to enable carbons", false
}
value, err := c.Session.Set(args[0], args[1])
if err != nil {
return err.Error(), false
}
if args[0] == "muc" {
switch args[1] {
case "true":
go c.MigrateToMUCs()
case "false":
go c.MigrateFromMUCs()
}
if c.loginStage == LoginStageMUC {
c.wizardStageOrPrompt(LoginStageSuccess, "")
}
}
gateway.DirtySessions = true
return fmt.Sprintf("%s set to %s", args[0], value), true
} else if len(args) > 0 {
value, err := c.Session.Get(args[0])
if err != nil {
return err.Error(), false
}
return fmt.Sprintf("%s is set to %s", args[0], value), true
}
var entries []string
for _, key := range persistence.ConfigKeys {
value, err := c.Session.Get(key)
if err != nil {
log.Errorf("Achtung! Programming error in sessions with key %v", key)
continue
}
entries = append(entries, fmt.Sprintf("%s is set to %s", key, value))
}
return strings.Join(entries, "\n"), true
case "status":
return fmt.Sprintf("Login stage: %v\nLogin: %v", c.loginStage, c.Session.Login), true
case "report":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), false
}
if contact == nil {
return "Contact not found", false
}
text := rawCmdArguments(cmdline, 1)
_, err = c.client.ReportChat(&client.ReportChatRequest{
ChatId: contact.Id,
Reason: &client.ReportReasonCustom{},
Text: text,
})
if err != nil {
return err.Error(), false
} else {
return "Reported", true
}
case "add":
return c.cmdAdd(args)
case "join":
return c.cmdJoin(args)
case "supergroup":
return c.cmdSupergroup(args, cmdline)
case "channel":
return c.cmdChannel(args, cmdline)
case "help":
return c.helpString(gateway.CommandTypeTransport, 0), true
case "preset":
cancelNextStage := false
defer func() {
if !cancelNextStage {
go c.promptMUC()
}
}()
switch args[0] {
case "modern", "classic":
for _, row := range persistence.Presets[args[0]] {
_, err := c.Session.Set(row[0], row[1])
if err != nil {
return err.Error(), false
}
}
gateway.DirtySessions = true
return fmt.Sprintf("Applied preset %s", args[0]), true
default:
cancelNextStage = true
return "Invalid argument. Allowed ones are " + gateway.TransportCommands["preset"].Arguments[0], false
}
case "pass":
switch c.loginStage {
case LoginStagePreset:
go c.promptMUC()
case LoginStageMUC:
c.wizardStageOrPrompt(LoginStageSuccess, "")
default:
return "Not applicable here", false
}
case "finish":
switch c.loginStage {
case LoginStagePreset, LoginStageMUC:
c.wizardStageOrPrompt(LoginStageSuccess, "")
default:
return "Not applicable here", false
}
}
return "", true
}
func (c *Client) promptMUC() {
c.wizardStageOrPrompt(LoginStageMUC, "Enable MUCs? Telegabber still supports the legacy approach of mapping Telegram group chats to personal messages in XMPP which might be suitable for some cases like logging or reliable participation in all group chats. Use `/config muc {true|false}` or /finish")
}
// ProcessChatCommand executes a command sent in a mapped chat
// and returns a response, the status of command support and the execution success result
func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool, bool) {
cmd, args := parseCommand(cmdline)
command, ok := gateway.ChatCommands[cmd]
if !ok {
return unknownCommand, false, false
}
if len(args) < command.RequiredArgs {
return notEnoughArguments, true, false
}
if command.OnlineOnly && !c.Online() {
return notOnline, true, false
}
chatType, _, chatTypeErr := c.GetChatType(chatID, true)
if chatTypeErr == nil && !gateway.IsCommandForChatType(command, chatType) {
return "Not applicable for this chat type", true, false
}
switch cmd {
// delete message
case "d":
if c.me == nil {
return "@me is not initialized", true, false
}
var limit int32
if len(args) > 0 {
limit64, err := strconv.ParseInt(args[0], 10, 32)
if err != nil {
return err.Error(), true, false
}
limit = int32(limit64)
} else {
limit = 1
}
messages, err := c.getLastMessages(chatID, "", c.me.Id, limit)
if err != nil {
return err.Error(), true, false
}
log.Debugf("pre-deletion query: %#v %#v", messages, messages.Messages)
var messageIds []int64
for _, message := range messages.Messages {
if message != nil {
messageIds = append(messageIds, message.Id)
}
}
_, err = c.client.DeleteMessages(&client.DeleteMessagesRequest{
ChatId: chatID,
MessageIds: messageIds,
Revoke: true,
})
if err != nil {
return err.Error(), true, false
}
return "", true, true
// edit message
case "s":
if c.me == nil {
return "@me is not initialized", true, false
}
messages, err := c.getLastMessages(chatID, "", c.me.Id, 1)
if err != nil {
return err.Error(), true, false
}
if len(messages.Messages) == 0 {
return "No last message", true, false
}
message := messages.Messages[0]
if message == nil {
return "Last message is empty", true, false
}
content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 0))
if content != nil {
_, err = c.client.EditMessageText(&client.EditMessageTextRequest{
ChatId: chatID,
MessageId: message.Id,
InputMessageContent: content,
})
if err != nil {
return "Message editing error", true, false
}
} else {
return "Message processing error", true, false
}
return "", true, true
// send without sound
case "silent":
content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 0))
if content != nil {
_, err := c.client.SendMessage(&client.SendMessageRequest{
ChatId: chatID,
InputMessageContent: content,
Options: &client.MessageSendOptions{
DisableNotification: true,
},
})
if err != nil {
return err.Error(), true, false
}
} else {
return "Message processing error", true, false
}
// schedule a message to timestamp or to going online
case "schedule":
var state client.MessageSchedulingState
var result string
due := args[0]
if due == "online" {
state = &client.MessageSchedulingStateSendWhenOnline{}
result = due
} else {
due += c.GetTZD()
switch 0 {
default:
// try bare time first
timestamp, err := time.Parse("15:04:05Z07:00", due)
if err == nil {
now := time.Now().In(c.Session.TimezoneToLocation())
// combine timestamp's time with today's date
timestamp = time.Date(
now.Year(),
now.Month(),
now.Day(),
timestamp.Hour(),
timestamp.Minute(),
timestamp.Second(),
0,
timestamp.Location(),
)
diff := timestamp.Sub(now)
if diff < 0 { // set to tomorrow
timestamp = timestamp.AddDate(0, 0, 1)
}
state = &client.MessageSchedulingStateSendAtDate{
SendDate: int32(timestamp.Unix()),
}
result = timestamp.Format(time.RFC3339)
break
}
timestamp, err = time.Parse(time.RFC3339, due)
if err == nil {
// 2038 doomsday again
state = &client.MessageSchedulingStateSendAtDate{
SendDate: int32(timestamp.Unix()),
}
result = timestamp.Format(time.RFC3339)
break
}
return "Invalid schedule time specifier", true, false
}
}
content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 1))
if content != nil {
_, err := c.client.SendMessage(&client.SendMessageRequest{
ChatId: chatID,
InputMessageContent: content,
Options: &client.MessageSendOptions{
SchedulingState: state,
},
})
if err != nil {
return err.Error(), true, false
}
return "Scheduled to " + result, true, true
} else {
return "Message processing error", true, false
}
// sends a raw non-interpreted message
case "raw":
content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 0))
if content != nil {
_, err := c.client.SendMessage(&client.SendMessageRequest{
ChatId: chatID,
InputMessageContent: content,
})
if err != nil {
return err.Error(), true, false
}
} else {
return "Message processing error", true, false
}
return "", true, true
// forward a message to chat
case "forward":
messageId, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
return "Cannot parse message ID", true, false
}
targetChatParts := strings.Split(args[1], "@") // full JIDs are supported too
targetChatId, err := strconv.ParseInt(targetChatParts[0], 10, 64)
if err != nil {
return "Cannot parse target chat ID", true, false
}
messages, err := c.client.ForwardMessages(&client.ForwardMessagesRequest{
ChatId: targetChatId,
FromChatId: chatID,
MessageIds: []int64{messageId},
})
if err != nil {
return err.Error(), true, false
}
if messages != nil && messages.Messages != nil {
for _, message := range messages.Messages {
c.ProcessIncomingMessage(targetChatId, message)
}
}
// print vCard
case "vcard":
info, err := c.GetVcardInfo(chatID)
if err != nil {
return err.Error(), true, false
}
_, link := c.PermastoreFile(info.Photo, true)
entries := []string{
keyValueString("Chat title", info.Fn),
keyValueString("Photo", link),
keyValueString("Usernames", c.usernamesToString(info.Nicknames)),
keyValueString("Full name", info.Given+" "+info.Family),
keyValueString("Phone number", info.Tel),
}
return strings.Join(entries, "\n"), true, true
// add @contact
case "add":
response, success := c.cmdAdd(args)
return response, true, success
// join https://t.me/publichat or @publicchat
case "join":
response, success := c.cmdJoin(args)
return response, true, success
// create new supergroup
case "supergroup":
response, success := c.cmdSupergroup(args, cmdline)
return response, true, success
// create new channel
case "channel":
response, success := c.cmdChannel(args, cmdline)
return response, true, success
// create new secret chat with current user
case "secret":
_, err := c.client.CreateNewSecretChat(&client.CreateNewSecretChatRequest{
UserId: chatID,
})
if err != nil {
return err.Error(), true, false
}
// create group chat with current user
case "group":
_, err := c.client.CreateNewBasicGroupChat(&client.CreateNewBasicGroupChatRequest{
UserIds: []int64{chatID},
Title: args[0],
})
if err != nil {
return err.Error(), true, false
}
// blacklists current user
case "block":
_, err := c.client.SetMessageSenderBlockList(&client.SetMessageSenderBlockListRequest{
SenderId: &client.MessageSenderUser{UserId: chatID},
BlockList: &client.BlockListMain{},
})
if err != nil {
return err.Error(), true, false
}
// unblacklists current user
case "unblock":
_, err := c.client.SetMessageSenderBlockList(&client.SetMessageSenderBlockListRequest{
SenderId: &client.MessageSenderUser{UserId: chatID},
BlockList: nil,
})
if err != nil {
return err.Error(), true, false
}
// invite @username to current groupchat
case "invite":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
_, err = c.client.AddChatMember(&client.AddChatMemberRequest{
ChatId: chatID,
UserId: contact.Id,
ForwardLimit: 100,
})
if err != nil {
return err.Error(), true, false
}
// get link to current chat
case "link":
link, err := c.client.CreateChatInviteLink(&client.CreateChatInviteLinkRequest{
ChatId: chatID,
})
if err != nil {
return err.Error(), true, false
}
return link.InviteLink, true, true
// kick @username from current group chat
case "kick":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusKicked, 0, "", "")
if err != nil {
return err.Error(), true, false
}
// mute [@username [n hours]]
case "mute":
if len(args) > 0 {
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
var hours int64
if len(args) > 1 {
hours, err = strconv.ParseInt(args[1], 10, 32)
if err != nil {
return "Invalid number of hours", true, false
}
}
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusMuted, hours, "", "")
if err != nil {
return err.Error(), true, false
}
} else {
if !c.Session.IgnoreChat(chatID) {
return "Chat is already ignored", true, false
}
gateway.DirtySessions = true
}
// unmute [@username]
case "unmute":
if len(args) > 0 {
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusUnmuted, 0, "", "")
if err != nil {
return err.Error(), true, false
}
} else {
if !c.Session.UnignoreChat(chatID) {
return "Chat wasn't ignored", true, false
}
gateway.DirtySessions = true
}
// ban @username from current chat [for N hours]
case "ban":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
var hours int64
if len(args) > 1 {
hours, err = strconv.ParseInt(args[1], 10, 32)
if err != nil {
return "Invalid number of hours", true, false
}
}
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusBanned, hours, "", "")
if err != nil {
return err.Error(), true, false
}
// unban @username
case "unban":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusUnbanned, 0, "", "")
if err != nil {
return err.Error(), true, false
}
// promote @username to admin
case "promote":
contact, _, err := c.GetContactByUsername(args[0], false)
if err != nil {
return err.Error(), true, false
}
if contact == nil {
return "Contact not found", true, false
}
var customTitle string
if len(args) > 1 {
customTitle = args[1]
}
// clone the permissions
err = c.SetChatMemberStatus(chatID, contact.Id, ChatMemberStatusPromoted, 0, customTitle, "")
if err != nil {
return err.Error(), true, false
}
// leave current chat
case "leave":
_, err := c.client.LeaveChat(&client.LeaveChatRequest{
ChatId: chatID,
})
if err != nil {
return err.Error(), true, false
}
err = c.leaveChat(chatID)
if err != nil {
return err.Error(), true, false
}
// leave current chat (for owners)
case "leave!":
err := c.DeleteChat(chatID)
if err != nil {
return err.Error(), true, false
}
err = c.leaveChat(chatID)
if err != nil {
return err.Error(), true, false
}
// set TTL
case "ttl":
var ttl int64
var err error
if len(args) > 0 {
ttl, err = strconv.ParseInt(args[0], 10, 32)
if err != nil {
return "Invalid TTL", true, false
}
}
_, err = c.client.SetChatMessageAutoDeleteTime(&client.SetChatMessageAutoDeleteTimeRequest{
ChatId: chatID,
MessageAutoDeleteTime: int32(ttl),
})
if err != nil {
return err.Error(), true, false
}
// close secret chat
case "close":
chat, _, err := c.GetContactByID(chatID, nil, true)
if err != nil {
return err.Error(), true, false
}
if chat == nil {
return "Chat not found", true, false
}
chatType := chat.Type.ChatTypeType()
if chatType == client.TypeChatTypeSecret {
chatTypeSecret, _ := chat.Type.(*client.ChatTypeSecret)
_, err = c.client.CloseSecretChat(&client.CloseSecretChatRequest{
SecretChatId: chatTypeSecret.SecretChatId,
})
if err != nil {
return err.Error(), true, false
}
err = c.leaveChat(chatID)
if err != nil {
return err.Error(), true, false
}
}
// delete current chat
case "delete":
_, err := c.client.DeleteChatHistory(&client.DeleteChatHistoryRequest{
ChatId: chatID,
RemoveFromChatList: true,
Revoke: true,
})
if err != nil {
return err.Error(), true, false
}
err = c.leaveChat(chatID)
if err != nil {
return err.Error(), true, false
}
// message search
case "search":
var limit int32 = 100
if len(args) > 1 {
newLimit, err := strconv.ParseInt(args[1], 10, 32)
if err == nil {
limit = int32(newLimit)
}
}
var query string
if len(args) > 0 {
query = args[0]
}
messages, err := c.getLastMessages(chatID, query, 0, limit)
if err != nil {
return err.Error(), true, false
}
c.sendMessagesReverse(chatID, messages.Messages, true, "")
return "", true, true
// get latest entries from history
case "history":
var limit int32 = 10
if len(args) > 0 {
newLimit, err := strconv.ParseInt(args[0], 10, 32)
if err == nil {
limit = int32(newLimit)
}
}
messages, err := c.getNLastMessages(chatID, NewMessageLimitMessages(limit))
if err != nil {
return err.Error(), true, false
}
c.sendMessagesReverse(chatID, messages, true, "")
return "", true, true
// chat members
case "members":
var query string
if len(args) > 0 {
query = args[0]
}
members, err := c.GetChatMembers(chatID, false, query, MembersListMembers)
if err != nil {
return err.Error(), true, false
}
var entries []string
for _, member := range members {
senderId := c.GetSenderId(member.MemberId)
entries = append(entries, fmt.Sprintf(
"%v | role: %v",
c.FormatContact(senderId),
member.Status.ChatMemberStatusType(),
))
}
return strings.Join(entries, "\n"), true, true
case "help":
return c.helpString(gateway.CommandTypeChat, chatID), true, true
default:
return "", false, false
}
return "Success", true, true
}
func (c *Client) cmdAdd(args []string) (string, bool) {
chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{
Username: args[0],
})
if err != nil {
return err.Error(), false
}
if chat == nil {
return "No error, but chat is nil", false
}
c.subscribeToID(chat.Id, chat, true)
return "Subscription sent", true
}
func (c *Client) cmdJoin(args []string) (string, bool) {
if strings.HasPrefix(args[0], "@") {
chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{
Username: args[0],
})
if err != nil {
return err.Error(), false
}
if chat == nil {
return "No error, but chat is nil", false
}
_, err = c.client.JoinChat(&client.JoinChatRequest{
ChatId: chat.Id,
})
if err != nil {
return err.Error(), false
}
} else {
_, err := c.client.JoinChatByInviteLink(&client.JoinChatByInviteLinkRequest{
InviteLink: args[0],
})
if err != nil {
return err.Error(), false
}
}
return "Joined", true
}
func (c *Client) cmdSupergroup(args []string, cmdline string) (string, bool) {
_, err := c.client.CreateNewSupergroupChat(&client.CreateNewSupergroupChatRequest{
Title: args[0],
Description: rawCmdArguments(cmdline, 1),
})
if err != nil {
return err.Error(), false
}
return "Created", true
}
func (c *Client) cmdChannel(args []string, cmdline string) (string, bool) {
_, err := c.client.CreateNewSupergroupChat(&client.CreateNewSupergroupChatRequest{
Title: args[0],
Description: rawCmdArguments(cmdline, 1),
IsChannel: true,
})
if err != nil {
return err.Error(), false
}
return "Created", true
}