diff --git a/commands.go b/commands.go index 9980b68..4ff1cf3 100644 --- a/commands.go +++ b/commands.go @@ -274,7 +274,7 @@ func (handler *CommandHandler) CommandPM(ce *CommandEvent) { } return } - err := portal.CreateMatrixRoom([]string{user.MXID}) + err := portal.CreateMatrixRoom(user) if err != nil { ce.Reply(fmt.Sprintf("Failed to create portal room: %v", err)) return diff --git a/example-config.yaml b/example-config.yaml index b2a5e4d..bf3461e 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -1,85 +1,86 @@ # Homeserver details. homeserver: - # The address that this appservice can use to connect to the homeserver. - address: https://matrix.org - # The domain of the homeserver (for MXIDs, etc). - domain: matrix.org + # The address that this appservice can use to connect to the homeserver. + address: https://matrix.org + # The domain of the homeserver (for MXIDs, etc). + domain: matrix.org # Application service host/registration related details. # Changing these values requires regeneration of the registration. appservice: - # The address that the homeserver can use to connect to this appservice. - address: http://localhost:8080 + # The address that the homeserver can use to connect to this appservice. + address: http://localhost:8080 - # The hostname and port where this appservice should listen. - hostname: 0.0.0.0 - port: 8080 + # The hostname and port where this appservice should listen. + hostname: 0.0.0.0 + port: 8080 - # Database config. - database: - # The database type. Only "sqlite3" is supported. - type: sqlite3 - # The database URI. Usually file name. https://github.com/mattn/go-sqlite3#connection-string - # postres example: postgres://synapse:changeme@db/whatsapp?sslmode=disable - uri: mautrix-whatsapp.db - # Path to the Matrix room state store. - state_store_path: ./mx-state.json + # Database config. + database: + # The database type. "sqlite3" and "postgres" are supported. + type: sqlite3 + # The database URI. + # SQLite: File name is enough. https://github.com/mattn/go-sqlite3#connection-string + # Postgres: Connection string. For example, postgres://user:password@host/database + uri: mautrix-whatsapp.db + # Path to the Matrix room state store. + state_store_path: ./mx-state.json - # The unique ID of this appservice. - id: whatsapp - # Appservice bot details. - bot: - # Username of the appservice bot. - username: whatsappbot - # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty - # to leave display name/avatar as-is. - displayname: WhatsApp bridge bot - avatar: mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr + # The unique ID of this appservice. + id: whatsapp + # Appservice bot details. + bot: + # Username of the appservice bot. + username: whatsappbot + # Display name and avatar for bot. Set to "remove" to remove display name/avatar, leave empty + # to leave display name/avatar as-is. + displayname: WhatsApp bridge bot + avatar: mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr - # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify. - as_token: "This value is generated when generating the registration" - hs_token: "This value is generated when generating the registration" + # Authentication tokens for AS <-> HS communication. Autogenerated; do not modify. + as_token: "This value is generated when generating the registration" + hs_token: "This value is generated when generating the registration" # Bridge config. Currently unused. bridge: - # Localpart template of MXIDs for WhatsApp users. - # {{.}} is replaced with the phone number of the WhatsApp user. - username_template: whatsapp_{{.}} - # Displayname template for WhatsApp users. - # {{.Notify}} - nickname set by the WhatsApp user - # {{.Jid}} - phone number (international format) - # The following variables are also available, but will cause problems on multi-user instances: - # {{.Name}} - display name from contact list - # {{.Short}} - short display name from contact list - displayname_template: "{{if .Notify}}{{.Notify}}{{else}}{{.Jid}}{{end}} (WA)" + # Localpart template of MXIDs for WhatsApp users. + # {{.}} is replaced with the phone number of the WhatsApp user. + username_template: whatsapp_{{.}} + # Displayname template for WhatsApp users. + # {{.Notify}} - nickname set by the WhatsApp user + # {{.Jid}} - phone number (international format) + # The following variables are also available, but will cause problems on multi-user instances: + # {{.Name}} - display name from contact list + # {{.Short}} - short display name from contact list + displayname_template: "{{if .Notify}}{{.Notify}}{{else}}{{.Jid}}{{end}} (WA)" - # The prefix for commands. Only required in non-management rooms. - command_prefix: "!wa" + # The prefix for commands. Only required in non-management rooms. + command_prefix: "!wa" - # Permissions for using the bridge. - # Permitted values: - # user - Access to use the bridge to chat with a WhatsApp account. - # admin - User level and some additional administration tools - # Permitted keys: - # * - All Matrix users - # domain - All users on that homeserver - # mxid - Specific user - permissions: - "example.com": user - "@admin:example.com": admin + # Permissions for using the bridge. + # Permitted values: + # user - Access to use the bridge to chat with a WhatsApp account. + # admin - User level and some additional administration tools + # Permitted keys: + # * - All Matrix users + # domain - All users on that homeserver + # mxid - Specific user + permissions: + "example.com": user + "@admin:example.com": admin # Logging config. logging: - # The directory for log files. Will be created if not found. - directory: ./logs - # Available variables: .Date for the file date and .Index for different log files on the same day. - file_name_format: "{{.Date}}-{{.Index}}.log" - # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants - file_date_format: 2006-01-02 - # Log file permissions. - file_mode: 0600 - # Timestamp format for log entries in the Go time format. - timestamp_format: Jan _2, 2006 15:04:05 - # Minimum severity for log messages. - # Options: debug, info, warn, error, fatal - print_level: debug + # The directory for log files. Will be created if not found. + directory: ./logs + # Available variables: .Date for the file date and .Index for different log files on the same day. + file_name_format: "{{.Date}}-{{.Index}}.log" + # Date format for file names in the Go time format: https://golang.org/pkg/time/#pkg-constants + file_date_format: 2006-01-02 + # Log file permissions. + file_mode: 0600 + # Timestamp format for log entries in the Go time format. + timestamp_format: Jan _2, 2006 15:04:05 + # Minimum severity for log messages. + # Options: debug, info, warn, error, fatal + print_level: debug diff --git a/go.mod b/go.mod index 7c3e154..abd813d 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f // indirect github.com/Rhymen/go-whatsapp v0.0.0-20190208090600-c1173899de99 github.com/gorilla/mux v1.7.0 // indirect + github.com/lib/pq v1.0.0 github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-sqlite3 v1.10.0 github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect diff --git a/go.sum b/go.sum index 31d2a14..e70bba7 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= diff --git a/portal.go b/portal.go index 0214bb3..8e58f4f 100644 --- a/portal.go +++ b/portal.go @@ -300,7 +300,10 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsappExt.ProfilePicInf func (portal *Portal) UpdateName(name string, setBy types.WhatsAppID) bool { if portal.Name != name { - intent := portal.bridge.GetPuppetByJID(setBy).Intent() + intent := portal.MainIntent() + if len(setBy) > 0 { + intent = portal.bridge.GetPuppetByJID(setBy).Intent() + } _, err := intent.SetRoomName(portal.MXID, name) if err == nil { portal.Name = name @@ -313,7 +316,10 @@ func (portal *Portal) UpdateName(name string, setBy types.WhatsAppID) bool { func (portal *Portal) UpdateTopic(topic string, setBy types.WhatsAppID) bool { if portal.Topic != topic { - intent := portal.bridge.GetPuppetByJID(setBy).Intent() + intent := portal.MainIntent() + if len(setBy) > 0 { + intent = portal.bridge.GetPuppetByJID(setBy).Intent() + } _, err := intent.SetRoomTopic(portal.MXID, topic) if err == nil { portal.Topic = topic @@ -325,6 +331,14 @@ func (portal *Portal) UpdateTopic(topic string, setBy types.WhatsAppID) bool { } func (portal *Portal) UpdateMetadata(user *User) bool { + if portal.IsPrivateChat() { + return false + } else if portal.IsStatusBroadcastRoom() { + update := false + update = portal.UpdateName("WhatsApp Status Broadcast", "") || update + update = portal.UpdateTopic("WhatsApp status updates from your contacts", "") || update + return update + } metadata, err := user.Conn.GetGroupMetaData(portal.Key.JID) if err != nil { portal.log.Errorln(err) @@ -354,7 +368,7 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) { if len(portal.MXID) == 0 { portal.Name = contact.Name - err := portal.CreateMatrixRoom([]string{user.MXID}) + err := portal.CreateMatrixRoom(user) if err != nil { portal.log.Errorln("Failed to create portal room:", err) return @@ -368,7 +382,9 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) { update := false update = portal.UpdateMetadata(user) || update - update = portal.UpdateAvatar(user, nil) || update + if !portal.IsStatusBroadcastRoom() { + update = portal.UpdateAvatar(user, nil) || update + } if update { portal.Update() } @@ -459,7 +475,7 @@ func (portal *Portal) RestrictMetadataChanges(restrict bool) { } } -func (portal *Portal) CreateMatrixRoom(invite []string) error { +func (portal *Portal) CreateMatrixRoom(user *User) error { portal.roomCreateLock.Lock() defer portal.roomCreateLock.Unlock() if len(portal.MXID) > 0 { @@ -471,20 +487,27 @@ func (portal *Portal) CreateMatrixRoom(invite []string) error { return err } - name := portal.Name - topic := portal.Topic isPrivateChat := false if portal.IsPrivateChat() { - name = "" - topic = "WhatsApp private chat" + portal.Name = "" + portal.Topic = "WhatsApp private chat" isPrivateChat = true + } else if portal.IsStatusBroadcastRoom() { + portal.Name = "WhatsApp Status Broadcast" + portal.Topic = "WhatsApp status updates from your contacts" + } else { + metadata, err := user.Conn.GetGroupMetaData(portal.Key.JID) + if err == nil && metadata.Status == 0 { + portal.Name = metadata.Name + portal.Topic = metadata.Topic + } } resp, err := intent.CreateRoom(&mautrix.ReqCreateRoom{ Visibility: "private", - Name: name, - Topic: topic, - Invite: invite, + Name: portal.Name, + Topic: portal.Topic, + Invite: []string{user.MXID}, Preset: "private_chat", IsDirect: isPrivateChat, InitialState: []*mautrix.Event{{ @@ -510,6 +533,10 @@ func (portal *Portal) IsPrivateChat() bool { return *portal.isPrivate } +func (portal *Portal) IsStatusBroadcastRoom() bool { + return portal.Key.JID == "status@broadcast" +} + func (portal *Portal) MainIntent() *appservice.IntentAPI { if portal.IsPrivateChat() { return portal.bridge.GetPuppetByJID(portal.Key.JID).Intent() @@ -560,7 +587,7 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa } defer lock.Unlock() - err := portal.CreateMatrixRoom([]string{source.MXID}) + err := portal.CreateMatrixRoom(source) if err != nil { portal.log.Errorln("Failed to create portal room:", err) return @@ -595,7 +622,7 @@ func (portal *Portal) HandleMediaMessage(source *User, download func() ([]byte, } defer lock.Unlock() - err := portal.CreateMatrixRoom([]string{source.MXID}) + err := portal.CreateMatrixRoom(source) if err != nil { portal.log.Errorln("Failed to create portal room:", err) return