diff --git a/FallenSub/db/chats.go b/FallenSub/db/chats.go new file mode 100644 index 0000000..5690d2b --- /dev/null +++ b/FallenSub/db/chats.go @@ -0,0 +1,102 @@ +package db + +import ( + "encoding/json" + "errors" + "github.com/go-redis/redis/v8" + "strconv" +) + +type State struct { + BotID int64 `json:"botId"` + Chats []int64 `json:"chats"` + Users []int64 `json:"users"` +} + +func stateKey(botId int64) string { + return "stats:" + strconv.FormatInt(botId, 10) +} + +func GetStats(botId int64) (*State, error) { + data, err := rdb.Get(ctx, stateKey(botId)).Result() + if errors.Is(err, redis.Nil) { + return &State{BotID: botId}, nil + } else if err != nil { + return nil, err + } + var state State + if err = json.Unmarshal([]byte(data), &state); err != nil { + return nil, err + } + return &state, nil +} + +// setStats sets the stats for a bot +func setStats(state *State) error { + data, err := json.Marshal(state) + if err != nil { + return err + } + return rdb.Set(ctx, stateKey(state.BotID), string(data), 0).Err() +} + +// AddChat adds a chat to the stats +func AddChat(botId int64, chatId int64) error { + state, err := GetStats(botId) + if err != nil { + return err + } + if !findInInt64Slice(state.Chats, chatId) { + state.Chats = append(state.Chats, chatId) + return setStats(state) + } + return nil +} + +// AddUser adds a user to the stats +func AddUser(botId int64, userId int64) error { + state, err := GetStats(botId) + if err != nil { + return err + } + if !findInInt64Slice(state.Users, userId) { + state.Users = append(state.Users, userId) + return setStats(state) + } + return nil +} + +// AllChats gets all chats in the stats +func AllChats(botId int64) ([]int64, error) { + state, err := GetStats(botId) + if err != nil { + return nil, err + } + return state.Chats, nil +} + +// AllUsers gets all users in the stats +func AllUsers(botId int64) ([]int64, error) { + state, err := GetStats(botId) + if err != nil { + return nil, err + } + return state.Users, nil +} + +// GetAllBots gets all bots in the stats +func GetAllBots() ([]int64, error) { + keys, _, err := rdb.Scan(ctx, 0, "stats:*", 0).Result() + if err != nil { + return nil, err + } + var bots []int64 + for _, key := range keys { + botId, err := strconv.ParseInt(key[6:], 10, 64) + if err != nil { + return nil, err + } + bots = append(bots, botId) + } + return bots, nil +} diff --git a/FallenSub/db/db.go b/FallenSub/db/db.go index 20299c0..1ea18d4 100644 --- a/FallenSub/db/db.go +++ b/FallenSub/db/db.go @@ -3,6 +3,7 @@ package db import ( "context" "encoding/json" + "errors" "github.com/Abishnoi69/Force-Sub-Bot/FallenSub/config" "github.com/go-redis/redis/v8" "log" @@ -40,16 +41,26 @@ func redisKey(chatId int64) string { return "forceSub:" + strconv.FormatInt(chatId, 10) } +// findInInt64Slice checks if a value is in a slice or not +func findInInt64Slice(slice []int64, val int64) bool { + for _, item := range slice { + if item == val { + return true + } + } + return false +} + // GetFSubSetting gets the FSub setting for a chat func GetFSubSetting(chatId int64) (*FSub, error) { data, err := rdb.Get(ctx, redisKey(chatId)).Result() - if err == redis.Nil { + if errors.Is(err, redis.Nil) { return &FSub{ChatId: chatId}, nil } else if err != nil { return nil, err } var fSub FSub - if err := json.Unmarshal([]byte(data), &fSub); err != nil { + if err = json.Unmarshal([]byte(data), &fSub); err != nil { return nil, err } return &fSub, nil @@ -120,13 +131,3 @@ func IsMuted(chatId int64, userId int64) (bool, error) { } return findInInt64Slice(fSub.FSubMuted, userId), nil } - -// findInInt64Slice checks if a value is in a slice or not -func findInInt64Slice(slice []int64, val int64) bool { - for _, item := range slice { - if item == val { - return true - } - } - return false -} diff --git a/FallenSub/modules/fSub.go b/FallenSub/modules/fSub.go index aced8ff..2584294 100644 --- a/FallenSub/modules/fSub.go +++ b/FallenSub/modules/fSub.go @@ -14,6 +14,11 @@ func setFSub(b *gotgbot.Bot, ctx *ext.Context) error { return ext.EndGroups } + go func() { + _ = db.AddChat(b.Id, ctx.EffectiveChat.Id) + }() + + // If the message is a reply to a forwarded message, handle it if repliedMsg := ctx.EffectiveMessage.ReplyToMessage; repliedMsg != nil && repliedMsg.ForwardOrigin != nil { return handleForwardedMessage(ctx, b, repliedMsg) } diff --git a/FallenSub/modules/loadModules.go b/FallenSub/modules/loadModules.go index 4f1816e..746c843 100644 --- a/FallenSub/modules/loadModules.go +++ b/FallenSub/modules/loadModules.go @@ -12,6 +12,9 @@ func LoadModules(d *ext.Dispatcher) { d.AddHandler(handlers.NewCommand("start", start)) d.AddHandler(handlers.NewCommand("ping", ping)) d.AddHandler(handlers.NewCommand("fsub", setFSub)) + d.AddHandler(handlers.NewCommand("getChats", getChats)) + d.AddHandler(handlers.NewCommand("getUsers", getUsers)) + d.AddHandler(handlers.NewCommand("getAllBots", getAllBots)) d.AddHandlerToGroup(handlers.NewMessage(message.All, fSubWatcher), 0) d.AddHandler(handlers.NewCallback(callbackquery.Prefix("unmuteMe_"), unMuteMe)) } diff --git a/FallenSub/modules/start.go b/FallenSub/modules/start.go index 7f58e22..0a1f3bb 100644 --- a/FallenSub/modules/start.go +++ b/FallenSub/modules/start.go @@ -2,6 +2,7 @@ package modules import ( "fmt" + "github.com/Abishnoi69/Force-Sub-Bot/FallenSub/db" "github.com/PaulSonOfLars/gotgbot/v2" "github.com/PaulSonOfLars/gotgbot/v2/ext" "time" @@ -9,6 +10,11 @@ import ( // start is the handler for the /start command func start(b *gotgbot.Bot, ctx *ext.Context) error { + if ctx.EffectiveChat.Type == gotgbot.ChatTypePrivate { + _ = db.AddUser(b.Id, ctx.EffectiveMessage.From.Id) + } + + _ = db.AddChat(b.Id, ctx.EffectiveChat.Id) text := fmt.Sprintf("Hello, %s!\n\nI am a bot that can help you manage your group by forcing users to join a channel before they can send messages in the group.\n\nTo get started, add me to your group and make me an admin with ban users permission. Then, set the channel that you want users to join using /fsub command.\n\nFor more information, click the button below.", ctx.EffectiveMessage.From.FirstName) button := gotgbot.InlineKeyboardMarkup{ InlineKeyboard: [][]gotgbot.InlineKeyboardButton{ diff --git a/FallenSub/modules/stats.go b/FallenSub/modules/stats.go new file mode 100644 index 0000000..b553658 --- /dev/null +++ b/FallenSub/modules/stats.go @@ -0,0 +1,96 @@ +package modules + +import ( + "fmt" + "github.com/Abishnoi69/Force-Sub-Bot/FallenSub/config" + "github.com/Abishnoi69/Force-Sub-Bot/FallenSub/db" + "github.com/PaulSonOfLars/gotgbot/v2" + "github.com/PaulSonOfLars/gotgbot/v2/ext" +) + +// getChats is the handler for the /getChats command +func getChats(b *gotgbot.Bot, ctx *ext.Context) error { + msg := ctx.EffectiveMessage + if msg.From.Id != config.OwnerId { + _, _ = msg.Reply(b, "You are not allowed to use this command.", nil) + return ext.EndGroups + } + + // Get all chats + chats, err := db.AllChats(b.Id) + if err != nil { + return logError("Error getting chats", err) + } + + // Create a message with the list of chats + var chatList string + for _, chat := range chats { + chatList += fmt.Sprintf("» %d\n", chat) + } + + // Send the list of chats as a message + _, err = b.SendMessage(msg.Chat.Id, fmt.Sprintf("Total chats: %d\n\n%s", len(chats), chatList), nil) + if err != nil { + return logError("Error sending message", err) + } + + return ext.EndGroups +} + +// getUsers is the handler for the /getUsers command +func getUsers(b *gotgbot.Bot, ctx *ext.Context) error { + msg := ctx.EffectiveMessage + if msg.From.Id != config.OwnerId { + _, _ = msg.Reply(b, "You are not allowed to use this command.", nil) + return ext.EndGroups + } + + // Get all users + users, err := db.AllUsers(b.Id) + if err != nil { + return logError("Error getting users", err) + } + + // Create a message with the list of users + var userList string + for _, user := range users { + userList += fmt.Sprintf("» %d\n", user) + } + + // Send the list of users as a message + _, err = b.SendMessage(msg.Chat.Id, fmt.Sprintf("Total users: %d\n\n%s", len(users), userList), nil) + if err != nil { + return logError("Error sending message", err) + } + + return ext.EndGroups +} + +// getAllBots is the handler for the /getAllBots command +func getAllBots(b *gotgbot.Bot, ctx *ext.Context) error { + msg := ctx.EffectiveMessage + if msg.From.Id != config.OwnerId { + _, _ = msg.Reply(b, "You are not allowed to use this command.", nil) + return ext.EndGroups + } + + // Get all bots + bots, err := db.GetAllBots() + if err != nil { + return logError("Error getting bots", err) + } + + // Create a message with the list of bots + var botList string + for _, bot := range bots { + botList += fmt.Sprintf("» %d\n", bot) + } + + // Send the list of bots as a message + _, err = b.SendMessage(msg.Chat.Id, fmt.Sprintf("Total bots: %d\n\n%s", len(bots), botList), nil) + if err != nil { + return logError("Error sending message", err) + } + + return ext.EndGroups +} diff --git a/README.md b/README.md index bfb6d4b..07c1f1a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,11 @@ source /etc/profile.d/golang_path.sh
  • /fsub on - Enable force subscription mode.
  • /fsub off - Disable force subscription mode.
  • /fsub - Get the current force subscription status.
  • + +

    Owner Only Commands

    +
  • /getChats - Get all chats.
  • +
  • /getUsers - Get all users who started in private.
  • +
  • /getAllBots - Get all bots running on webhook.