v2: fix things

This commit is contained in:
Tulir Asokan 2024-09-10 15:56:51 +03:00
parent 91fbf6f609
commit 3c53ea5ed1
7 changed files with 136 additions and 92 deletions

View file

@ -8,7 +8,7 @@ INSERT INTO user_login (bridge_id, user_mxid, id, remote_name, space_room, metad
SELECT
'', -- bridge_id
mxid, -- user_mxid
username || '@s.whatsapp.net', -- id
username, -- id
'+' || username, -- remote_name
space_room,
-- only: postgres

View file

@ -2,16 +2,13 @@ package connector
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/rs/zerolog"
"go.mau.fi/whatsmeow"
"go.mau.fi/whatsmeow/store"
"go.mau.fi/whatsmeow/types"
"maunium.net/go/mautrix"
waLog "go.mau.fi/whatsmeow/util/log"
"maunium.net/go/mautrix/bridge/status"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/networkid"
@ -19,57 +16,35 @@ import (
"maunium.net/go/mautrix-whatsapp/pkg/waid"
)
const (
WANotLoggedIn status.BridgeStateErrorCode = "wa-not-logged-in"
)
func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.UserLogin) error {
loginMetadata := login.Metadata.(*UserLoginMetadata)
type respGetProxy struct {
ProxyURL string `json:"proxy_url"`
}
jid := waid.ParseUserLoginID(login.ID, loginMetadata.WADeviceID)
func (wa *WhatsAppConnector) getProxy(reason string) (string, error) {
if wa.Config.GetProxyURL == "" {
return wa.Config.Proxy, nil
}
parsed, err := url.Parse(wa.Config.GetProxyURL)
device, err := wa.DeviceStore.GetDevice(jid)
if err != nil {
return "", fmt.Errorf("failed to parse address: %w", err)
return err
}
q := parsed.Query()
q.Set("reason", reason)
parsed.RawQuery = q.Encode()
req, err := http.NewRequest(http.MethodGet, parsed.String(), nil)
if err != nil {
return "", fmt.Errorf("failed to prepare request: %w", err)
}
req.Header.Set("User-Agent", mautrix.DefaultUserAgent)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send request: %w", err)
} else if resp.StatusCode >= 300 || resp.StatusCode < 200 {
return "", fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
var respData respGetProxy
err = json.NewDecoder(resp.Body).Decode(&respData)
if err != nil {
return "", fmt.Errorf("failed to decode response: %w", err)
}
return respData.ProxyURL, nil
}
func (wa *WhatsAppConnector) updateProxy(client *whatsmeow.Client, isLogin bool) error {
if wa.Config.ProxyOnlyLogin && !isLogin {
return nil
w := &WhatsAppClient{
Main: wa,
UserLogin: login,
Device: device,
JID: jid,
}
reason := "connect"
if isLogin {
reason = "login"
}
if proxy, err := wa.getProxy(reason); err != nil {
return fmt.Errorf("failed to get proxy address: %w", err)
} else if err = client.SetProxyAddress(proxy); err != nil {
return fmt.Errorf("failed to set proxy address: %w", err)
if device != nil {
log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger()
w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log))
w.Client.AddEventHandler(w.handleWAEvent)
w.Client.AutomaticMessageRerequestFromPhone = true
w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts)
} else {
w.UserLogin.Log.Debug().Stringer("jid", jid).Msg("No device found for user in whatsmeow store")
}
login.Client = w
return nil
}
@ -81,16 +56,7 @@ type WhatsAppClient struct {
JID types.JID
}
var (
_ bridgev2.NetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.TypingHandlingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.IdentifierResolvingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.GroupCreatingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.ContactListingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.RoomNameHandlingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.RoomAvatarHandlingNetworkAPI = (*WhatsAppClient)(nil)
//_ bridgev2.RoomTopicHandlingNetworkAPI = (*WhatsAppClient)(nil)
)
var _ bridgev2.NetworkAPI = (*WhatsAppClient)(nil)
var pushCfg = &bridgev2.PushConfig{
Web: &bridgev2.WebPushConfig{},
@ -134,7 +100,9 @@ func (wa *WhatsAppClient) Connect(ctx context.Context) error {
}
func (wa *WhatsAppClient) Disconnect() {
wa.Client.Disconnect()
if wa.Client != nil {
wa.Client.Disconnect()
}
}
func (wa *WhatsAppClient) LogoutRemote(ctx context.Context) {

View file

@ -107,6 +107,8 @@ func upgradeConfig(helper up.Helper) {
type DisplaynameParams struct {
types.ContactInfo
Phone string
// Deprecated form of Phone
JID string
}
func (c *Config) FormatDisplayname(jid types.JID, contact types.ContactInfo) string {
@ -114,6 +116,7 @@ func (c *Config) FormatDisplayname(jid types.JID, contact types.ContactInfo) str
err := c.displaynameTemplate.Execute(&nameBuf, &DisplaynameParams{
ContactInfo: contact,
Phone: "+" + jid.User,
JID: "+" + jid.User,
})
if err != nil {
panic(err)

View file

@ -14,7 +14,6 @@ import (
"maunium.net/go/mautrix-whatsapp/pkg/connector/wadb"
"maunium.net/go/mautrix-whatsapp/pkg/msgconv"
"maunium.net/go/mautrix-whatsapp/pkg/waid"
)
type WhatsAppConnector struct {
@ -91,34 +90,3 @@ func (wa *WhatsAppConnector) Start(ctx context.Context) error {
}
return nil
}
func (wa *WhatsAppConnector) LoadUserLogin(_ context.Context, login *bridgev2.UserLogin) error {
loginMetadata := login.Metadata.(*UserLoginMetadata)
jid := waid.ParseUserLoginID(login.ID, loginMetadata.WADeviceID)
device, err := wa.DeviceStore.GetDevice(jid)
if err != nil {
return err
}
w := &WhatsAppClient{
Main: wa,
UserLogin: login,
Device: device,
JID: jid,
}
log := w.UserLogin.Log.With().Str("component", "whatsmeow").Logger()
if device != nil {
w.Client = whatsmeow.NewClient(w.Device, waLog.Zerolog(log))
w.Client.AddEventHandler(w.handleWAEvent)
w.Client.AutomaticMessageRerequestFromPhone = true
w.Client.SetForceActiveDeliveryReceipts(wa.Config.ForceActiveDeliveryReceipts)
}
login.Client = w
return nil
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"time"
"go.mau.fi/whatsmeow/appstate"
"go.mau.fi/whatsmeow/types"
"go.mau.fi/whatsmeow/types/events"
"maunium.net/go/mautrix/bridge/status"
@ -15,6 +16,7 @@ import (
)
const (
WANotLoggedIn status.BridgeStateErrorCode = "wa-not-logged-in"
WALoggedOut status.BridgeStateErrorCode = "wa-logged-out"
WAMainDeviceGone status.BridgeStateErrorCode = "wa-main-device-gone"
WAUnknownLogout status.BridgeStateErrorCode = "wa-unknown-logout"
@ -111,6 +113,42 @@ func (wa *WhatsAppClient) handleWAEvent(rawEvt any) {
case *events.Connected:
log.Debug().Msg("Connected to WhatsApp socket")
wa.UserLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected})
if len(wa.Client.Store.PushName) > 0 {
go func() {
err := wa.Client.SendPresence(types.PresenceUnavailable)
if err != nil {
log.Warn().Err(err).Msg("Failed to send initial presence after connecting")
}
}()
}
case *events.AppStateSyncComplete:
if len(wa.Client.Store.PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock {
err := wa.Client.SendPresence(types.PresenceUnavailable)
if err != nil {
log.Warn().Err(err).Msg("Failed to send presence after app state sync")
}
} else if evt.Name == appstate.WAPatchCriticalUnblockLow {
go func() {
// TODO resync contacts
//err := user.ResyncContacts(false)
//if err != nil {
// user.zlog.Err(err).Msg("Failed to resync contacts after app state sync")
//}
}()
}
case *events.PushNameSetting:
// Send presence available when connecting and when the pushname is changed.
// This makes sure that outgoing messages always have the right pushname.
err := wa.Client.SendPresence(types.PresenceUnavailable)
if err != nil {
log.Warn().Err(err).Msg("Failed to send presence after push name update")
}
_, _, err = wa.Client.Store.Contacts.PutPushName(wa.JID.ToNonAD(), evt.Action.GetName())
if err != nil {
log.Err(err).Msg("Failed to update push name in store")
}
// TODO update own ghost info
//go user.syncPuppet(user.JID.ToNonAD(), "push name setting")
case *events.Disconnected:
// Don't send the normal transient disconnect state if we're already in a different transient disconnect state.
// TODO remove this if/when the phone offline state is moved to a sub-state of CONNECTED

View file

@ -20,6 +20,10 @@ func (wa *WhatsAppClient) makeWAPortalKey(chatJID types.JID) (key networkid.Port
}
func (wa *WhatsAppClient) makeEventSender(id types.JID) bridgev2.EventSender {
if id.Server == types.NewsletterServer {
// Send as bot
return bridgev2.EventSender{}
}
return bridgev2.EventSender{
IsFromMe: waid.MakeUserLoginID(id) == wa.UserLogin.ID,
Sender: waid.MakeUserID(id),

63
pkg/connector/proxy.go Normal file
View file

@ -0,0 +1,63 @@
package connector
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"go.mau.fi/whatsmeow"
"maunium.net/go/mautrix"
)
// TODO move proxy stuff to mautrix-go
type respGetProxy struct {
ProxyURL string `json:"proxy_url"`
}
func (wa *WhatsAppConnector) getProxy(reason string) (string, error) {
if wa.Config.GetProxyURL == "" {
return wa.Config.Proxy, nil
}
parsed, err := url.Parse(wa.Config.GetProxyURL)
if err != nil {
return "", fmt.Errorf("failed to parse address: %w", err)
}
q := parsed.Query()
q.Set("reason", reason)
parsed.RawQuery = q.Encode()
req, err := http.NewRequest(http.MethodGet, parsed.String(), nil)
if err != nil {
return "", fmt.Errorf("failed to prepare request: %w", err)
}
req.Header.Set("User-Agent", mautrix.DefaultUserAgent)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send request: %w", err)
} else if resp.StatusCode >= 300 || resp.StatusCode < 200 {
return "", fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
var respData respGetProxy
err = json.NewDecoder(resp.Body).Decode(&respData)
if err != nil {
return "", fmt.Errorf("failed to decode response: %w", err)
}
return respData.ProxyURL, nil
}
func (wa *WhatsAppConnector) updateProxy(client *whatsmeow.Client, isLogin bool) error {
if wa.Config.ProxyOnlyLogin && !isLogin {
return nil
}
reason := "connect"
if isLogin {
reason = "login"
}
if proxy, err := wa.getProxy(reason); err != nil {
return fmt.Errorf("failed to get proxy address: %w", err)
} else if err = client.SetProxyAddress(proxy); err != nil {
return fmt.Errorf("failed to set proxy address: %w", err)
}
return nil
}