legacymigrate: fix things

This commit is contained in:
Tulir Asokan 2024-09-10 15:09:08 +03:00
parent 86ffca8a6b
commit 91fbf6f609
15 changed files with 311 additions and 38 deletions

2
.gitignore vendored
View file

@ -4,10 +4,10 @@
!.pre-commit-config.yaml !.pre-commit-config.yaml
!example-config.yaml !example-config.yaml
*.session
*.json *.json
*.db *.db
*.log *.log
*.bak
/mautrix-whatsapp /mautrix-whatsapp
/start /start

View file

@ -2,8 +2,10 @@ package main
import ( import (
_ "embed" _ "embed"
"strings"
up "go.mau.fi/util/configupgrade" up "go.mau.fi/util/configupgrade"
"go.mau.fi/util/dbutil/litestream"
"maunium.net/go/mautrix/bridgev2/bridgeconfig" "maunium.net/go/mautrix/bridgev2/bridgeconfig"
) )
@ -26,9 +28,41 @@ ALTER TABLE "user" RENAME TO user_old;
//go:embed legacymigrate.sql //go:embed legacymigrate.sql
var legacyMigrateCopyData string var legacyMigrateCopyData string
//lint:ignore U1000 - TODO use this function func init() {
litestream.Functions["split_part"] = func(input, delimiter string, partNum int) string {
// split_part is 1-indexed
partNum++
parts := strings.Split(input, delimiter)
if len(parts) <= partNum {
return ""
}
return parts[partNum]
}
}
func migrateLegacyConfig(helper up.Helper) { func migrateLegacyConfig(helper up.Helper) {
helper.Set(up.Str, "maunium.net/go/mautrix-whatsapp", "encryption", "pickle_key") helper.Set(up.Str, "maunium.net/go/mautrix-whatsapp", "encryption", "pickle_key")
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "os_name"}, []string{"network", "os_name"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "browser_name"}, []string{"network", "browser_name"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "proxy"}, []string{"network", "proxy"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"whatsapp", "get_proxy_url"}, []string{"network", "get_proxy_url"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"whatsapp", "proxy_only_login"}, []string{"network", "proxy_only_login"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "displayname_template"}, []string{"network", "displayname_template"}) bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "displayname_template"}, []string{"network", "displayname_template"})
// TODO other fields bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "call_start_notices"}, []string{"network", "call_start_notices"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "send_presence_on_typing"}, []string{"network", "send_presence_on_typing"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "enable_status_broadcast"}, []string{"network", "enable_status_broadcast"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "disable_status_broadcast_send"}, []string{"network", "disable_status_broadcast_send"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "mute_status_broadcast"}, []string{"network", "mute_status_broadcast"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "status_broadcast_tag"}, []string{"network", "status_broadcast_tag"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "whatsapp_thumbnail"}, []string{"network", "whatsapp_thumbnail"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "url_previews"}, []string{"network", "url_previews"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "force_active_delivery_receipts"}, []string{"network", "force_active_delivery_receipts"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "history_sync", "request_full_sync"}, []string{"network", "history_sync", "request_full_sync"})
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "days_limit"}, []string{"network", "history_sync", "full_sync_config", "days_limit"})
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "size_limit_mb"}, []string{"network", "history_sync", "full_sync_config", "size_limit_mb"})
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "full_sync_config", "storage_quota_mb"}, []string{"network", "history_sync", "full_sync_config", "storage_quota_mb"})
bridgeconfig.CopyToOtherLocation(helper, up.Bool, []string{"bridge", "history_sync", "media_requests", "auto_request_media"}, []string{"network", "history_sync", "media_requests", "auto_request_media"})
bridgeconfig.CopyToOtherLocation(helper, up.Str, []string{"bridge", "history_sync", "media_requests", "request_method"}, []string{"network", "history_sync", "media_requests", "request_method"})
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "media_requests", "request_local_time"}, []string{"network", "history_sync", "media_requests", "request_local_time"})
bridgeconfig.CopyToOtherLocation(helper, up.Int, []string{"bridge", "history_sync", "media_requests", "max_async_handle"}, []string{"network", "history_sync", "media_requests", "max_async_handle"})
} }

View file

@ -20,7 +20,8 @@ SELECT
'phone_last_seen', phone_last_seen, 'phone_last_seen', phone_last_seen,
'phone_last_pinged', phone_last_pinged, 'phone_last_pinged', phone_last_pinged,
'timezone', timezone 'timezone', timezone
) -- metadata ), -- metadata
'{}' -- remote_profile
FROM user_old FROM user_old
WHERE username<>'' AND device<>0; WHERE username<>'' AND device<>0;
@ -51,6 +52,9 @@ SELECT
) -- metadata ) -- metadata
FROM puppet_old; FROM puppet_old;
INSERT INTO ghost (bridge_id, id, name, avatar_id, avatar_hash, avatar_mxc, name_set, avatar_set, contact_info_set, is_bot, identifiers, metadata)
VALUES ('', '', '', '', '', '', false, false, false, false, '[]', '{}');
INSERT INTO portal ( INSERT INTO portal (
bridge_id, id, receiver, mxid, parent_id, parent_receiver, relay_bridge_id, relay_login_id, other_user_id, bridge_id, id, receiver, mxid, parent_id, parent_receiver, relay_bridge_id, relay_login_id, other_user_id,
name, topic, avatar_id, avatar_hash, avatar_mxc, name_set, avatar_set, topic_set, name, topic, avatar_id, avatar_hash, avatar_mxc, name_set, avatar_set, topic_set,
@ -98,21 +102,35 @@ SELECT
last_read_ts * 1000000000 -- last_read TODO check multiplier last_read_ts * 1000000000 -- last_read TODO check multiplier
FROM user_portal_old; FROM user_portal_old;
ALTER TABLE message_old ADD COLUMN combined_id TEXT;
UPDATE message_old SET combined_id = chat_jid || ':' || (
CASE WHEN sender LIKE '%:%@s.whatsapp.net'
THEN (split_part(replace(sender, '@s.whatsapp.net', ''), ':', 1) || '@s.whatsapp.net')
ELSE sender
END
) || ':' || jid;
INSERT INTO message ( INSERT INTO message (
bridge_id, id, part_id, mxid, room_id, room_receiver, sender_id, sender_mxid, timestamp, edit_count, metadata bridge_id, id, part_id, mxid, room_id, room_receiver, sender_id, sender_mxid, timestamp, edit_count, metadata
) )
SELECT SELECT
'', -- bridge_id '', -- bridge_id
jid, -- id FIXME requires prefix combined_id, -- id
'', -- part_id '', -- part_id
mxid, mxid,
chat_jid, -- room_id chat_jid, -- room_id
CASE WHEN chat_receiver LIKE '%@s.whatsapp.net' THEN chat_receiver ELSE '' END, -- room_receiver CASE WHEN chat_receiver LIKE '%@s.whatsapp.net' THEN chat_receiver ELSE '' END, -- room_receiver
sender, -- sender_id CASE WHEN sender=chat_jid THEN '' ELSE split_part(replace(sender, '@s.whatsapp.net', ''), ':', 1) END, -- sender_id
sender_mxid, -- sender_mxid sender_mxid, -- sender_mxid
timestamp * 1000000000, -- timestamp TODO check multiplier timestamp * 1000000000, -- timestamp TODO check multiplier
0, -- edit_count 0, -- edit_count
'{}' -- metadata -- only: postgres
jsonb_build_object
-- only: sqlite (line commented)
-- json_object
(
'sender_device_id', CAST(nullif(split_part(replace(sender, '@s.whatsapp.net', ''), ':', 2), '') AS INTEGER)
) -- metadata
FROM message_old; FROM message_old;
INSERT INTO reaction ( INSERT INTO reaction (
@ -120,17 +138,27 @@ INSERT INTO reaction (
) )
SELECT SELECT
'', -- bridge_id '', -- bridge_id
target_jid, -- message_id FIXME requires prefix message_old.combined_id, -- message_id
'', -- message_part_id '', -- message_part_id
sender, -- sender_id replace(reaction_old.sender, '@s.whatsapp.net', ''), -- sender_id
'', -- emoji_id '', -- emoji_id
chat_jid, -- room_id reaction_old.chat_jid, -- room_id
CASE WHEN chat_receiver LIKE '%@s.whatsapp.net' THEN chat_receiver ELSE '' END, -- room_receiver CASE WHEN reaction_old.chat_receiver LIKE '%@s.whatsapp.net' THEN reaction_old.chat_receiver ELSE '' END, -- room_receiver
mxid, reaction_old.mxid,
0, -- timestamp 0, -- timestamp
'', -- emoji '', -- emoji
'{}' -- metadata -- only: postgres
FROM reaction_old; jsonb_build_object
-- only: sqlite (line commented)
-- json_object
(
'sender_device_id', CAST(nullif(split_part(replace(reaction_old.sender, '@s.whatsapp.net', ''), ':', 2), '') AS INTEGER)
) -- metadata
FROM reaction_old
LEFT JOIN message_old
ON reaction_old.chat_jid = message_old.chat_jid
AND reaction_old.chat_receiver = message_old.chat_receiver
AND reaction_old.target_jid = message_old.jid;
INSERT INTO disappearing_message (bridge_id, mx_room, mxid, type, timer, disappear_at) INSERT INTO disappearing_message (bridge_id, mx_room, mxid, type, timer, disappear_at)
SELECT SELECT
@ -152,7 +180,7 @@ SELECT
CASE WHEN portal_receiver LIKE '%@s.whatsapp.net' THEN portal_receiver ELSE '' END, -- portal_receiver CASE WHEN portal_receiver LIKE '%@s.whatsapp.net' THEN portal_receiver ELSE '' END, -- portal_receiver
(SELECT id FROM user_login WHERE user_login.user_mxid=backfill_queue_old.user_mxid), -- user_login_id (SELECT id FROM user_login WHERE user_login.user_mxid=backfill_queue_old.user_mxid), -- user_login_id
COUNT(*), -- batch_count COUNT(*), -- batch_count
COUNT(*) == COUNT(completed_at), -- is_done COUNT(*) = COUNT(completed_at), -- is_done
'', -- cursor '', -- cursor
'', -- oldest_message_id '', -- oldest_message_id
-- only: postgres -- only: postgres
@ -165,14 +193,81 @@ FROM backfill_queue_old
WHERE type IN (0, 200) WHERE type IN (0, 200)
GROUP BY user_mxid, portal_jid, portal_receiver; GROUP BY user_mxid, portal_jid, portal_receiver;
INSERT INTO whatsapp_poll_option_id (bridge_id, msg_mxid, opt_id, opt_hash)
SELECT '', msg_mxid, opt_id, opt_hash
FROM poll_option_id_old;
INSERT INTO whatsapp_history_sync_conversation (
bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time,
end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread, unread_count
)
SELECT
'',
user_login.id,
portal_jid,
-- only: postgres
CAST(EXTRACT(EPOCH FROM last_message_timestamp) AS BIGINT),
-- only: sqlite (line commented)
-- unixepoch(last_message_timestamp),
archived,
CASE WHEN pinned > 0 THEN true ELSE false END,
-- only: postgres
CAST(EXTRACT(EPOCH FROM mute_end_time) AS BIGINT),
-- only: sqlite (line commented)
-- unixepoch(mute_end_time),
end_of_history_transfer_type,
ephemeral_expiration,
0,
marked_as_unread,
unread_count
FROM history_sync_conversation_old
LEFT JOIN user_login ON user_login.user_mxid = history_sync_conversation_old.user_mxid
WHERE user_login.id IS NOT NULL;
INSERT INTO whatsapp_history_sync_message (
bridge_id, user_login_id, chat_jid, message_id, timestamp, data, inserted_time
)
SELECT
'',
user_login.id,
conversation_id,
message_id,
-- only: postgres
CAST(EXTRACT(EPOCH FROM timestamp) AS BIGINT),
-- only: sqlite (line commented)
-- unixepoch(timestamp),
data,
-- only: postgres
CAST(EXTRACT(EPOCH FROM inserted_time) AS BIGINT)
-- only: sqlite (line commented)
-- unixepoch(inserted_time)
FROM history_sync_message_old
LEFT JOIN user_login ON user_login.user_mxid = history_sync_message_old.user_mxid
WHERE user_login.id IS NOT NULL;
INSERT INTO whatsapp_media_backfill_request (
bridge_id, user_login_id, portal_id, portal_receiver, event_id, media_key, status, error
)
SELECT
'',
user_login.id,
portal_jid,
CASE WHEN portal_receiver LIKE '%@s.whatsapp.net' THEN portal_receiver ELSE '' END,
event_id,
media_key,
status,
error
FROM media_backfill_requests_old
LEFT JOIN user_login ON user_login.user_mxid = media_backfill_requests_old.user_mxid
WHERE user_login.id IS NOT NULL;
DROP TABLE backfill_queue_old; DROP TABLE backfill_queue_old;
DROP TABLE backfill_state_old; DROP TABLE backfill_state_old;
DROP TABLE disappearing_message_old; DROP TABLE disappearing_message_old;
-- TODO migrate these tables DROP TABLE history_sync_message_old;
-- DROP TABLE history_sync_message_old; DROP TABLE history_sync_conversation_old;
-- DROP TABLE history_sync_conversation_old; DROP TABLE media_backfill_requests_old;
-- DROP TABLE media_backfill_requests_old; DROP TABLE poll_option_id_old;
-- DROP TABLE poll_option_id_old;
DROP TABLE user_portal_old; DROP TABLE user_portal_old;
DROP TABLE reaction_old; DROP TABLE reaction_old;
DROP TABLE message_old; DROP TABLE message_old;

View file

@ -1,9 +1,11 @@
package main package main
import ( import (
"maunium.net/go/mautrix/bridgev2/bridgeconfig"
"maunium.net/go/mautrix/bridgev2/matrix/mxmain" "maunium.net/go/mautrix/bridgev2/matrix/mxmain"
"maunium.net/go/mautrix-whatsapp/pkg/connector" "maunium.net/go/mautrix-whatsapp/pkg/connector"
"maunium.net/go/mautrix-whatsapp/pkg/connector/wadb/upgrades"
) )
// Information to find out exactly which commit the bridge was built from. // Information to find out exactly which commit the bridge was built from.
@ -24,12 +26,16 @@ var m = mxmain.BridgeMain{
} }
func main() { func main() {
bridgeconfig.HackyMigrateLegacyNetworkConfig = migrateLegacyConfig
m.PostInit = func() { m.PostInit = func() {
m.CheckLegacyDB( m.CheckLegacyDB(
57, 57,
"v0.8.6", "v0.8.6",
"v0.11.0", "v0.11.0",
m.LegacyMigrateSimple(legacyMigrateRenameTables, legacyMigrateCopyData, 16), m.LegacyMigrateWithAnotherUpgrader(
legacyMigrateRenameTables, legacyMigrateCopyData, 17,
upgrades.Table, "whatsapp_version", 1,
),
true, true,
) )
} }

2
go.mod
View file

@ -20,7 +20,7 @@ require (
golang.org/x/sync v0.8.0 golang.org/x/sync v0.8.0
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.20.1-0.20240906145130-6b055b1475bd maunium.net/go/mautrix v0.20.1-0.20240910112932-ffdb1d575e5f
) )
require ( require (

4
go.sum
View file

@ -114,5 +114,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.20.1-0.20240906145130-6b055b1475bd h1:gfiJD2cPS9iUek1UI+DOUn08zogF4kmu7XYfBqSrAU4= maunium.net/go/mautrix v0.20.1-0.20240910112932-ffdb1d575e5f h1:T4K6V2Ige2mshH7FW69xLep23GBrW7xS1sxomaKe1Hg=
maunium.net/go/mautrix v0.20.1-0.20240906145130-6b055b1475bd/go.mod h1:l6nYvD5/FMSrAZ/IP1AqJV0b47SRl/0uQNRiy4CcSVk= maunium.net/go/mautrix v0.20.1-0.20240910112932-ffdb1d575e5f/go.mod h1:l6nYvD5/FMSrAZ/IP1AqJV0b47SRl/0uQNRiy4CcSVk=

View file

@ -79,8 +79,8 @@ func upgradeConfig(helper up.Helper) {
helper.Copy(up.Str, "os_name") helper.Copy(up.Str, "os_name")
helper.Copy(up.Str, "browser_name") helper.Copy(up.Str, "browser_name")
helper.Copy(up.Str, "proxy") helper.Copy(up.Str|up.Null, "proxy")
helper.Copy(up.Str, "get_proxy_url") helper.Copy(up.Str|up.Null, "get_proxy_url")
helper.Copy(up.Bool, "proxy_only_login") helper.Copy(up.Bool, "proxy_only_login")
helper.Copy(up.Str, "displayname_template") helper.Copy(up.Str, "displayname_template")
@ -95,9 +95,9 @@ func upgradeConfig(helper up.Helper) {
helper.Copy(up.Bool, "url_previews") helper.Copy(up.Bool, "url_previews")
helper.Copy(up.Bool, "history_sync", "request_full_sync") helper.Copy(up.Bool, "history_sync", "request_full_sync")
helper.Copy(up.Int, "history_sync", "full_sync_config", "days_limit") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "days_limit")
helper.Copy(up.Int, "history_sync", "full_sync_config", "size_mb_limit") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "size_mb_limit")
helper.Copy(up.Int, "history_sync", "full_sync_config", "storage_quota_mb") helper.Copy(up.Int|up.Null, "history_sync", "full_sync_config", "storage_quota_mb")
helper.Copy(up.Bool, "history_sync", "media_requests", "auto_request_media") helper.Copy(up.Bool, "history_sync", "media_requests", "auto_request_media")
helper.Copy(up.Str, "history_sync", "media_requests", "request_method") helper.Copy(up.Str, "history_sync", "media_requests", "request_method")
helper.Copy(up.Int, "history_sync", "media_requests", "request_local_time") helper.Copy(up.Int, "history_sync", "media_requests", "request_local_time")

View file

@ -12,6 +12,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix-whatsapp/pkg/connector/wadb"
"maunium.net/go/mautrix-whatsapp/pkg/msgconv" "maunium.net/go/mautrix-whatsapp/pkg/msgconv"
"maunium.net/go/mautrix-whatsapp/pkg/waid" "maunium.net/go/mautrix-whatsapp/pkg/waid"
) )
@ -21,6 +22,7 @@ type WhatsAppConnector struct {
Config Config Config Config
DeviceStore *sqlstore.Container DeviceStore *sqlstore.Container
MsgConv *msgconv.MessageConverter MsgConv *msgconv.MessageConverter
DB *wadb.Database
} }
var _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil) var _ bridgev2.NetworkConnector = (*WhatsAppConnector)(nil)
@ -44,6 +46,7 @@ func (wa *WhatsAppConnector) GetName() bridgev2.BridgeName {
func (wa *WhatsAppConnector) Init(bridge *bridgev2.Bridge) { func (wa *WhatsAppConnector) Init(bridge *bridgev2.Bridge) {
wa.Bridge = bridge wa.Bridge = bridge
wa.MsgConv = msgconv.New(bridge) wa.MsgConv = msgconv.New(bridge)
wa.DB = wadb.New(bridge.DB.Database, bridge.Log.With().Str("db_section", "whatsapp").Logger())
wa.DeviceStore = sqlstore.NewWithDB( wa.DeviceStore = sqlstore.NewWithDB(
bridge.DB.RawDB, bridge.DB.RawDB,
@ -66,11 +69,15 @@ func (wa *WhatsAppConnector) Init(bridge *bridgev2.Bridge) {
} }
} }
func (wa *WhatsAppConnector) Start(_ context.Context) error { func (wa *WhatsAppConnector) Start(ctx context.Context) error {
err := wa.DeviceStore.Upgrade() err := wa.DeviceStore.Upgrade()
if err != nil { if err != nil {
return bridgev2.DBUpgradeError{Err: err, Section: "whatsmeow"} return bridgev2.DBUpgradeError{Err: err, Section: "whatsmeow"}
} }
err = wa.DB.Upgrade(ctx)
if err != nil {
return bridgev2.DBUpgradeError{Err: err, Section: "whatsapp"}
}
ver, err := whatsmeow.GetLatestVersion(nil) ver, err := whatsmeow.GetLatestVersion(nil)
if err != nil { if err != nil {

View file

@ -10,8 +10,12 @@ func (wa *WhatsAppConnector) GetDBMetaTypes() database.MetaTypes {
Ghost: func() any { Ghost: func() any {
return &GhostMetadata{} return &GhostMetadata{}
}, },
Message: nil, Message: func() any {
Reaction: nil, return &MessageMetadata{}
},
Reaction: func() any {
return &ReactionMetadata{}
},
Portal: func() any { Portal: func() any {
return &PortalMetadata{} return &PortalMetadata{}
}, },
@ -26,6 +30,14 @@ type UserLoginMetadata struct {
//TODO: Add phone last ping/seen //TODO: Add phone last ping/seen
} }
type MessageMetadata struct {
SenderDeviceID uint16 `json:"sender_device_id,omitempty"`
}
type ReactionMetadata struct {
SenderDeviceID uint16 `json:"sender_device_id,omitempty"`
}
type PortalMetadata struct { type PortalMetadata struct {
DisappearingTimerSetAt int64 `json:"disappearing_timer_set_at,omitempty"` DisappearingTimerSetAt int64 `json:"disappearing_timer_set_at,omitempty"`
} }

View file

@ -27,6 +27,7 @@ var (
_ bridgev2.RemoteEventThatMayCreatePortal = (*WAMessageEvent)(nil) _ bridgev2.RemoteEventThatMayCreatePortal = (*WAMessageEvent)(nil)
_ bridgev2.RemoteReaction = (*WAMessageEvent)(nil) _ bridgev2.RemoteReaction = (*WAMessageEvent)(nil)
_ bridgev2.RemoteReactionRemove = (*WAMessageEvent)(nil) _ bridgev2.RemoteReactionRemove = (*WAMessageEvent)(nil)
_ bridgev2.RemoteReactionWithMeta = (*WAMessageEvent)(nil)
_ bridgev2.RemoteEdit = (*WAMessageEvent)(nil) _ bridgev2.RemoteEdit = (*WAMessageEvent)(nil)
_ bridgev2.RemoteMessageRemove = (*WAMessageEvent)(nil) _ bridgev2.RemoteMessageRemove = (*WAMessageEvent)(nil)
) )
@ -77,6 +78,12 @@ func (evt *WAMessageEvent) GetReactionEmoji() (string, networkid.EmojiID) {
return evt.Message.Message.GetReactionMessage().GetText(), "" return evt.Message.Message.GetReactionMessage().GetText(), ""
} }
func (evt *WAMessageEvent) GetReactionDBMetadata() any {
return &ReactionMetadata{
SenderDeviceID: evt.Info.Sender.Device,
}
}
func (evt *WAMessageEvent) GetRemovedEmojiID() networkid.EmojiID { func (evt *WAMessageEvent) GetRemovedEmojiID() networkid.EmojiID {
return "" return ""
} }
@ -129,5 +136,11 @@ func (evt *WAMessageEvent) GetTransactionID() networkid.TransactionID {
} }
func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) { func (evt *WAMessageEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI) (*bridgev2.ConvertedMessage, error) {
return evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message.Message), nil converted := evt.wa.Main.MsgConv.ToMatrix(ctx, portal, evt.wa.Client, intent, evt.Message.Message)
for _, part := range converted.Parts {
part.DBMetadata = &MessageMetadata{
SenderDeviceID: evt.Info.Sender.Device,
}
}
return converted, nil
} }

View file

@ -16,7 +16,7 @@ proxy_only_login: false
# {{.PushName}} - nickname set by the WhatsApp user # {{.PushName}} - nickname set by the WhatsApp user
# {{.BusinessName}} - validated WhatsApp business name # {{.BusinessName}} - validated WhatsApp business name
# {{.Phone}} - phone number (international format) # {{.Phone}} - phone number (international format)
# {{.FullName}} - Name you set in the contacts list # {{.FullName}} - Name you set in the contacts list
displayname_template: "{{or .FullName .BusinessName .PushName .Phone}} (WA)" displayname_template: "{{or .FullName .BusinessName .PushName .Phone}} (WA)"
# Should incoming calls send a message to the Matrix room? # Should incoming calls send a message to the Matrix room?

View file

@ -47,8 +47,11 @@ func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2
return &bridgev2.MatrixMessageResponse{ return &bridgev2.MatrixMessageResponse{
DB: &database.Message{ DB: &database.Message{
ID: wrappedMsgID, ID: wrappedMsgID,
SenderID: networkid.UserID(wa.UserLogin.ID), SenderID: waid.MakeUserID(wa.JID),
Timestamp: resp.Timestamp, Timestamp: resp.Timestamp,
Metadata: &MessageMetadata{
SenderDeviceID: wa.JID.Device,
},
}, },
RemovePending: networkid.TransactionID(wrappedMsgID), RemovePending: networkid.TransactionID(wrappedMsgID),
}, nil }, nil
@ -56,7 +59,7 @@ func (wa *WhatsAppClient) HandleMatrixMessage(ctx context.Context, msg *bridgev2
func (wa *WhatsAppClient) PreHandleMatrixReaction(_ context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) { func (wa *WhatsAppClient) PreHandleMatrixReaction(_ context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) {
return bridgev2.MatrixReactionPreResponse{ return bridgev2.MatrixReactionPreResponse{
SenderID: networkid.UserID(wa.UserLogin.ID), SenderID: waid.MakeUserID(wa.JID),
Emoji: variationselector.Remove(msg.Content.RelatesTo.Key), Emoji: variationselector.Remove(msg.Content.RelatesTo.Key),
MaxReactions: 1, MaxReactions: 1,
}, nil }, nil
@ -82,7 +85,11 @@ func (wa *WhatsAppClient) HandleMatrixReaction(ctx context.Context, msg *bridgev
resp, err := wa.Client.SendMessage(ctx, portalJID, reactionMsg) resp, err := wa.Client.SendMessage(ctx, portalJID, reactionMsg)
zerolog.Ctx(ctx).Trace().Any("response", resp).Msg("WhatsApp reaction response") zerolog.Ctx(ctx).Trace().Any("response", resp).Msg("WhatsApp reaction response")
return nil, err return &database.Reaction{
Metadata: &ReactionMetadata{
SenderDeviceID: wa.JID.Device,
},
}, err
} }
func (wa *WhatsAppClient) HandleMatrixReactionRemove(ctx context.Context, msg *bridgev2.MatrixReactionRemove) error { func (wa *WhatsAppClient) HandleMatrixReactionRemove(ctx context.Context, msg *bridgev2.MatrixReactionRemove) error {

View file

@ -1,3 +1,19 @@
package wadb package wadb
// TODO: ADD POLL AND HISTORYSYNC AND MEDIABACKFILLREQUEST DBs import (
"github.com/rs/zerolog"
"go.mau.fi/util/dbutil"
"maunium.net/go/mautrix-whatsapp/pkg/connector/wadb/upgrades"
)
type Database struct {
*dbutil.Database
}
func New(db *dbutil.Database, log zerolog.Logger) *Database {
db = db.Child("whatsapp_version", upgrades.Table, dbutil.ZeroLogger(log))
return &Database{
Database: db,
}
}

View file

@ -0,0 +1,67 @@
-- v0 -> v1 (compatible with v1+): Latest revision
CREATE TABLE whatsapp_poll_option_id (
bridge_id TEXT,
msg_mxid TEXT,
opt_id TEXT,
opt_hash bytea CHECK ( length(opt_hash) = 32 ),
PRIMARY KEY (bridge_id, msg_mxid, opt_id),
CONSTRAINT whatsapp_poll_option_unique_hash UNIQUE (bridge_id, msg_mxid, opt_hash),
CONSTRAINT message_mxid_fkey FOREIGN KEY (bridge_id, msg_mxid)
REFERENCES message(bridge_id, mxid) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE whatsapp_history_sync_conversation (
bridge_id TEXT,
user_login_id TEXT,
chat_jid TEXT,
last_message_timestamp BIGINT,
archived BOOLEAN,
pinned BOOLEAN,
mute_end_time BIGINT,
end_of_history_transfer_type INTEGER,
ephemeral_expiration INTEGER,
ephemeral_setting_timestamp BIGINT,
marked_as_unread BOOLEAN,
unread_count INTEGER,
PRIMARY KEY (bridge_id, user_login_id, chat_jid),
CONSTRAINT whatsapp_history_sync_conversation_user_login_fkey FOREIGN KEY (bridge_id, user_login_id)
REFERENCES user_login(bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE whatsapp_history_sync_message (
bridge_id TEXT,
user_login_id TEXT,
chat_jid TEXT,
message_id TEXT,
timestamp BIGINT,
data bytea,
inserted_time BIGINT,
PRIMARY KEY (bridge_id, user_login_id, chat_jid, message_id),
CONSTRAINT whatsapp_history_sync_message_user_login_fkey FOREIGN KEY (bridge_id, user_login_id)
REFERENCES user_login(bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT whatsapp_history_sync_message_conversation_fkey FOREIGN KEY (bridge_id, user_login_id, chat_jid)
REFERENCES whatsapp_history_sync_conversation(bridge_id, user_login_id, chat_jid) ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE TABLE whatsapp_media_backfill_request (
bridge_id TEXT,
user_login_id TEXT,
portal_id TEXT,
portal_receiver TEXT,
event_id TEXT,
media_key bytea,
status INTEGER,
error TEXT,
PRIMARY KEY (bridge_id, user_login_id, portal_id, portal_receiver, event_id),
CONSTRAINT whatsapp_media_backfill_request_user_login_fkey FOREIGN KEY (bridge_id, user_login_id)
REFERENCES user_login(bridge_id, id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT whatsapp_media_backfill_request_portal_fkey FOREIGN KEY (bridge_id, portal_id, portal_receiver)
REFERENCES portal(bridge_id, id, receiver) ON UPDATE CASCADE ON DELETE CASCADE
);

View file

@ -0,0 +1,16 @@
package upgrades
import (
"embed"
"go.mau.fi/util/dbutil"
)
var Table dbutil.UpgradeTable
//go:embed *.sql
var rawUpgrades embed.FS
func init() {
Table.RegisterFS(rawUpgrades)
}