diff --git a/commands.go b/commands.go index a0e1817..9980b68 100644 --- a/commands.go +++ b/commands.go @@ -17,9 +17,11 @@ package main import ( - "strings" - + "fmt" "github.com/Rhymen/go-whatsapp" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/format" + "strings" "maunium.net/go/maulogger/v2" "maunium.net/go/mautrix-appservice" @@ -54,7 +56,9 @@ type CommandEvent struct { // Reply sends a reply to command as notice func (ce *CommandEvent) Reply(msg string) { - _, err := ce.Bot.SendNotice(string(ce.User.ManagementRoom), msg) + content := format.RenderMarkdown(msg) + content.MsgType = mautrix.MsgNotice + _, err := ce.Bot.SendMessageEvent(ce.User.ManagementRoom, mautrix.EventMessage, content) if err != nil { ce.Handler.log.Warnfln("Failed to reply to command from %s: %v", ce.User.MXID, err) } @@ -79,8 +83,14 @@ func (handler *CommandHandler) Handle(roomID types.MatrixRoomID, user *User, mes handler.CommandLogout(ce) case "help": handler.CommandHelp(ce) - case "import": - handler.CommandImport(ce) + case "sync": + handler.CommandSync(ce) + case "list": + handler.CommandList(ce) + case "open": + handler.CommandOpen(ce) + case "pm": + handler.CommandPM(ce) default: ce.Reply("Unknown Command") } @@ -128,53 +138,146 @@ func (handler *CommandHandler) CommandHelp(ce *CommandEvent) { cmdPrefix = handler.bridge.Config.Bridge.CommandPrefix + " " } - ce.Reply(strings.Join([]string{ + ce.Reply("* " + strings.Join([]string{ cmdPrefix + cmdHelpHelp, cmdPrefix + cmdLoginHelp, cmdPrefix + cmdLogoutHelp, - cmdPrefix + cmdImportHelp, - }, "\n")) + cmdPrefix + cmdSyncHelp, + cmdPrefix + cmdListHelp, + cmdPrefix + cmdOpenHelp, + cmdPrefix + cmdPMHelp, + }, "\n* ")) } -const cmdImportHelp = `import |contacts - Open up a room for JID or for each WhatsApp contact` +const cmdSyncHelp = `sync [--create] - Synchronize contacts from phone and optionally create portals for group chats.` -// CommandImport handles import command -func (handler *CommandHandler) CommandImport(ce *CommandEvent) { - // ensure all messages go to the management room - ce.RoomID = ce.User.ManagementRoom +// CommandSync handles sync command +func (handler *CommandHandler) CommandSync(ce *CommandEvent) { + user := ce.User + create := len(ce.Args) > 0 && ce.Args[0] == "--create" - if len(ce.Args) == 0 { - ce.Reply("Usage: import |contacts") + handler.log.Debugln("Importing all contacts of", user) + _, err := user.Conn.Contacts() + if err != nil { + handler.log.Errorln("Error on update of contacts of user", user, ":", err) return } + for jid, contact := range user.Conn.Store.Contacts { + if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { + puppet := user.bridge.GetPuppetByJID(contact.Jid) + puppet.Sync(user, contact) + } else { + portal := user.bridge.GetPortalByJID(database.GroupPortalKey(contact.Jid)) + if len(portal.MXID) > 0 || create { + portal.Sync(user, contact) + } + } + } + + ce.Reply("Imported contacts successfully.") +} + +const cmdListHelp = `list - Get a list of all contacts and groups.` + +func (handler *CommandHandler) CommandList(ce *CommandEvent) { + var contacts strings.Builder + var groups strings.Builder + for jid, contact := range ce.User.Conn.Store.Contacts { + if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { + _, _ = fmt.Fprintf(&contacts, "* %s / %s - `%s`\n", contact.Name, contact.Notify, contact.Jid[:len(contact.Jid)-len(whatsappExt.NewUserSuffix)]) + } else { + _, _ = fmt.Fprintf(&groups, "* %s - `%s`\n", contact.Name, contact.Jid) + } + } + ce.Reply(fmt.Sprintf("### Contacts\n%s\n\n### Groups\n%s", contacts.String(), groups.String())) +} + +const cmdOpenHelp = `open <_group JID_> - Open a group chat portal.` + +func (handler *CommandHandler) CommandOpen(ce *CommandEvent) { + if len(ce.Args) == 0 { + ce.Reply("**Usage:** `open `") + } + + user := ce.User + jid := ce.Args[0] + + if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { + ce.Reply(fmt.Sprintf("That looks like a user JID. Did you mean `pm %s`?", jid[:len(jid)-len(whatsappExt.NewUserSuffix)])) + return + } + + contact, ok := user.Conn.Store.Contacts[jid] + if !ok { + ce.Reply("Group JID not found in contacts. Try syncing contacts with `sync` first.") + return + } + handler.log.Debugln("Importing", jid, "for", user) + portal := user.bridge.GetPortalByJID(database.GroupPortalKey(jid)) + if len(portal.MXID) > 0 { + portal.Sync(user, contact) + ce.Reply("Portal room synced.") + } else { + portal.Sync(user, contact) + ce.Reply("Portal room created.") + } +} + +const cmdPMHelp = `pm [--force] <_international phone number_> - Open a private chat with the given phone number.` + +func (handler *CommandHandler) CommandPM(ce *CommandEvent) { + if len(ce.Args) == 0 { + ce.Reply("**Usage:** `pm [--force] `") + return + } + + force := ce.Args[0] == "--force" + if force { + ce.Args = ce.Args[1:] + } + user := ce.User - if ce.Args[0] == "contacts" { - handler.log.Debugln("Importing all contacts of", user) - _, err := user.Conn.Contacts() - if err != nil { - handler.log.Errorln("Error on update of contacts of user", user, ":", err) + 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 } - - for jid, contact := range user.Conn.Store.Contacts { - if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) { - puppet := user.bridge.GetPuppetByJID(contact.Jid) - puppet.Sync(user, contact) - } else { - portal := user.bridge.GetPortalByJID(database.GroupPortalKey(contact.Jid)) - portal.Sync(user, contact) - } - } - - ce.Reply("Importing all contacts done") - } else { - jid := ce.Args[0] + whatsappExt.NewUserSuffix - handler.log.Debugln("Importing", jid, "for", user) - puppet := user.bridge.GetPuppetByJID(jid) - - contact := whatsapp.Contact { Jid: jid } - puppet.Sync(user, contact) } + 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} + } + puppet := user.bridge.GetPuppetByJID(contact.Jid) + puppet.Sync(user, contact) + portal := user.bridge.GetPortalByJID(database.NewPortalKey(contact.Jid, user.JID)) + if len(portal.MXID) > 0 { + _, err := portal.MainIntent().InviteUser(portal.MXID, &mautrix.ReqInviteUser{UserID: user.MXID}) + if err != nil { + fmt.Println(err) + } else { + ce.Reply("Existing portal room found, invited you to it.") + } + return + } + err := portal.CreateMatrixRoom([]string{user.MXID}) + if err != nil { + ce.Reply(fmt.Sprintf("Failed to create portal room: %v", err)) + return + } + ce.Reply("Created portal room and invited you to it.") }