package main import ( "fmt" "log" "regexp" "strings" "code.ivysaur.me/libnmdc" telegram "github.com/go-telegram-bot-api/telegram-bot-api" ) // registerUser lodges a user mapping in the configuration file. // This allows them to join the group chat (unbanning them if necessary). // An actual NMDC connection will occur once the user joins for the first time. func (this *NTFServer) registerUser(telegramUserId int64, hubUsername string) error { if existingHubNick, ok := this.config.KnownUsers[telegramUserId]; ok { if existingHubNick == hubUsername { return nil } return fmt.Errorf("Telegram account is already registered with hub nick '%s'", existingHubNick) } for _, v := range this.config.KnownUsers { if v == hubUsername { return fmt.Errorf("Requested hub nick '%s' is already used by another member", hubUsername) } } this.config.KnownUsers[telegramUserId] = hubUsername err := this.config.Save(this.configFile) if err != nil { return err } // Unban from groupchat, if necessary // Ignore errors because the user might not have been banned _, err = this.bot.UnbanChatMember(telegram.ChatMemberConfig{ ChatID: this.config.GroupChatID, UserID: int(telegramUserId), }) if err != nil { log.Printf("Couldn't unban user '%s' from groupchat because: %s (assuming OK, continuing)", hubUsername, err.Error()) } return nil } func (this *NTFServer) HandleDirectMessage(update telegram.Update) error { // Registration workflow userID := int64(update.Message.From.ID) // Stash the telegram user ID against this direct-message chat ID so that // we can always reply later on chatID := update.Message.Chat.ID if oldChatID, ok := this.config.DirectMessageChats[userID]; !ok || oldChatID != chatID { this.config.DirectMessageChats[userID] = chatID err := this.config.Save(this.configFile) if err != nil { log.Printf("Couldn't save chat ID %d for user %d", chatID, userID) } } respond := func(str string) error { return this.ReplyTelegramUser(userID, str, update.Message.MessageID) } // Find out the current status for this chat ID hubNick, isKnown := this.config.KnownUsers[userID] _, isInGroupChat := this.config.GroupChatMembers[userID] // Handle the incoming request msg := update.Message.Text if strings.HasPrefix(msg, "/pm ") { if !(isKnown && isInGroupChat) { return respond("Can't send a native PM until you've joined.") } conn, ok := this.conns[hubNick] if !ok { return respond("Can't send a native PM (No upstream hub connection)") } parts := strings.SplitN(msg, " ", 3) if len(parts) != 3 { return respond("Expected format /pm [recipient] [message] - try again...") } if !conn.UserExists(parts[1]) { return respond(fmt.Sprintf("Can't PM offline user '%s'", parts[1])) } err := conn.SayPrivate(parts[1], parts[2]) if err != nil { log.Printf("Sending PM %s -> %s failed because: %s", hubNick, parts[1], err.Error()) return respond(fmt.Sprintf("Sending PM failed because: %s", err.Error())) } } else if strings.HasPrefix(msg, "/join ") { requestedHubNick := msg[6:] // Some users take the [] in the message literally. A-Za-z0-9 are the only supported characters requestedHubNick = regexp.MustCompile(`[^a-zA-Z0-9]`).ReplaceAllString(requestedHubNick, "") // Minimum nick lengths if len(requestedHubNick) < this.config.HubNickMinChars { return respond(fmt.Sprintf("Upstream nickname '%s' should be at least %d characters long", requestedHubNick, this.config.HubNickMinChars)) } err := this.registerUser(userID, requestedHubNick) if err != nil { log.Printf("Failed to register user: %s", err.Error()) return respond(fmt.Sprintf("Couldn't allow registration because: %s", err.Error())) } return respond(fmt.Sprintf("Hi '%s'! You are now registered, and can join %s at %s", requestedHubNick, this.config.HubDescription, this.inviteLink)) } else if strings.HasPrefix(msg, "/rejoin") { if isKnown && !isInGroupChat { return respond(fmt.Sprintf("Welcome back '%s'! You can join %s at %s", hubNick, this.config.HubDescription, this.inviteLink)) } else { return respond("You are either already joined (try /quit first), or not yet registered (try /join first).") } } else if strings.HasPrefix(msg, "/userlist") { conn, ok := this.conns[hubNick] if !ok { return respond("Can't get userlist for you (No upstream hub connection)") } usernames, err := this.getUserlistText(conn) if err != nil { return respond("Can't get userlist for you (internal error)") } return respond("Online users: " + usernames) } else if strings.HasPrefix(msg, "/whois ") { target := msg[7:] conn, ok := this.conns[hubNick] if !ok { return respond("Can't get user details for you (No upstream hub connection)") } var userinfo libnmdc.UserInfo var exists bool err := conn.Users(func(umap *map[string]libnmdc.UserInfo) error { userinfo, exists = (*umap)[target] return nil }) if err != nil { log.Printf("Error retrieving userlist: %s", err.Error()) return respond("Can't get user details for you (internal error)") } if !exists { return respond(fmt.Sprintf("There is no online user '%s'.", target)) } return respond(fmt.Sprintf("Description: %s\nShare size: %d\nClient: %s %s", userinfo.Description, userinfo.ShareSize, userinfo.ClientTag, userinfo.ClientVersion)) } else if strings.HasPrefix(msg, "/quit") { this.kickAndDrop(userID) return respond("Disconnected. You can register again by typing /help .") } else { // e.g. /start or /help // Help helpMsg := `I am a bot that connects Telegram with ` + this.config.HubDescription + ".\n" if isKnown { helpMsg += "You are currently registered as: '" + hubNick + "'\n" } else { helpMsg += "You aren't connected yet.\n" } if isInGroupChat { helpMsg += "You are currently in the groupchat.\n" } else { helpMsg += "You haven't joined the groupchat.\n" } helpMsg += ` Available commands: /setup - Welcome message /join [hubnick] - Register as 'hubnick' and join ` + this.config.HubDescription + ` /rejoin - Re-invite if you are already registered /pm [recipient] [message] - Send a native PM (if connected) /userlist - List native online users /whois [hubnick] - Check if native user is online, and read their description /quit - Unregister your nick and leave the group chat ` return respond(helpMsg) } return nil }