diff --git a/.gitignore b/.gitignore index eb5b34a..9a15d12 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,13 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +#swap files +*~ + # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ settings_custom.json history.json users.json -chat_data.json \ No newline at end of file +chat_data.json diff --git a/alfred.go b/alfred.go index a52b950..be6af5b 100644 --- a/alfred.go +++ b/alfred.go @@ -19,19 +19,25 @@ import ( func main() { registeredCommands := map[string]func(*tb.Message){ - tb.OnText: commands.OnText, - "/hello": commands.Hello, - "/sponge": commands.Sponge, - "/git": commands.Git, - "/framapad": commands.Framapad, - "/setgender": commands.SetGender, - "/gender": commands.Gender, - "/roll": commands.Dice, - "/trump": commands.LastTrumpTweet, - "/trends": commands.TwitterTrends, - "/chaos": commands.TwitterSJW, - "/apero": commands.AperoTime, - "/quote": commands.Quote, + tb.OnText: commands.OnText, + "/hello": commands.Hello, + "/sponge": commands.Sponge, + "/git": commands.Git, + "/framapad": commands.Framapad, + "/setgender": commands.SetGender, + "/gender": commands.Gender, + "/roll": commands.Dice, + "/trump": commands.LastTrumpTweet, + "/trends": commands.TwitterTrends, + "/chaos": commands.TwitterSJW, + "/apero": commands.AperoTime, + "/quote": commands.Quote, + "/subscribe": commands.Subscribe, + "/unsubscribe": commands.Unsubscribe, + "/listsubscribers": commands.ListSubscribers, + "/publish": commands.Publish, + "/unpublish": commands.Unpublish, + "/retrieve": commands.Retrieve, } if err := settings.LoadSettings("settings.json", "settings_custom.json"); err != nil { diff --git a/commands/subscribe.go b/commands/subscribe.go new file mode 100644 index 0000000..d0ac26c --- /dev/null +++ b/commands/subscribe.go @@ -0,0 +1,187 @@ +/* +* @Author: Amalvy Arthur + */ + +// This module uses a property named "subscribed_chats" in the shared Users structure. +// It consists of a list of chatID as a string, chatID are separated by ":" (unix PATH style) + +// This module uses a property named "saved_messages" in the shared Chats structure +// It consists of a slice of string + +package commands + +import ( + "fmt" + "strconv" + "strings" + + "../shared" + tb "gopkg.in/tucnak/telebot.v2" +) + +// Subscribe user sending the command to the current chat +// Command syntax : /subscribe +func Subscribe(m *tb.Message) { + if m.Chat.Type != "group" && m.Chat.Type != "supergroup" { + shared.Bot.Send(m.Chat, "Cette commande n'est pas authorisée pour ce type de chat") + return + } + userSubscribedChats, exists := shared.Users.Get(m.Sender.Username, "subscribed_chats") + if !exists { + shared.Bot.Send(m.Chat, "Abonnement au chat : "+m.Chat.Title) + shared.Users.Set(m.Sender.Username, "subscribed_chats", strconv.FormatInt(m.Chat.ID, 10)) + return + } + splittedChats := strings.Split(userSubscribedChats, ":") + for _, chatID := range splittedChats { + if chatID == strconv.FormatInt(m.Chat.ID, 10) { + shared.Bot.Send(m.Chat, "Vous êtes déjà abonné à ce chat : "+m.Chat.Title) + return + } + } + shared.Bot.Send(m.Chat, "Abonnement au chat : "+m.Chat.Title) + if len(userSubscribedChats) != 0 { + shared.Users.Set(m.Sender.Username, "subscribed_chats", userSubscribedChats+":"+strconv.FormatInt(m.Chat.ID, 10)) + } else { + shared.Users.Set(m.Sender.Username, "subscribed_chats", strconv.FormatInt(m.Chat.ID, 10)) + } +} + +// Unsubscribe user sending the command from the current chat +// Command syntax : /unsubscribe +func Unsubscribe(m *tb.Message) { + if m.Chat.Type != "group" && m.Chat.Type != "supergroup" { + shared.Bot.Send(m.Chat, "Cette commande n'est pas authorisée pour ce type de chat") + return + } + userSubscribedChats, exists := shared.Users.Get(m.Sender.Username, "subscribed_chats") + if !exists || len(userSubscribedChats) == 0 { + shared.Bot.Send(m.Chat, "Vous n'êtes abonné à aucun chat") + return + } + splittedChats := strings.Split(userSubscribedChats, ":") + for i, chatID := range splittedChats { + if chatID == strconv.FormatInt(m.Chat.ID, 10) { + shared.Bot.Send(m.Chat, "désabonnement du chat : "+m.Chat.Title) + splittedChats = append(splittedChats[:i], splittedChats[i+1:]...) + break + } + } + shared.Users.Set(m.Sender.Username, "subscribed_chats", strings.Join(splittedChats, ":")) +} + +// ListSubscribers List all subscribers of the current chat +// Command syntax : /listsubscribers +func ListSubscribers(m *tb.Message) { + if m.Chat.Type != "group" && m.Chat.Type != "supergroup" { + shared.Bot.Send(m.Chat, "Cette commande n'est pas authorisée pour ce type de chat") + return + } + subscribers := m.Chat.Title + " subscribers : \n\n" + for _, subscriber := range getSubscribers(m.Chat.ID) { + subscribers = subscribers + "- " + subscriber + "\n" + } + shared.Bot.Send(m.Chat, subscribers) +} + +// Publish a message from the current chat +// Command syntax (while repying to a message) : /publish +func Publish(m *tb.Message) { + if m.ReplyTo == nil { + shared.Bot.Send(m.Chat, "Veuillez répondre à un message pour le publier") + return + } + + defer shared.Bot.Send(m.Chat, "Message publié : "+m.ReplyTo.Text) + savedMessages, exists := shared.ChatData.Get(m.Chat.ID, "published_messages") + if !exists { + messageList := []string{m.ReplyTo.Text} + shared.ChatData.Set(m.Chat.ID, "published_messages", messageList) + return + } + shared.ChatData.Set(m.Chat.ID, "published_messages", + append(savedMessages.([]interface{}), m.ReplyTo.Text)) + +} + +// Unpublish remove a message from published messages in the current chat +// Command syntax : /unpublish [publication ID] +func Unpublish(m *tb.Message) { + parsedCommand := strings.Split(m.Text, " ") + if len(parsedCommand) < 2 { + shared.Bot.Send(m.Chat, "syntaxe : /unpublish [publication ID]") + return + } + index, err := strconv.Atoi(parsedCommand[1]) + if err != nil { + shared.Bot.Send(m.Chat, "ID de publication invalide") + return + } + savedMessages, exists := shared.ChatData.Get(m.Chat.ID, "published_messages") + if !exists || len(savedMessages.([]interface{})) == 0 { + shared.Bot.Send(m.Chat, "Aucun message publié") + return + } + if len(savedMessages.([]interface{})) <= index || index < 0 { + shared.Bot.Send(m.Chat, "Aucun message avec cet ID de publication n'existe") + return + } + savedMessages = append(savedMessages.([]interface{})[:index], savedMessages.([]interface{})[index+1:]...) + shared.ChatData.Set(m.Chat.ID, "published_messages", savedMessages) + shared.Bot.Send(m.Chat, "Message supprimé des publication") +} + +// Retrieve If performed in MP : retrieve all messages from all subscribed sources for the user +// If performed in Group Chat : retrieved all published messages for this chat +// Command syntax : /retrieve +func Retrieve(m *tb.Message) { + if m.Chat.Type == "group" || m.Chat.Type == "supergroup" { + savedMessages, exists := shared.ChatData.Get(m.Chat.ID, "published_messages") + fmt.Println(savedMessages) + if !exists || len(savedMessages.([]interface{})) == 0 { + shared.Bot.Send(m.Chat, "Aucun message publié") + return + } + shared.Bot.Send(m.Chat, "--- Messages publiés ---") + for index, message := range savedMessages.([]interface{}) { + shared.Bot.Send(m.Chat, strconv.Itoa(index)+" : "+message.(string)) + } + shared.Bot.Send(m.Chat, "--- Messages publiés ---") + return + } + if m.Chat.Type == "private" { + userSubscribedChats, exists := shared.Users.Get(m.Sender.Username, "subscribed_chats") + if !exists || len(userSubscribedChats) == 0 { + shared.Bot.Send(m.Chat, "Aucun abonnement") + return + } + for _, chat := range strings.Split(userSubscribedChats, ":") { + chatID, _ := strconv.ParseInt(chat, 10, 64) + savedMessages, _ := shared.ChatData.Get(chatID, "published_messages") + shared.Bot.Send(m.Chat, "--- Messages publiés ---") + for index, message := range savedMessages.([]interface{}) { + shared.Bot.Send(m.Chat, strconv.Itoa(index)+" : "+message.(string)) + } + shared.Bot.Send(m.Chat, "--- Messages publiés ---") + } + return + } + shared.Bot.Send(m.Chat, "Cette commande n'est pas autorisée pour ce type de chat") +} + +// Get all users subscribed to the provided channel +func getSubscribers(chatID int64) []string { + var subscribers []string + for _, username := range shared.Users.GetUsernames() { + userSubscribedChats, exists := shared.Users.Get(username, "subscribed_chats") + if exists { + splittedChats := strings.Split(userSubscribedChats, ":") + for _, splittedChatID := range splittedChats { + if splittedChatID == strconv.FormatInt(chatID, 10) { + subscribers = append(subscribers, username) + } + } + } + } + return subscribers +} diff --git a/doc/publish.md b/doc/publish.md new file mode 100644 index 0000000..84e4e4d --- /dev/null +++ b/doc/publish.md @@ -0,0 +1,65 @@ +# Publish system overview + +The publish module intend to be a "multi-pin" feature for chats, allowing users to retrieve importants messages in a crowded conversation. + +## Commands + + +### Subscribe + +**Description** : Subscribe to this group chat publications + +**Usage location** : Group chat only + +**Command syntax** : /subscribe + + +### Unsubscribe + +**Description** : Unsubscribe [target chat member] from this group chat publications. If no member is provided, unsubscribe yourself. + +**Usage location** : Group chat + +**Command syntax** : /unsubscribe [target chat member] + +*note* : parameter [target chat member] not implemented + + +### ListSubscribers + +**Description** : List all subscribers of this group chat + +**Usage location** : Group chat + +**Command syntax** : /listsubscribers + + +### Publish + +**Description** : Publish a message to Alfred for this group chat and sends this message via MP to every subscriber of the group chat. The message can then be retrieved using the *retrieve* command. + +**Usage location** : Group chat only + +**Usage conditions** : Reply to the message to publish + +**Command syntax** : /publish + +*note* : MP not implemented + +### Unpublish + +**Description** : Remove a published message from Alfred for this group chat + +**Usage location** : Group chat only + +**Command syntax** : /unpublish [publication ID] + + +### Retrieve + +**Description** : if MP, retrieve all messages from subscribed sources for user, if Group Chat, retrieve all published messages for this chat + +**Usage location** : Group Chat or MP + +**Command syntax** : /retrieve + diff --git a/shared/users.go b/shared/users.go index ebb78f8..7eb47a2 100644 --- a/shared/users.go +++ b/shared/users.go @@ -61,6 +61,17 @@ func (u users) Set(username string, key, data string) { go uf.write() } +// GetUsernames all usernames stored in settings +func (u users) GetUsernames() []string { + u.mutex.Lock() + defer u.mutex.Unlock() + var usernames []string + for username, _ := range u.data { + usernames = append(usernames, username) + } + return usernames +} + func (u usersFile) read() { u.mutex.Lock() defer u.mutex.Unlock()