From e424382cc955fa324f4d1fd8f73c261a5e7a77e7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Jun 2022 23:25:37 +0300 Subject: [PATCH] Add support for list messages --- formatting.go | 4 +- portal.go | 131 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 110 insertions(+), 25 deletions(-) diff --git a/formatting.go b/formatting.go index 9a6dbec..1418d79 100644 --- a/formatting.go +++ b/formatting.go @@ -109,7 +109,7 @@ func (formatter *Formatter) getMatrixInfoByJID(roomID id.RoomID, jid types.JID) return } -func (formatter *Formatter) ParseWhatsApp(roomID id.RoomID, content *event.MessageEventContent, mentionedJIDs []string, allowInlineURL bool) { +func (formatter *Formatter) ParseWhatsApp(roomID id.RoomID, content *event.MessageEventContent, mentionedJIDs []string, allowInlineURL, forceHTML bool) { output := html.EscapeString(content.Body) for regex, replacement := range formatter.waReplString { output = regex.ReplaceAllString(output, replacement) @@ -135,7 +135,7 @@ func (formatter *Formatter) ParseWhatsApp(roomID id.RoomID, content *event.Messa output = strings.ReplaceAll(output, number, fmt.Sprintf(`%s`, mxid, displayname)) content.Body = strings.ReplaceAll(content.Body, number, displayname) } - if output != content.Body { + if output != content.Body || forceHTML { output = strings.ReplaceAll(output, "\n", "
") content.FormattedBody = output content.Format = event.FormatHTML diff --git a/portal.go b/portal.go index 7a715ed..3c5b4c6 100644 --- a/portal.go +++ b/portal.go @@ -352,7 +352,8 @@ func containsSupportedMessage(waMsg *waProto.Message) bool { waMsg.StickerMessage != nil || waMsg.AudioMessage != nil || waMsg.VideoMessage != nil || waMsg.DocumentMessage != nil || waMsg.ContactMessage != nil || waMsg.LocationMessage != nil || waMsg.LiveLocationMessage != nil || waMsg.GroupInviteMessage != nil || waMsg.ContactsArrayMessage != nil || - waMsg.HighlyStructuredMessage != nil || waMsg.TemplateMessage != nil || waMsg.TemplateButtonReplyMessage != nil + waMsg.HighlyStructuredMessage != nil || waMsg.TemplateMessage != nil || waMsg.TemplateButtonReplyMessage != nil || + waMsg.ListMessage != nil || waMsg.ListResponseMessage != nil } func getMessageType(waMsg *waProto.Message) string { @@ -490,6 +491,10 @@ func (portal *Portal) convertMessage(intent *appservice.IntentAPI, source *User, return portal.convertTemplateMessage(intent, source, info, waMsg.GetHighlyStructuredMessage().GetHydratedHsm()) case waMsg.TemplateButtonReplyMessage != nil: return portal.convertTemplateButtonReplyMessage(intent, waMsg.GetTemplateButtonReplyMessage()) + case waMsg.ListMessage != nil: + return portal.convertListMessage(intent, source, waMsg.GetListMessage()) + case waMsg.ListResponseMessage != nil: + return portal.convertListResponseMessage(intent, waMsg.GetListResponseMessage()) case waMsg.ImageMessage != nil: return portal.convertMediaMessage(intent, source, info, waMsg.GetImageMessage(), "photo", isBackfill) case waMsg.StickerMessage != nil: @@ -1717,7 +1722,7 @@ func (portal *Portal) convertTextMessage(intent *appservice.IntentAPI, source *U } contextInfo := msg.GetExtendedTextMessage().GetContextInfo() - portal.bridge.Formatter.ParseWhatsApp(portal.MXID, content, contextInfo.GetMentionedJid(), false) + portal.bridge.Formatter.ParseWhatsApp(portal.MXID, content, contextInfo.GetMentionedJid(), false, false) replyTo := contextInfo.GetStanzaId() expiresIn := contextInfo.GetExpiration() extraAttrs := map[string]interface{}{} @@ -1733,25 +1738,6 @@ func (portal *Portal) convertTextMessage(intent *appservice.IntentAPI, source *U } } -func (portal *Portal) convertTemplateButtonReplyMessage(intent *appservice.IntentAPI, msg *waProto.TemplateButtonReplyMessage) *ConvertedMessage { - return &ConvertedMessage{ - Intent: intent, - Type: event.EventMessage, - Content: &event.MessageEventContent{ - Body: msg.GetSelectedDisplayText(), - MsgType: event.MsgText, - }, - Extra: map[string]interface{}{ - "fi.mau.whatsapp.template_button_reply": map[string]interface{}{ - "id": msg.GetSelectedId(), - "index": msg.GetSelectedIndex(), - }, - }, - ReplyTo: msg.GetContextInfo().GetStanzaId(), - ExpiresIn: msg.GetContextInfo().GetExpiration(), - } -} - func (portal *Portal) convertTemplateMessage(intent *appservice.IntentAPI, source *User, info *types.MessageInfo, tplMsg *waProto.TemplateMessage) *ConvertedMessage { converted := &ConvertedMessage{ Intent: intent, @@ -1808,7 +1794,7 @@ func (portal *Portal) convertTemplateMessage(intent *appservice.IntentAPI, sourc } converted.Content.Body = content - portal.bridge.Formatter.ParseWhatsApp(portal.MXID, converted.Content, nil, true) + portal.bridge.Formatter.ParseWhatsApp(portal.MXID, converted.Content, nil, true, false) if convertedTitle != nil { converted.MediaKey = convertedTitle.MediaKey converted.Extra = convertedTitle.Extra @@ -1823,6 +1809,105 @@ func (portal *Portal) convertTemplateMessage(intent *appservice.IntentAPI, sourc return converted } +func (portal *Portal) convertTemplateButtonReplyMessage(intent *appservice.IntentAPI, msg *waProto.TemplateButtonReplyMessage) *ConvertedMessage { + return &ConvertedMessage{ + Intent: intent, + Type: event.EventMessage, + Content: &event.MessageEventContent{ + Body: msg.GetSelectedDisplayText(), + MsgType: event.MsgText, + }, + Extra: map[string]interface{}{ + "fi.mau.whatsapp.template_button_reply": map[string]interface{}{ + "id": msg.GetSelectedId(), + "index": msg.GetSelectedIndex(), + }, + }, + ReplyTo: msg.GetContextInfo().GetStanzaId(), + ExpiresIn: msg.GetContextInfo().GetExpiration(), + } +} + +func (portal *Portal) convertListMessage(intent *appservice.IntentAPI, source *User, msg *waProto.ListMessage) *ConvertedMessage { + converted := &ConvertedMessage{ + Intent: intent, + Type: event.EventMessage, + Content: &event.MessageEventContent{ + Body: "Unsupported business message", + MsgType: event.MsgText, + }, + ReplyTo: msg.GetContextInfo().GetStanzaId(), + ExpiresIn: msg.GetContextInfo().GetExpiration(), + } + body := msg.GetDescription() + if msg.GetTitle() != "" { + if body == "" { + body = msg.GetTitle() + } else { + body = fmt.Sprintf("%s\n\n%s", msg.GetTitle(), body) + } + } + randomID := appservice.RandomString(64) + body = fmt.Sprintf("%s\n%s", body, randomID) + if msg.GetFooterText() != "" { + body = fmt.Sprintf("%s\n\n%s", body, msg.GetFooterText()) + } + converted.Content.Body = body + portal.bridge.Formatter.ParseWhatsApp(portal.MXID, converted.Content, nil, false, true) + + var optionsMarkdown strings.Builder + _, _ = fmt.Fprintf(&optionsMarkdown, "#### %s\n", msg.GetButtonText()) + for _, section := range msg.GetSections() { + nesting := "" + if section.GetTitle() != "" { + _, _ = fmt.Fprintf(&optionsMarkdown, "* %s\n", section.GetTitle()) + nesting = " " + } + for _, row := range section.GetRows() { + if row.GetDescription() != "" { + _, _ = fmt.Fprintf(&optionsMarkdown, "%s* %s: %s\n", nesting, row.GetTitle(), row.GetDescription()) + } else { + _, _ = fmt.Fprintf(&optionsMarkdown, "%s* %s\n", nesting, row.GetTitle()) + } + } + } + optionsMarkdown.WriteString("\nUse the WhatsApp app to respond") + rendered := format.RenderMarkdown(optionsMarkdown.String(), true, false) + converted.Content.Body = strings.Replace(converted.Content.Body, randomID, rendered.Body, 1) + converted.Content.FormattedBody = strings.Replace(converted.Content.FormattedBody, randomID, rendered.FormattedBody, 1) + return converted +} + +func (portal *Portal) convertListResponseMessage(intent *appservice.IntentAPI, msg *waProto.ListResponseMessage) *ConvertedMessage { + var body string + if msg.GetTitle() != "" { + if msg.GetDescription() != "" { + body = fmt.Sprintf("%s\n\n%s", msg.GetTitle(), msg.GetDescription()) + } else { + body = msg.GetTitle() + } + } else if msg.GetDescription() != "" { + body = msg.GetDescription() + } else { + body = "Unsupported list reply message" + } + return &ConvertedMessage{ + Intent: intent, + Type: event.EventMessage, + Content: &event.MessageEventContent{ + Body: body, + MsgType: event.MsgText, + }, + Extra: map[string]interface{}{ + "fi.mau.whatsapp.list_reply": map[string]interface{}{ + "row_id": msg.GetSingleSelectReply().GetSelectedRowId(), + }, + }, + ReplyTo: msg.GetContextInfo().GetStanzaId(), + ExpiresIn: msg.GetContextInfo().GetExpiration(), + } +} + func (portal *Portal) convertLiveLocationMessage(intent *appservice.IntentAPI, msg *waProto.LiveLocationMessage) *ConvertedMessage { content := &event.MessageEventContent{ Body: "Started sharing live location", @@ -2329,7 +2414,7 @@ func (portal *Portal) convertMediaMessageContent(intent *appservice.IntentAPI, m MsgType: event.MsgNotice, } - portal.bridge.Formatter.ParseWhatsApp(portal.MXID, captionContent, msg.GetContextInfo().GetMentionedJid(), false) + portal.bridge.Formatter.ParseWhatsApp(portal.MXID, captionContent, msg.GetContextInfo().GetMentionedJid(), false, false) } return &ConvertedMessage{