diff --git a/commands.go b/commands.go index 9b851da..0d9f6f8 100644 --- a/commands.go +++ b/commands.go @@ -654,7 +654,7 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) { cmdPrefix + cmdSyncHelp, cmdPrefix + cmdListHelp, cmdPrefix + cmdOpenHelp, - // cmdPrefix + cmdPMHelp, + cmdPrefix + cmdPMHelp, // cmdPrefix + cmdInviteLinkHelp, // cmdPrefix + cmdJoinHelp, // cmdPrefix + cmdCreateHelp, @@ -762,21 +762,11 @@ func (handler *CommandHandler) CommandDeleteAllPortals(ce *CommandEvent) { }() } -const cmdListHelp = `list groups [page] [items per page] - Get a list of all contacts and groups.` - -// //TODO +const cmdListHelp = `list [page] [items per page] - Get a list of all contacts and groups.` func formatContacts(contacts bool, input map[string]string) (result []string) { for jid, contact := range input { - if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) != contacts { - continue - } - - if contacts { - result = append(result, fmt.Sprintf("* %s / %s - `%s`", contact, jid)) - } else { - result = append(result, fmt.Sprintf("* %s - `%s`", contact, jid)) - } + result = append(result, fmt.Sprintf("* %s - `%s`", contact, jid)) } sort.Sort(sort.StringSlice(result)) return @@ -886,44 +876,36 @@ func (handler *CommandHandler) CommandOpen(ce *CommandEvent) { _, _ = portal.MainIntent().InviteUser(portal.MXID, &mautrix.ReqInviteUser{UserID: user.MXID}) } -const cmdPMHelp = `pm [--force] <_international phone number_> - Open a private chat with the given phone number.` +const cmdPMHelp = `pm - To direct message someone already in a shared group start a direct chat with them in Matrix` func (handler *CommandHandler) CommandPM(ce *CommandEvent) { - // if len(ce.Args) == 0 { - // ce.Reply("**Usage:** `pm [--force] `") - // return - // } + ce.Reply(fmt.Sprintf("**DEPRECATED COMMAND:** `%s`", cmdPMHelp)) + return + if len(ce.Args) == 0 { + ce.Reply(fmt.Sprintf("**DEPRECATED COMMAND:** `%s`", cmdPMHelp)) + return + } - // force := ce.Args[0] == "--force" - // if force { - // ce.Args = ce.Args[1:] - // } - - // user := ce.User - - // number := strings.Join(ce.Args, "") - // if number[0] == '+' { - // number = number[1:] - // } - // for _, char := range number { - // if char < '0' || char > '9' { - // ce.Reply("Invalid phone number.") - // return - // } - // } - // jid := number + whatsappExt.NewUserSuffix - - // handler.log.Debugln("Importing", jid, "for", user) - - // contact, ok := user.Conn.Store.Contacts[jid] - // if !ok { - // if !force { - // ce.Reply("Phone number not found in contacts. Try syncing contacts with `sync` first. " + - // "To create a portal anyway, use `pm --force `.") - // return - // } - // contact = whatsapp.Contact{Jid: jid} - // } + // force := ce.Args[0] == "--force" + // if force { + // ce.Args = ce.Args[1:] + // } + // + // user := ce.User + // + // jid := ce.Args[0] + // + // handler.log.Debugln("Importing", jid, "for", user.MXID) + // + // contact, ok := user.Conn.Store.Contacts[jid] + // if !ok { + // if !force { + // ce.Reply("Phone number not found in contacts. Try syncing contacts with `sync` first. " + + // "To create a portal anyway, use `pm --force `.") + // return + // } + // contact = whatsapp.Contact{Jid: jid} + // } // puppet := user.bridge.GetPuppetByJID(contact.Jid) // puppet.Sync(user, contact) // portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.Jid, user.JID)) diff --git a/database/database.go b/database/database.go index 26da8da..4555d6c 100644 --- a/database/database.go +++ b/database/database.go @@ -57,7 +57,7 @@ func New(dbType string, uri string, baseLog log.Logger) (*Database, error) { } gdb, err := gorm.Open(conn, &gorm.Config{ - //Logger: logger.Default.LogMode(logger.Info), + // Logger: logger.Default.LogMode(logger.Info), // Logger: baseLog, DisableForeignKeyConstraintWhenMigrating: true, diff --git a/database/portal.go b/database/portal.go index 28f9a7b..3beb5dd 100644 --- a/database/portal.go +++ b/database/portal.go @@ -24,6 +24,8 @@ import ( "github.com/karmanyaahm/matrix-groupme-go/types" ) +// JID is the puppet or the group +// Receiver is the "Other Person" in a DM or the group itself in a group type PortalKey struct { JID types.GroupMeID `gorm:"primaryKey"` Receiver types.GroupMeID `gorm:"primaryKey"` @@ -50,6 +52,11 @@ func (key PortalKey) String() string { return key.JID + "-" + key.Receiver } +func (key PortalKey) IsPrivate() bool { + //also see FindPrivateChats + return key.JID != key.Receiver +} + type PortalQuery struct { db *Database log log.Logger @@ -82,8 +89,8 @@ func (pq *PortalQuery) GetAllByJID(jid types.GroupMeID) []*Portal { } func (pq *PortalQuery) FindPrivateChats(receiver types.GroupMeID) []*Portal { - print("aaaaaaaaaaaaaaaaaa wrong portal stuff") - return pq.getAll(pq.db.DB.Where("receiver = ? AND jid LIKE '%@s.whatsapp.net'", receiver)) + //also see IsPrivate + return pq.getAll(pq.db.DB.Where("receiver = ? AND receiver <> jid", receiver)) } diff --git a/database/user.go b/database/user.go index 08e397c..8fdd95f 100644 --- a/database/user.go +++ b/database/user.go @@ -178,8 +178,8 @@ type UserPortal struct { func (user *User) SetPortalKeys(newKeys []PortalKeyWithMeta) error { tx := user.db.Begin() - ans := tx.Where("user_jid = ?", *user.jidPtr()).Delete(&UserPortal{}) - print("make sure all are deletede") + ans := tx.Where("user_jid = ?", *user.jidPtr()).Delete(UserPortal{}) + if ans.Error != nil { _ = tx.Rollback() return ans.Error diff --git a/groupmeExt/client.go b/groupmeExt/client.go index 527493e..e3aab56 100644 --- a/groupmeExt/client.go +++ b/groupmeExt/client.go @@ -25,30 +25,61 @@ func (c Client) IndexAllGroups() ([]*groupme.Group, error) { }) } -func (c Client) LoadMessagesAfter(groupID, lastMessageID string, lastMessageFromMe bool, num int) ([]*groupme.Message, error) { - //TODO: limit max 100 - i, e := c.IndexMessages(context.TODO(), groupme.ID(groupID), &groupme.IndexMessagesQuery{ - AfterID: groupme.ID(lastMessageID), - Limit: num, +func (c Client) IndexAllChats() ([]*groupme.Chat, error) { + return c.IndexChats(context.TODO(), &groupme.IndexChatsQuery{ + PerPage: 100, //TODO? }) - - if e != nil { - return nil, e - } - return i.Messages, nil } -func (c Client) LoadMessagesBefore(groupID, lastMessageID string, num int) ([]*groupme.Message, error) { - //TODO: limit max 100 - i, e := c.IndexMessages(context.TODO(), groupme.ID(groupID), &groupme.IndexMessagesQuery{ - BeforeID: groupme.ID(lastMessageID), - Limit: num, - }) - //fmt.Println(groupID, lastMessageID, num, i.Count, e) - if e != nil { - return nil, e +func (c Client) LoadMessagesAfter(groupID, lastMessageID string, lastMessageFromMe bool, private bool) ([]*groupme.Message, error) { + if private { + i, e := c.IndexDirectMessages(context.TODO(), groupID, &groupme.IndexDirectMessagesQuery{ + SinceID: groupme.ID(lastMessageID), + //Limit: num, + }) + //fmt.Println(groupID, lastMessageID, num, i.Count, e) + if e != nil { + return nil, e + } + return i.Messages, nil + } else { + i, e := c.IndexMessages(context.TODO(), groupme.ID(groupID), &groupme.IndexMessagesQuery{ + AfterID: groupme.ID(lastMessageID), + //20 for consistency with dms + Limit: 20, + }) + //fmt.Println(groupID, lastMessageID, num, i.Count, e) + if e != nil { + return nil, e + } + return i.Messages, nil + } +} + +func (c Client) LoadMessagesBefore(groupID, lastMessageID string, private bool) ([]*groupme.Message, error) { + if private { + i, e := c.IndexDirectMessages(context.TODO(), groupID, &groupme.IndexDirectMessagesQuery{ + BeforeID: groupme.ID(lastMessageID), + //Limit: num, + }) + //fmt.Println(groupID, lastMessageID, num, i.Count, e) + if e != nil { + return nil, e + } + return i.Messages, nil + } else { + //TODO: limit max 100 + i, e := c.IndexMessages(context.TODO(), groupme.ID(groupID), &groupme.IndexMessagesQuery{ + BeforeID: groupme.ID(lastMessageID), + //20 for consistency with dms + Limit: 20, + }) + //fmt.Println(groupID, lastMessageID, num, i.Count, e) + if e != nil { + return nil, e + } + return i.Messages, nil } - return i.Messages, nil } func (c *Client) RemoveFromGroup(uid, groupID types.GroupMeID) error { diff --git a/portal.go b/portal.go index 8f2ea7f..efceb9e 100644 --- a/portal.go +++ b/portal.go @@ -186,7 +186,6 @@ type Portal struct { messages chan PortalMessage - isPrivate *bool hasRelaybot *bool } @@ -549,7 +548,12 @@ func (portal *Portal) Sync(user *User, group groupme.Group) { portal.hasRelaybot = &yes } - err := user.Conn.SubscribeToGroup(context.TODO(), group.ID, user.Token) + var err error + if portal.IsPrivateChat() { + err = user.Conn.SubscribeToUser(context.TODO(), groupme.ID(portal.Key.JID), user.Token) + } else { + err = user.Conn.SubscribeToGroup(context.TODO(), groupme.ID(portal.Key.JID), user.Token) + } if err != nil { portal.log.Errorln("Subscribing failed, live metadata updates won't work", err) } @@ -698,7 +702,7 @@ func (portal *Portal) BackfillHistory(user *User, lastMessageTime uint64) error portal.log.Infoln("Backfilling history since", lastMessageID, "for", user.MXID) for len(lastMessageID) > 0 { portal.log.Debugln("Fetching 50 messages of history after", lastMessageID) - messages, err := user.Client.LoadMessagesAfter(portal.Key.JID, lastMessageID, lastMessageFromMe, 50) + messages, err := user.Client.LoadMessagesAfter(portal.Key.JID, lastMessageID, lastMessageFromMe, portal.IsPrivateChat()) if err != nil { return err } @@ -799,12 +803,12 @@ func (portal *Portal) FillInitialHistory(user *User) error { before := "" chunkNum := 1 for n > 0 { - count := 50 + count := 20 if n < count { count = n } portal.log.Debugfln("Fetching chunk %d (%d messages / %d cap) before message %s", chunkNum, count, n, before) - chunk, err := user.Client.LoadMessagesBefore(portal.Key.JID, before, count) + chunk, err := user.Client.LoadMessagesBefore(portal.Key.JID, before, portal.IsPrivateChat()) if err != nil { return err } @@ -1062,11 +1066,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { } func (portal *Portal) IsPrivateChat() bool { - if portal.isPrivate == nil { - val := strings.HasSuffix(portal.Key.JID, whatsappExt.NewUserSuffix) - portal.isPrivate = &val - } - return *portal.isPrivate + return portal.Key.IsPrivate() } func (portal *Portal) HasRelaybot() bool { diff --git a/user.go b/user.go index 1486f3b..f41af4f 100644 --- a/user.go +++ b/user.go @@ -378,9 +378,15 @@ func (user *User) Login(ce *CommandEvent) { type Chat struct { Portal *Portal LastMessageTime uint64 - Group groupme.Group + Group *groupme.Group + DM *groupme.Chat } +////returns private chat assuming one of group or dm have been initialized properly +//func (c Chat) IsPrivate() bool { +// return c.Group == nil +//} + type ChatList []Chat func (cl ChatList) Len() int { @@ -536,44 +542,67 @@ func (user *User) HandleChatList() { chatMap := make(map[string]groupme.Group) chats, err := user.Client.IndexAllGroups() if err != nil { - log.Fatal(err) //TODO: handle + user.log.Errorln("chat sync error", err) //TODO: handle + return } for _, chat := range chats { chatMap[chat.ID.String()] = *chat } - user.chatListReceived <- struct{}{} - user.log.Infoln("Chat list received") user.GroupList = chatMap - go user.syncPortals(chatMap, false) + + dmMap := make(map[string]groupme.Chat) + dms, err := user.Client.IndexAllChats() + if err != nil { + user.log.Errorln("chat sync error", err) //TODO: handle + return + } + for _, dm := range dms { + dmMap[dm.OtherUser.ID.String()] = *dm + } + user.ChatList = dmMap + + user.log.Infoln("Chat list received") + user.chatListReceived <- struct{}{} + go user.syncPortals(false) } -func (user *User) syncPortals(chatMap map[string]groupme.Group, createAll bool) { - if chatMap == nil { - // chatMap = user.Conn.Store.Chats - log.Fatal("chatmap nil major oops") - } +func (user *User) syncPortals(createAll bool) { + user.log.Infoln("Reading chat list") - chats := make(ChatList, 0, len(chatMap)) + chats := make(ChatList, 0, len(user.GroupList)+len(user.ChatList)) existingKeys := user.GetInCommunityMap() - portalKeys := make([]database.PortalKeyWithMeta, 0, len(chatMap)) - for _, chat := range chatMap { - portal := user.bridge.GetPortalByJID(database.GroupPortalKey(chat.ID.String())) + portalKeys := make([]database.PortalKeyWithMeta, 0, cap(chats)) + + for _, group := range user.GroupList { + + portal := user.bridge.GetPortalByJID(database.GroupPortalKey(group.ID.String())) chats = append(chats, Chat{ Portal: portal, - LastMessageTime: uint64(chat.UpdatedAt.ToTime().Unix()), - Group: chat, + LastMessageTime: uint64(group.UpdatedAt.ToTime().Unix()), + Group: &group, }) + } + for _, dm := range user.ChatList { + portal := user.bridge.GetPortalByJID(database.NewPortalKey(dm.OtherUser.ID.String(), user.JID)) + chats = append(chats, Chat{ + Portal: portal, + LastMessageTime: uint64(dm.UpdatedAt.ToTime().Unix()), + DM: &dm, + }) + } + + for _, chat := range chats { var inCommunity, ok bool - if inCommunity, ok = existingKeys[portal.Key]; !ok || !inCommunity { - inCommunity = user.addPortalToCommunity(portal) - if portal.IsPrivateChat() { - puppet := user.bridge.GetPuppetByJID(portal.Key.JID) + if inCommunity, ok = existingKeys[chat.Portal.Key]; !ok || !inCommunity { + inCommunity = user.addPortalToCommunity(chat.Portal) + if chat.Portal.IsPrivateChat() { + puppet := user.bridge.GetPuppetByJID(chat.Portal.Key.JID) user.addPuppetToCommunity(puppet) } } - portalKeys = append(portalKeys, database.PortalKeyWithMeta{PortalKey: portal.Key, InCommunity: inCommunity}) + portalKeys = append(portalKeys, database.PortalKeyWithMeta{PortalKey: chat.Portal.Key, InCommunity: inCommunity}) } user.log.Infoln("Read chat list, updating user-portal mapping") @@ -594,11 +623,11 @@ func (user *User) syncPortals(chatMap map[string]groupme.Group, createAll bool) if chat.LastMessageTime+user.bridge.Config.Bridge.SyncChatMaxAge < now { break } + wg.Add(1) go func(chat Chat) { - wg.Add(1) create := (chat.LastMessageTime >= user.LastConnection && user.LastConnection > 0) || i < limit if len(chat.Portal.MXID) > 0 || create || createAll { - chat.Portal.Sync(user, chat.Group) + chat.Portal.Sync(user, *chat.Group) err := chat.Portal.BackfillHistory(user, chat.LastMessageTime) if err != nil { chat.Portal.log.Errorln("Error backfilling history:", err)