From 7de7a6e321c700ac4fb5827e9cdb91ecf20b779b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hermann=20H=C3=B6hne?= Date: Wed, 7 Aug 2024 21:50:20 +0200 Subject: [PATCH] Release v1.16.0 * Feature: Offer to pair with 8-character code. * Feature: Video notes are received. * Feature: Have option to display message ID in conversation. --- .gitignore | 3 - CHANGELOG.md | 8 +- README.md | 15 +-- VERSION | 2 +- src/c/blist.c | 1 + src/c/bridge.h | 4 + src/c/constants.h | 1 + src/c/display_message.c | 20 ++-- src/c/groups.c | 2 +- src/c/login.c | 2 +- src/c/options.c | 8 ++ src/c/process_message.c | 2 +- src/c/qrcode.c | 48 ++++++---- src/c/send_file.c | 2 +- src/c/send_message.c | 4 +- src/go/bridge.go | 24 +++-- src/go/go.mod | 20 ++-- src/go/go.sum | 40 ++++---- src/go/handle_message.go | 199 ++++++++++++++++++++++----------------- src/go/handler.go | 4 +- src/go/login.go | 44 ++++++--- src/go/send_file.go | 2 +- src/go/send_message.go | 6 +- submodules/purple-cmake | 2 +- 24 files changed, 278 insertions(+), 185 deletions(-) diff --git a/.gitignore b/.gitignore index fd18da8..8399ce7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,3 @@ out build ignore - -src/go/go.sum -src/go/go.mod diff --git a/CHANGELOG.md b/CHANGELOG.md index d72c514..b5a706e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.16.0 + +* Feature: Offer to pair with 8-character code. +* Feature: Video notes are received. +* Feature: Have option to display message ID in conversation. + # 1.15.0 * Update: Depends on whatsmeow v0.0.0-20240521160649-74c49f5a7d31 or later. @@ -13,7 +19,7 @@ * Feature: Installation into user's home directory (Linux only). * Change: Incoming images can be shown in-line, offered as a file-transfer or both ("both" is new). * Change: KeepAliveTimeout is ignored (was terminate connection). -* Bugfix: Upon re-connect chats currently open in Pidgin can be re-joined implicitly. +* Bugfix: Upon re-connect, chats currently open in Pidgin can be re-joined implicitly. * Bugfix: Incoming newlines are converted to br-tags as it is the custom in libpurple. * Bugfix: libgcc is now linked statically when building for win32 with GCC later than 4.7.2. diff --git a/README.md b/README.md index 69952a2..d18d259 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ This is a re-write of [purple-gowhatsapp](https://github.com/hoehermann/purple-g Standard features: -* Connecting to existing account via QR-code. +* Connecting to existing account via QR code or 8-character code. * Receiving messages, sending messages. -* Receiving files (images, videos, voice, document, stickers). +* Receiving files (image, video and note, audio and voice, document, sticker). * Received images are displayed in the conversation window (optional). -* Sending images as image messages. +* Sending JPEG images as image messages. * Sending opus audio files as voice messages. * Sending mp4 video files as video messages. * Sending other files as documents. @@ -154,8 +154,8 @@ For sending opus in ogg audio files as voice messages, add a static win32 build You must enter your phone's internationalized number followed by `@s.whatsapp.net`. Example: `123456789` from Germany would use `49123456789@s.whatsapp.net`. -* Upon login, a QR code is shown in a Pidgin request window. - Using your phone's camera, scan the code within 20 seconds ā€“ just like you would do with WhatsApp Web. +* Upon login, a QR code and the 8-character code is shown in a Pidgin request window. + Using your phone's camera, scan the code within 20 seconds or enter the 8-character code on your main device ā€“ just like you would do with WhatsApp Web. *Note:* On headless clients such as Spectrum, the QR code will be wrapped in a message by a fake contact called "Logon QR Code". You may need to temporarily configure your UI to accept messages from unsolicited users for linking purposes. Wait until the connection has been fully set up. Unfortunately, there is no progress indicator while keys are exchanged and old messages are fetched. Usually, a couple of seconds is enough. Some power users with many groups and contacts reported the process can take more than a minute. If the plug-in is not yet ready, outgoing messages may be dropped silently (see issue #142). @@ -234,6 +234,9 @@ For sending opus in ogg audio files as voice messages, add a static win32 build Note: Neither of these indicate whether the message has been received by the *contact*. +* `display-message-id` + If set to true, the ID of a text message will be appended to the displayed text. For outgoing messages, this only has effect if `echo-sent-messages` is set to `on-success`. + * `autojoin-chats` Automatically join all chats representing the WhatsApp groups after connecting and every time group information is provided. This is useful for protocol bridges. @@ -335,7 +338,7 @@ This plug-in supports a couple of "IRC-style" commands. The user can write them Request the current list of participants. Can only be used in group chat conversations. * `?presenceavailable`, `?presenceunavailable`, `?presence` - Overrides the presence which is being sent to WhatsApp servers. The displayed connection state may no longer match the advertised connection state. This can be used to appear unavailable while still being able to receive messages for logging or notification purposes. Using this command may result in unexpected behaviour. Use `/presence` (without a suffix) to give back control to the plug-in's internals. + Overrides the presence which is being sent to WhatsApp servers. The displayed connection state may no longer match the advertised connection state. This can be used to appear unavailable while still being able to receive messages for logging or notification purposes. Using this command may result in unexpected behaviour. Use `?presence` (without a suffix) to give back control to the plug-in's internals. * `?logout` Performs a log-out. The QR-code will be requested upon connecting again. diff --git a/VERSION b/VERSION index d19d089..71bd5d9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.15.0 \ No newline at end of file +1.16.0 \ No newline at end of file diff --git a/src/c/blist.c b/src/c/blist.c index 0cfcfa2..b15ae90 100644 --- a/src/c/blist.c +++ b/src/c/blist.c @@ -50,6 +50,7 @@ void gowhatsapp_ensure_buddy_in_blist( gowhatsapp_assume_buddy_online(account, buddy); // update name after checking against local alias and persisted name + // TODO: merge changes from purple-presage const char *local_alias = purple_buddy_get_alias(buddy); const char *server_alias = purple_blist_node_get_string(&buddy->node, "server_alias"); if (display_name != NULL && !purple_strequal(local_alias, display_name) && !purple_strequal(server_alias, display_name)) { diff --git a/src/c/bridge.h b/src/c/bridge.h index e63b5ee..63b9c7e 100644 --- a/src/c/bridge.h +++ b/src/c/bridge.h @@ -70,7 +70,11 @@ struct gowhatsapp_message { PurpleAccount *account; /// pointer identifying the account char *remoteJid; /// conversation identifier (may be a single contact or a group) char *senderJid; /// message author's identifier (useful in group chats) + char *messageId; /// message ID char *text; /// the message payload (interpretation depends on type) + char *pairing_code; /// 6-character pairing code + char *pairing_qrdata; /// the pairing QR-code raw data + char *pairing_qrterminal; /// graphical QR for printing on a terminal char *name; /// remote user's name (chosen by them) or filename (in case of attachment) void *blob; /// binary payload (used for inlining images) char **participants; /// list of participants (for group chats) diff --git a/src/c/constants.h b/src/c/constants.h index d3b4605..e2d1c9f 100644 --- a/src/c/constants.h +++ b/src/c/constants.h @@ -27,6 +27,7 @@ CHARCONSTANT(GOWHATSAPP_INLINE_STICKERS_OPTION, "inline-stickers"); CHARCONSTANT(GOWHATSAPP_GROUP_IS_FILE_ORIGIN_OPTION, "group-is-file-origin"); CHARCONSTANT(GOWHATSAPP_AUTO_JOIN_CHAT_OPTION, "autojoin-chats"); CHARCONSTANT(GOWHATSAPP_BRIDGE_COMPATIBILITY_OPTION, "bridge-compatibility"); +CHARCONSTANT(GOWHATSAPP_DISPLAY_MESSAGE_ID_OPTION, "display-message-id"); CHARCONSTANT(GOWHATSAPP_ECHO_OPTION, "echo-sent-messages"); CHARCONSTANT(GOWHATSAPP_MESSAGE_CACHE_SIZE_OPTION, "message-cache-size"); CHARCONSTANT(GOWHATSAPP_IGNORE_STATUS_BROADCAST_OPTION, "ignore-status-broadcast"); diff --git a/src/c/display_message.c b/src/c/display_message.c index e1b7b0d..3aef577 100644 --- a/src/c/display_message.c +++ b/src/c/display_message.c @@ -1,12 +1,18 @@ #include "gowhatsapp.h" #include "constants.h" -void gowhatsapp_display_text_message(PurpleConnection *pc, gowhatsapp_message_t *gwamsg, PurpleMessageFlags flags) { - g_return_if_fail(pc != NULL); +void gowhatsapp_display_text_message(PurpleConnection *connection, gowhatsapp_message_t *gwamsg, PurpleMessageFlags flags) { + g_return_if_fail(connection != NULL); // WhatsApp is a plain-text protocol, but Pidgin expects HTML - // NOTE: This turns newlines into br-tags which may mess up textual representation of QR-codes gchar * text = purple_markup_escape_text(gwamsg->text, -1); - gowhatsapp_display_message_common(pc, gwamsg->senderJid, gwamsg->remoteJid, text, gwamsg->timestamp, gwamsg->isGroup, gwamsg->isOutgoing, gwamsg->name, flags); + PurpleAccount *account = purple_connection_get_account(connection); + if (purple_account_get_bool(account, GOWHATSAPP_DISPLAY_MESSAGE_ID_OPTION, FALSE)) { + // for https://github.com/Juliaria08 + gchar * text_with_id = g_strdup_printf("%s %s", text, gwamsg->messageId); + g_free(text); + text = text_with_id; + } + gowhatsapp_display_message_common(connection, gwamsg->senderJid, gwamsg->remoteJid, text, gwamsg->timestamp, gwamsg->isGroup, gwamsg->isOutgoing, gwamsg->name, flags); g_free(text); } @@ -53,10 +59,8 @@ void gowhatsapp_display_message_common( } if (isGroup) { - PurpleConversation *conv = gowhatsapp_enter_group_chat(pc, remoteJid, NULL); - if (conv != NULL) { - purple_serv_got_chat_in(pc, g_str_hash(remoteJid), senderJid, flags, text, timestamp); - } + gowhatsapp_enter_group_chat(pc, remoteJid, NULL); + purple_serv_got_chat_in(pc, g_str_hash(remoteJid), senderJid, flags, text, timestamp); } else { if (flags & PURPLE_MESSAGE_SEND) { // display message sent from own account (other device as well as local echo) diff --git a/src/c/groups.c b/src/c/groups.c index f5ce271..e57eed9 100644 --- a/src/c/groups.c +++ b/src/c/groups.c @@ -106,7 +106,7 @@ gowhatsapp_roomlist_get_list(PurpleConnection *pc) { g_return_val_if_fail(wpd != NULL, NULL); PurpleRoomlist *roomlist = wpd->roomlist; if (roomlist != NULL) { - purple_debug_info(GOWHATSAPP_NAME, "Already getting roomlist."); + purple_debug_info(GOWHATSAPP_NAME, "Already getting roomlist.\n"); return roomlist; } roomlist = purple_roomlist_new(account); // MEMCHECK: caller takes ownership diff --git a/src/c/login.c b/src/c/login.c index ccebb03..74434a3 100644 --- a/src/c/login.c +++ b/src/c/login.c @@ -83,7 +83,7 @@ gowhatsapp_store_credentials(PurpleAccount *account, char *credentials) // Pidgin stores the credentials in the account settings // since commit ee89203, spectrum supports this out of the box // in bitlbee, this has no effect - // TODO: ask spectrum maintainer if storing in password woukd okay, too + // TODO: ask spectrum maintainer if storing in password would okay, too // or do not store credentials at all (just use the username for look-up) purple_account_set_string(account, GOWHATSAPP_CREDENTIALS_KEY, credentials); diff --git a/src/c/options.c b/src/c/options.c index c1439f6..b444dbb 100644 --- a/src/c/options.c +++ b/src/c/options.c @@ -167,6 +167,14 @@ gowhatsapp_add_account_options(GList *account_options) FALSE ); account_options = g_list_append(account_options, option); + + // for https://github.com/Juliaria08 + option = purple_account_option_bool_new( // MEMCHECK: account_options takes ownership + "Display message ID", + GOWHATSAPP_DISPLAY_MESSAGE_ID_OPTION, + FALSE + ); + account_options = g_list_append(account_options, option); return account_options; } diff --git a/src/c/process_message.c b/src/c/process_message.c index 8df963f..fc5c76f 100644 --- a/src/c/process_message.c +++ b/src/c/process_message.c @@ -121,7 +121,7 @@ gowhatsapp_process_message(gowhatsapp_message_t *gwamsg) gowhatsapp_handle_group(pc, gwamsg); break; default: - purple_debug_info(GOWHATSAPP_NAME, "handling this message type is not implemented"); + purple_debug_info(GOWHATSAPP_NAME, "Handling this message type is not implemented.\n"); g_free(gwamsg->blob); } } diff --git a/src/c/qrcode.c b/src/c/qrcode.c index b573642..6b67c02 100644 --- a/src/c/qrcode.c +++ b/src/c/qrcode.c @@ -18,7 +18,7 @@ gowhatsapp_close_qrcode(PurpleAccount *account) } static void -gowhatsapp_display_qrcode(PurpleAccount *account, const char * challenge, void * image_data, size_t image_data_len) +gowhatsapp_display_qrcode(PurpleAccount *account, const char *pairing_code, const char *qr_data, void * image_data, size_t image_data_len) { g_return_if_fail(account != NULL); @@ -26,19 +26,27 @@ gowhatsapp_display_qrcode(PurpleAccount *account, const char * challenge, void * PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); - PurpleRequestField *string_field = purple_request_field_string_new("qr_string", "QR Code Data", challenge, FALSE); - purple_request_field_group_add_field(group, string_field); - PurpleRequestField *image_field = purple_request_field_image_new("qr_image", "QR Code Image", image_data, image_data_len); - purple_request_field_group_add_field(group, image_field); + { + PurpleRequestField *string_code = purple_request_field_string_new("pairing_code", "Pairing Code", pairing_code, FALSE); + purple_request_field_group_add_field(group, string_code); + } + { + PurpleRequestField *string_field = purple_request_field_string_new("qr_data", "QR Code Data", qr_data, FALSE); + purple_request_field_group_add_field(group, string_field); + } + { + PurpleRequestField *image_field = purple_request_field_image_new("qr_image", "QR Code Image", image_data, image_data_len); + purple_request_field_group_add_field(group, image_field); + } const char *username = purple_account_get_username(account); - char *secondary = g_strdup_printf("WhatsApp account %s (multi-device mode must be enabled)", username); // MEMCHECK: released here + char *secondary = g_strdup_printf("WhatsApp account %s", username); // MEMCHECK: released here gowhatsapp_close_qrcode(account); purple_request_fields( account, /*handle*/ "Logon QR Code", /*title*/ - "Please scan this QR code with your phone", /*primary*/ + "Please enter pairing code or scan the QR code", /*primary*/ secondary, /*secondary*/ fields, /*fields*/ "OK", G_CALLBACK(null_cb), /*OK*/ @@ -59,33 +67,37 @@ gowhatsapp_handle_qrcode(PurpleConnection *pc, gowhatsapp_message_t *gwamsg) if (!ui_ops || !ui_ops->request_fields || gwamsg->blobsize <= 0) { // The UI hasn't implemented the func we want, just output as a message instead PurpleMessageFlags flags = PURPLE_MESSAGE_RECV; - gchar *msg_out; int img_id = 0; if (gwamsg->blobsize > 0) { img_id = purple_imgstore_add_with_id(gwamsg->blob, gwamsg->blobsize, NULL); // MEMCHECK: released including gwamsg->blob by purple_imgstore_unref_by_id (see below) } + gchar *msg_img = NULL; if (img_id > 0) { gwamsg->blob = NULL; // MEMCHECK: not our memory to free any more - msg_out = g_strdup_printf( // MEMCHECK: msg_out released here (see below) - "%s
\"%s\"/
%s", - "Please scan this QR code with your phone and WhatsApp multi-device mode enabled:", img_id, gwamsg->text, gwamsg->name - ); + msg_img = g_strdup_printf("", img_id); // MEMCHECK: released here (see below) flags |= PURPLE_MESSAGE_IMAGES; } else { - msg_out = g_strdup_printf( // MEMCHECK: msg_out released here (see below) - "%s
%s
%s", - "Please scan this QR code with your phone and WhatsApp multi-device mode enabled:", gwamsg->text, gwamsg->name - ); + // NOTE: This turns the newlines into br-tags. Front-ends should know what they are doing. + gchar * qrterminal_html = purple_markup_escape_text(gwamsg->pairing_qrterminal, -1); // MEMCHECK: released here (see below) + msg_img = g_strdup_printf("Your UI does not handle images. The next lines emulate the QR code with text characters. If viewed with a mono-spaced font, scanning may succeed. In case you see the raw HTML (with br-tags), you need to convert them into newlines first.
%s", qrterminal_html); // MEMCHECK: released here (see below) + g_free(qrterminal_html); } + gchar *msg_out = g_strdup_printf( + "Please enter pairing code %s or scan the QR code with your phone.
%s
In case the QR code above does not work, this is the challenge data. Use the QR code generator of your choice to turn it into an image:
%s", + gwamsg->pairing_code, + msg_img, + gwamsg->pairing_qrdata + ); // MEMCHECK: released here (see below) + g_free(msg_img); const gchar *who = "Logon QR Code"; purple_serv_got_im(pc, who, msg_out, flags, time(NULL)); + g_free(msg_out); if (img_id > 0) { purple_imgstore_unref_by_id(img_id); } - g_free(msg_out); } else { PurpleAccount *account = purple_connection_get_account(pc); - gowhatsapp_display_qrcode(account, gwamsg->text, gwamsg->blob, gwamsg->blobsize); + gowhatsapp_display_qrcode(account, gwamsg->pairing_code, gwamsg->pairing_qrdata, gwamsg->blob, gwamsg->blobsize); } g_free(gwamsg->blob); } diff --git a/src/c/send_file.c b/src/c/send_file.c index 6a6dd28..b2133c3 100644 --- a/src/c/send_file.c +++ b/src/c/send_file.c @@ -51,7 +51,7 @@ gowhatsapp_chat_send_file(PurpleConnection *pc, int id, const char *filename) if (conv != NULL) { const gchar *who = purple_conversation_get_data(conv, "name"); if (who != NULL) { - gowhatsapp_send_file(pc, who, (gchar *)filename); + gowhatsapp_send_file(pc, who, (gchar *)filename); // TODO: there should be a group flag here somewhere } } // TODO: display error if conv or who are NULL diff --git a/src/c/send_message.c b/src/c/send_message.c index c2734e8..46c09b9 100644 --- a/src/c/send_message.c +++ b/src/c/send_message.c @@ -8,7 +8,9 @@ send_message(PurpleConnection *pc, const gchar *who, const gchar *message, gbool char *msg = purple_markup_strip_html(message); // Note: This turns newlines into spaces and
tags into newlines PurpleAccount *account = purple_connection_get_account(pc); char *w = (char *)who; // cgo does not suport const - return gowhatsapp_go_send_message(account, w, msg, is_group); + int ret = gowhatsapp_go_send_message(account, w, msg, is_group); + g_free(msg); + return ret; } int diff --git a/src/go/bridge.go b/src/go/bridge.go index 002a211..9049f9d 100644 --- a/src/go/bridge.go +++ b/src/go/bridge.go @@ -163,6 +163,7 @@ func gowhatsapp_go_query_group_participants(account *PurpleAccount, groupid *C.c if groupid != nil { go_groupid := C.GoString(groupid) jid, err := parseJID(go_groupid) + // TODO: check that jid actually is a group jid, see https://github.com/hoehermann/purple-gowhatsapp/issues/195 if err == nil { return participants_to_ntcstrarray(handler.query_group_participants_retry(jid, 1, 10, 0)) } else { @@ -258,16 +259,18 @@ func gowhatsapp_go_request_profile_picture(account *PurpleAccount, who *C.char, } /* - * This will display a QR code via PurpleRequest API. + * This will display a QR code via PurpleRequest API + * or in a conversation window (depending on UI features and user settings). */ -func purple_display_qrcode(account *PurpleAccount, terminal string, challenge string, png []byte) { +func purple_display_qrcode(account *PurpleAccount, piring_code string, qr_data string, qr_terminal string, png []byte) { cmessage := C.struct_gowhatsapp_message{ - account: account, - msgtype: C.char(C.gowhatsapp_message_type_login), - text: C.CString(challenge), - name: C.CString(terminal), - blob: C.CBytes(png), - blobsize: C.size_t(len(png)), + account: account, + msgtype: C.char(C.gowhatsapp_message_type_login), + pairing_code: C.CString(piring_code), + pairing_qrdata: C.CString(qr_data), + pairing_qrterminal: C.CString(qr_terminal), + blob: C.CBytes(png), + blobsize: C.size_t(len(png)), } C.gowhatsapp_process_message_bridge(cmessage) } @@ -309,7 +312,7 @@ func purple_disconnected(account *PurpleAccount) { * This will display a text message. * Single participants and group chats. */ -func purple_display_text_message(account *PurpleAccount, remoteJid string, isGroup bool, isOutgoing bool, senderJid string, pushName *string, timestamp time.Time, text string) { +func purple_display_text_message(account *PurpleAccount, remoteJid string, isGroup bool, isOutgoing bool, senderJid string, pushName *string, timestamp time.Time, text string, id *string) { cmessage := C.struct_gowhatsapp_message{ account: account, msgtype: C.char(C.gowhatsapp_message_type_text), @@ -323,6 +326,9 @@ func purple_display_text_message(account *PurpleAccount, remoteJid string, isGro if pushName != nil { cmessage.name = C.CString(*pushName) } + if id != nil { + cmessage.messageId = C.CString(*id) + } C.gowhatsapp_process_message_bridge(cmessage) } diff --git a/src/go/go.mod b/src/go/go.mod index fb2fd2e..3736a65 100644 --- a/src/go/go.mod +++ b/src/go/go.mod @@ -8,22 +8,22 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 github.com/mdp/qrterminal/v3 v3.0.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1 - golang.org/x/net v0.25.0 - google.golang.org/protobuf v1.33.0 + go.mau.fi/whatsmeow v0.0.0-20240726213518-bb5852f056ca + golang.org/x/net v0.27.0 + google.golang.org/protobuf v1.34.2 ) require ( - filippo.io/edwards25519 v1.0.0 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/rs/zerolog v1.32.0 // indirect - go.mau.fi/libsignal v0.1.0 // indirect - go.mau.fi/util v0.4.1 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + go.mau.fi/libsignal v0.1.1 // indirect + go.mau.fi/util v0.6.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect rsc.io/qr v0.2.0 // indirect ) diff --git a/src/go/go.sum b/src/go/go.sum index 0774465..55b56b7 100644 --- a/src/go/go.sum +++ b/src/go/go.sum @@ -1,5 +1,5 @@ -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/alfg/mp4 v0.0.0-20210728035756-55ea58c08aeb h1:v8z8Yym2Z/NCYgXO/aFqmYDYa/d3uRyVMq1DjgMfzn4= github.com/alfg/mp4 v0.0.0-20210728035756-55ea58c08aeb/go.mod h1:RfuO8OqkqAcWXXkaS3MuymrBQJbKJWi04H/bs6vNvh0= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -30,34 +30,34 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c= -go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I= -go.mau.fi/util v0.4.1 h1:3EC9KxIXo5+h869zDGf5OOZklRd/FjeVnimTwtm3owg= -go.mau.fi/util v0.4.1/go.mod h1:GjkTEBsehYZbSh2LlE6cWEn+6ZIZTGrTMM/5DMNlmFY= -go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1 h1:mUEEmZs1xk5QHKXjDxiAP4bYgyj8r7PaZCafHN+KMQg= -go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1/go.mod h1:0+65CYaE6r4dWzr0dN8i+UZKy0gIfJ79VuSqIl0nKRM= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +go.mau.fi/libsignal v0.1.1 h1:m/0PGBh4QKP/I1MQ44ti4C0fMbLMuHb95cmDw01FIpI= +go.mau.fi/libsignal v0.1.1/go.mod h1:QLs89F/OA3ThdSL2Wz2p+o+fi8uuQUz0e1BRa6ExdBw= +go.mau.fi/util v0.6.0 h1:W6SyB3Bm/GjenQ5iq8Z8WWdN85Gy2xS6L0wmnR7SVjg= +go.mau.fi/util v0.6.0/go.mod h1:ljYdq3sPfpICc3zMU+/mHV/sa4z0nKxc67hSBwnrk8U= +go.mau.fi/whatsmeow v0.0.0-20240726213518-bb5852f056ca h1:L0Pc6fi5RevuEASIP6Nd65/HZwCK8wTwm62FEly6UeY= +go.mau.fi/whatsmeow v0.0.0-20240726213518-bb5852f056ca/go.mod h1:BhHKalSq0qNtSCuGIUIvoJyU5KbT4a7k8DQ5yw1Ssk4= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= diff --git a/src/go/handle_message.go b/src/go/handle_message.go index 1494605..58c161b 100644 --- a/src/go/handle_message.go +++ b/src/go/handle_message.go @@ -14,12 +14,12 @@ import ( "strings" "time" - waProto "go.mau.fi/whatsmeow/binary/proto" + "go.mau.fi/whatsmeow/proto/waE2E" "go.mau.fi/whatsmeow/types" "golang.org/x/net/http2" ) -func (handler *Handler) handle_message(message *waProto.Message, id string, source types.MessageSource, name *string, timestamp time.Time, is_historical bool) { +func (handler *Handler) handle_message(message *waE2E.Message, id string, source types.MessageSource, name *string, timestamp time.Time, is_historical bool) { //handler.log.Infof("message: %#v", message) if source.Chat == types.StatusBroadcastJID && purple_get_bool(handler.account, C.GOWHATSAPP_IGNORE_STATUS_BROADCAST_OPTION, true) { handler.log.Warnf("Ignoring status broadcast.") @@ -27,85 +27,93 @@ func (handler *Handler) handle_message(message *waProto.Message, id string, sour // or other undesired behaviour such as just being annoying return } - text := "" - - if pm := message.GetProtocolMessage(); pm != nil { - if em := pm.GetEditedMessage(); em != nil { - message = em - text = "[EDIT] " + { + if pm := message.GetProtocolMessage(); pm != nil { + if em := pm.GetEditedMessage(); em != nil { + message = em + text = "[EDIT] " + } } } - text += message.GetConversation() - - etm := message.ExtendedTextMessage - if etm != nil { - // message containing quote or link to group - // link messages have message.Conversation set to nil anyway - // it should be safe to overwrite here - // quoted message repeats the text - ci := etm.ContextInfo - if ci != nil { - cm := ci.QuotedMessage - if cm != nil && cm.Conversation != nil { - quotelines := strings.Split(*cm.Conversation, "\n") - text = "> " + strings.Join(quotelines, "\n> ") + "\n" + { + etm := message.ExtendedTextMessage + if etm != nil { + // message containing quote or link to group + // link messages have message.Conversation set to nil anyway + // it should be safe to overwrite here + // quoted message repeats the text + ci := etm.ContextInfo + if ci != nil { + cm := ci.QuotedMessage + if cm != nil && cm.Conversation != nil { + quotelines := strings.Split(*cm.Conversation, "\n") + text = "> " + strings.Join(quotelines, "\n> ") + "\n" + } + } + if etm.Text != nil { + text += *etm.Text } } - if etm.Text != nil { - text += *etm.Text - } - } - im := message.GetImageMessage() - if im != nil && im.Caption != nil { - text += *message.GetImageMessage().Caption } - vm := message.GetVideoMessage() - if vm != nil && vm.Caption != nil { - text += *message.GetVideoMessage().Caption + { + im := message.GetImageMessage() + if im != nil && im.Caption != nil { + text += im.GetCaption() + } } - - rm := message.GetReactionMessage() - if rm != nil && rm.Text != nil && rm.Key != nil && rm.Key.ID != nil { - quote := "" - for i := range handler.cachedMessages { - if handler.cachedMessages[i].id == rm.Key.GetID() { - message := &handler.cachedMessages[i] - quote = fmt.Sprintf("message \"%.50s\" from %s", message.text, message.timestamp.Format(time.RFC822)) - // TODO: truncate string when storing, not when displaying - break - } + { + vm := message.GetVideoMessage() + if vm != nil && vm.Caption != nil { + text += vm.GetCaption() } - if quote == "" { - quote = fmt.Sprintf("unknown message with ID %s", rm.Key.GetID()) + } + { + ptv := message.GetPtvMessage() + if ptv != nil && ptv.Caption != nil { + text += ptv.GetCaption() } - if *rm.Text == "" { - text += fmt.Sprintf("removed their reaction to %s.", quote) - } else { - text += fmt.Sprintf("reacted with %s to %s.", *rm.Text, quote) + } + { + rm := message.GetReactionMessage() + if rm != nil && rm.Text != nil && rm.Key != nil && rm.Key.ID != nil { + quote := "" + for i := range handler.cachedMessages { + if handler.cachedMessages[i].id == rm.Key.GetID() { + message := &handler.cachedMessages[i] + quote = fmt.Sprintf("message \"%.50s\" from %s", message.text, message.timestamp.Format(time.RFC822)) + // TODO: truncate string when storing, not when displaying + break + } + } + if quote == "" { + quote = fmt.Sprintf("unknown message with ID %s", rm.Key.GetID()) + } + if *rm.Text == "" { + text += fmt.Sprintf("removed their reaction to %s.", quote) + } else { + text += fmt.Sprintf("reacted with %s to %s.", *rm.Text, quote) + } } } - if message.GetPollCreationMessage() != nil || message.GetPollCreationMessageV2() != nil || message.GetPollCreationMessageV3() != nil { text = "created a poll, but this plug-in cannot display polls." // TODO: display poll content // TODO: also use GetPollUpdateMessage() } - if text == "" { handler.log.Warnf("Received a message without any text.") } else { // note: info.PushName always denotes the sender (not the chat) - purple_display_text_message(handler.account, source.Chat.ToNonAD().String(), source.IsGroup, false, source.Sender.ToNonAD().String(), name, timestamp, text) + purple_display_text_message(handler.account, source.Chat.ToNonAD().String(), source.IsGroup, false, source.Sender.ToNonAD().String(), name, timestamp, text, &id) handler.addToCache(CachedMessage{id: id, text: text, timestamp: timestamp}) if !source.IsFromMe && !is_historical { // do not send receipt for own messages or historical messages handler.mark_read_defer(id, source.Chat, source.Sender) handler.mark_read_if_on_receival(source.Chat) } } - handler.handle_attachment(message, source) } @@ -121,7 +129,7 @@ func extension_from_mimetype(mimeType *string) string { } // based on https://github.com/FKLC/WhatsAppToDiscord/blob/master/WA2DC.go -func (handler *Handler) handle_attachment(message *waProto.Message, source types.MessageSource) { +func (handler *Handler) handle_attachment(message *waE2E.Message, source types.MessageSource) { var ( data []byte err error @@ -130,39 +138,56 @@ func (handler *Handler) handle_attachment(message *waProto.Message, source types mimetype *string ) chat := source.Chat.ToNonAD().String() - - im := message.GetImageMessage() - if im != nil { - data, err = handler.client.Download(im) - filename = hex.EncodeToString(im.GetFileSHA256()) + extension_from_mimetype(im.Mimetype) - data_type = C.gowhatsapp_attachment_type_image - mimetype = im.Mimetype - } - vm := message.GetVideoMessage() - if vm != nil { - data, err = handler.client.Download(vm) - filename = hex.EncodeToString(vm.GetFileSHA256()) + extension_from_mimetype(vm.Mimetype) - data_type = C.gowhatsapp_attachment_type_video - } - am := message.GetAudioMessage() - if am != nil { - data, err = handler.client.Download(am) - filename = hex.EncodeToString(am.GetFileSHA256()) + extension_from_mimetype(am.Mimetype) - data_type = C.gowhatsapp_attachment_type_audio - } - dm := message.GetDocumentMessage() - if dm != nil { - data, err = handler.client.Download(dm) - filename = *message.GetDocumentMessage().Title - // TODO: sanitize filename - data_type = C.gowhatsapp_attachment_type_document - } - sm := message.GetStickerMessage() - if sm != nil { - data, err = handler.client.Download(sm) - filename = hex.EncodeToString(sm.GetFileSHA256()) + extension_from_mimetype(sm.Mimetype) - data_type = C.gowhatsapp_attachment_type_sticker - mimetype = sm.Mimetype + { + im := message.GetImageMessage() + if im != nil { + data, err = handler.client.Download(im) + filename = hex.EncodeToString(im.GetFileSHA256()) + extension_from_mimetype(im.Mimetype) + data_type = C.gowhatsapp_attachment_type_image + mimetype = im.Mimetype + } + } + { + vm := message.GetVideoMessage() + if vm != nil { + data, err = handler.client.Download(vm) + filename = hex.EncodeToString(vm.GetFileSHA256()) + extension_from_mimetype(vm.Mimetype) + data_type = C.gowhatsapp_attachment_type_video + } + } + { + ptv := message.GetPtvMessage() + if ptv != nil { + data, err = handler.client.Download(ptv) + filename = hex.EncodeToString(ptv.GetFileSHA256()) + extension_from_mimetype(ptv.Mimetype) + data_type = C.gowhatsapp_attachment_type_video + } + } + { + am := message.GetAudioMessage() + if am != nil { + data, err = handler.client.Download(am) + filename = hex.EncodeToString(am.GetFileSHA256()) + extension_from_mimetype(am.Mimetype) + data_type = C.gowhatsapp_attachment_type_audio + } + } + { + dm := message.GetDocumentMessage() + if dm != nil { + data, err = handler.client.Download(dm) + filename = *message.GetDocumentMessage().Title + // TODO: sanitize filename + data_type = C.gowhatsapp_attachment_type_document + } + } + { + sm := message.GetStickerMessage() + if sm != nil { + data, err = handler.client.Download(sm) + filename = hex.EncodeToString(sm.GetFileSHA256()) + extension_from_mimetype(sm.Mimetype) + data_type = C.gowhatsapp_attachment_type_sticker + mimetype = sm.Mimetype + } } if err != nil { if len(data) == 0 { diff --git a/src/go/handler.go b/src/go/handler.go index 1281f4a..c9ecf79 100644 --- a/src/go/handler.go +++ b/src/go/handler.go @@ -133,14 +133,14 @@ func (handler *Handler) eventHandler(rawEvt interface{}) { chat := bcm.From.ToNonAD().String() sender := bcm.CallCreator.ToNonAD().String() text := "This contact is trying to call you, but WhatsApp Web does not support calls." - purple_display_text_message(handler.account, chat, false, false, sender, nil, bcm.Timestamp, text) + purple_display_text_message(handler.account, chat, false, false, sender, nil, bcm.Timestamp, text, nil) case *events.CallOfferNotice: // same as CallOffer, but is a group bcm := evt.BasicCallMeta chat := bcm.From.ToNonAD().String() sender := bcm.CallCreator.ToNonAD().String() text := "This contact is trying to call you, but WhatsApp Web does not support calls." - purple_display_text_message(handler.account, chat, true, false, sender, nil, bcm.Timestamp, text) + purple_display_text_message(handler.account, chat, true, false, sender, nil, bcm.Timestamp, text, nil) case *events.CallRelayLatency: // related to calls. ignore silently. case *events.CallTerminate: diff --git a/src/go/login.go b/src/go/login.go index 7c19adf..c9d69a9 100644 --- a/src/go/login.go +++ b/src/go/login.go @@ -16,11 +16,11 @@ import ( "github.com/mdp/qrterminal/v3" "github.com/skip2/go-qrcode" "go.mau.fi/whatsmeow" + "go.mau.fi/whatsmeow/proto/waCompanionReg" "go.mau.fi/whatsmeow/store" "go.mau.fi/whatsmeow/store/sqlstore" "go.mau.fi/whatsmeow/types" "google.golang.org/protobuf/proto" - waProto "go.mau.fi/whatsmeow/binary/proto" ) /* @@ -84,7 +84,7 @@ func login(account *PurpleAccount, purple_user_dir string, username string, cred store.DeviceProps.Os = proto.String("purple-whatsmeow") // limit fetching history since we cannot even parse it - store.DeviceProps.HistorySyncConfig = &waProto.DeviceProps_HistorySyncConfig{ + store.DeviceProps.HistorySyncConfig = &waCompanionReg.DeviceProps_HistorySyncConfig{ FullSyncDaysLimit: proto.Uint32(1), FullSyncSizeMbLimit: proto.Uint32(1), StorageQuotaMb: proto.Uint32(1), @@ -165,29 +165,51 @@ func login(account *PurpleAccount, purple_user_dir string, username string, cred err = handler.client.Connect() if err != nil { purple_error(handler.account, fmt.Sprintf("%#v", err), ERROR_TRANSIENT) + return } } +/* + * Generates an pairing 8-character pairing code. + */ +func (handler *Handler) generate_pairing_code() (string, error) { + own_jid, err := parseJID(handler.username) + if err != nil { + return "", fmt.Errorf("ā€ž%sā€œ is not a valid WhatsApp JID.", handler.username) + } + phone := own_jid.ToNonAD().User + showPushNotification := true + // cannot use store.DeviceProps.Os here, since "only common browsers/OSes are allowed", + // see https://pkg.go.dev/go.mau.fi/whatsmeow#Client.PairPhone + // Firefox on Linux chosen arbitrarily + clientDisplayName := "Firefox (Linux)" + clientType := whatsmeow.PairClientFirefox + pairing_code, err := handler.client.PairPhone(phone, showPushNotification, clientType, clientDisplayName) + return pairing_code, err +} + /* * After calling client.Connect() with a pristine device ID, WhatsApp servers * send a list of codes which can be turned into QR codes for scanning with the offical app. */ -func (handler *Handler) handle_qrcode(codes []string) { - code := codes[0] // use only first code for now +func (handler *Handler) handle_qrcode(qrcodes []string) { + pairing_code, err := handler.generate_pairing_code() + if err != nil { + purple_error(handler.account, fmt.Sprintf("%#v", err), ERROR_FATAL) + } + qrcode_data := qrcodes[0] // use only first code for now // TODO: emit events to destroy and update the code in the ui - var png []byte - var err error - var b strings.Builder - fmt.Fprintf(&b, "Scan this code to log in:\n%s\n", code) - qrterminal.GenerateHalfBlock(code, qrterminal.L, &b) + var stringBuilder strings.Builder + qrterminal.GenerateHalfBlock(qrcode_data, qrterminal.L, &stringBuilder) size := purple_get_int(handler.account, C.GOWHATSAPP_QRCODE_SIZE_OPTION, 256) + var png []byte if size > 0 { - png, err = qrcode.Encode(code, qrcode.Medium, size) + png, err = qrcode.Encode(qrcode_data, qrcode.Medium, size) if err != nil { purple_error(handler.account, fmt.Sprintf("%#v", err), ERROR_FATAL) } } - purple_display_qrcode(handler.account, b.String(), code, png) + purple_display_qrcode(handler.account, pairing_code, qrcode_data, stringBuilder.String(), png) } /* diff --git a/src/go/send_file.go b/src/go/send_file.go index d748e97..2852fb2 100644 --- a/src/go/send_file.go +++ b/src/go/send_file.go @@ -21,7 +21,7 @@ import ( // based on https://github.com/tulir/whatsmeow/blob/main/mdtest/main.go func (handler *Handler) send_file(who string, filename string) string { - isGroup := false // can only send to single contacts for now + isGroup := false // TODO: there is a gowhatsapp_chat_send_file, respect the group chat. recipient, err := parseJID(who) if err != nil { return fmt.Sprintf("%#v", err) diff --git a/src/go/send_message.go b/src/go/send_message.go index 3b85fee..f6cbbc0 100644 --- a/src/go/send_message.go +++ b/src/go/send_message.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "fmt" + "go.mau.fi/whatsmeow" waProto "go.mau.fi/whatsmeow/binary/proto" "go.mau.fi/whatsmeow/types" "google.golang.org/protobuf/proto" @@ -62,7 +63,8 @@ func (handler *Handler) send_text_message(recipient types.JID, isGroup bool, mes }, } } - resp, err := handler.client.SendMessage(context.Background(), recipient, msg) + msgID := handler.client.GenerateMessageID() + resp, err := handler.client.SendMessage(context.Background(), recipient, msg, whatsmeow.SendRequestExtra{ID: msgID}) if err != nil { errmsg := fmt.Sprintf("Error sending message: %v", err) purple_display_system_message(handler.account, recipient.ToNonAD().String(), isGroup, errmsg) @@ -73,7 +75,7 @@ func (handler *Handler) send_text_message(recipient types.JID, isGroup bool, mes if setting == C.GoString(C.GOWHATSAPP_ECHO_CHOICE_ON_SUCCESS) { ownJid := handler.client.Store.ID.ToNonAD().String() recipientJid := recipient.ToNonAD().String() - purple_display_text_message(handler.account, recipientJid, isGroup, true, ownJid, nil, resp.Timestamp, message) + purple_display_text_message(handler.account, recipientJid, isGroup, true, ownJid, nil, resp.Timestamp, message, &msgID) } handler.addToCache(CachedMessage{id: resp.ID, text: message, timestamp: resp.Timestamp}) return true diff --git a/submodules/purple-cmake b/submodules/purple-cmake index 5b6df4c..bd68bdc 160000 --- a/submodules/purple-cmake +++ b/submodules/purple-cmake @@ -1 +1 @@ -Subproject commit 5b6df4c64c15bb88d39c172bf7baeab160811115 +Subproject commit bd68bdc60d457e4d52a47cd7b6c2a866a16feb86