From cc1336ebb63174a4017e35a21b86c8e95d03bb01 Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Fri, 30 Jan 2026 17:41:30 +0000 Subject: [PATCH 1/6] Some fixes - Hide "Delete" button for DM/Group DM messages that aren't yours - Fix Pin/Edit message checks in handler --- src/windows/MessageList.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index 08c34607..89862c11 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -3288,6 +3288,13 @@ void MessageList::HandleRightClickMenuCommand(int command) } case ID_DUMMYPOPUP_EDITMESSAGE: { + Channel* pChan = GetDiscordInstance()->GetCurrentChannel(); + if (!pChan) + break; + + if (!pChan->HasPermission(PERM_SEND_MESSAGES)) + break; + SendMessage(g_Hwnd, WM_STARTEDITING, 0, (LPARAM) &rightClickedMessage); break; } @@ -3336,7 +3343,7 @@ void MessageList::HandleRightClickMenuCommand(int command) if (!pChan) break; - if (!pChan->HasPermission(PERM_SEND_MESSAGES)) + if (!pChan->HasPermission(PERM_MANAGE_MESSAGES)) break; static char buffer[8192]; @@ -3685,9 +3692,10 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA bool maySendMessages = pChan->HasPermission(PERM_SEND_MESSAGES); bool isActionMessage = IsActionMessage(pRCMsg->m_msg->m_type) || IsClientSideMessage(pRCMsg->m_msg->m_type); bool isForward = pRCMsg->m_msg->m_bIsForward; + bool isDM = pChan->IsDM(); bool mayCopy = !isForward && !isActionMessage; - bool mayDelete = isThisMyMessage || mayManageMessages; + bool mayDelete = isThisMyMessage || (mayManageMessages && !isDM); bool mayEdit = isThisMyMessage && !isForward && !isActionMessage && maySendMessages; bool mayPin = mayManageMessages; bool maySpeak = !isActionMessage && !pRCMsg->m_msg->m_message.empty(); From 51bae90d6b9acd6ee5f7aaeae6aac0f3bbd3021a Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:06:41 +0000 Subject: [PATCH 2/6] Add support for Pinning and Unpinning messages --- src/core/DiscordInstance.cpp | 30 +++++++++++++++++++++++++++++ src/core/DiscordInstance.hpp | 6 ++++++ src/core/models/Message.cpp | 3 +++ src/core/models/Message.hpp | 1 + src/core/network/DiscordRequest.hpp | 1 + src/resource.h | 5 ++++- src/resource.rc | 3 +++ src/windows/MessageList.cpp | 29 ++++++++++++++++++++++++++-- 8 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/core/DiscordInstance.cpp b/src/core/DiscordInstance.cpp index bf1df456..d6df7bc5 100644 --- a/src/core/DiscordInstance.cpp +++ b/src/core/DiscordInstance.cpp @@ -1541,6 +1541,36 @@ void DiscordInstance::RequestDeleteMessage(Snowflake chan, Snowflake msg) ); } +void DiscordInstance::RequestPinMessage(Snowflake chan, Snowflake msg) +{ + std::string url = GetDiscordAPI() + "channels/" + std::to_string(chan) + "/messages/pins/" + std::to_string(msg); + + GetHTTPClient()->PerformRequest( + true, + NetRequest::PUT, + url, + 0, + DiscordRequest::SET_PINNED, + "", + m_token + ); +} + +void DiscordInstance::RequestUnpinMessage(Snowflake chan, Snowflake msg) +{ + std::string url = GetDiscordAPI() + "channels/" + std::to_string(chan) + "/messages/pins/" + std::to_string(msg); + + GetHTTPClient()->PerformRequest( + true, + NetRequest::DELETE_, + url, + 0, + DiscordRequest::SET_PINNED, + "", + m_token + ); +} + void DiscordInstance::UpdateSubscriptions(Snowflake guildId, Snowflake channelId, bool typing, bool activities, bool threads, int rangeMembers) { Json j, data; diff --git a/src/core/DiscordInstance.hpp b/src/core/DiscordInstance.hpp index db7d2a1b..35d8c728 100644 --- a/src/core/DiscordInstance.hpp +++ b/src/core/DiscordInstance.hpp @@ -462,6 +462,12 @@ class DiscordInstance // Request a message deletion. void RequestDeleteMessage(Snowflake chan, Snowflake msg); + // Request a message to be pinned. + void RequestPinMessage(Snowflake chan, Snowflake msg); + + // Request a message to be unpinned. + void RequestUnpinMessage(Snowflake chan, Snowflake msg); + // Request to leave a guild. void RequestLeaveGuild(Snowflake guild); diff --git a/src/core/models/Message.cpp b/src/core/models/Message.cpp index aac15896..061a25fe 100644 --- a/src/core/models/Message.cpp +++ b/src/core/models/Message.cpp @@ -288,6 +288,9 @@ void Message::Load(Json& data, Snowflake guild) m_avatar = pf->m_avatarlnk; } + if (data["pinned"].is_boolean()) + m_bIsPinned = data["pinned"]; + if (data["attachments"].is_array()) { m_attachments.clear(); diff --git a/src/core/models/Message.hpp b/src/core/models/Message.hpp index d1cb9e8b..790aad43 100644 --- a/src/core/models/Message.hpp +++ b/src/core/models/Message.hpp @@ -111,6 +111,7 @@ class Message std::set m_roleMentions; bool m_bMentionedEveryone = false; bool m_bIsAuthorBot = false; + bool m_bIsPinned = false; bool m_bRead = false; // valid only for the notification viewer messages bool m_bIsForward = false; Snowflake m_refMessageGuild = 0; diff --git a/src/core/network/DiscordRequest.hpp b/src/core/network/DiscordRequest.hpp index faab2d23..648ed4f2 100644 --- a/src/core/network/DiscordRequest.hpp +++ b/src/core/network/DiscordRequest.hpp @@ -16,6 +16,7 @@ namespace DiscordRequest DELETE_MESSAGE, ACK, PINS, + SET_PINNED, MESSAGE_CREATE, UPLOAD_ATTACHMENT, UPLOAD_ATTACHMENT_2, diff --git a/src/resource.h b/src/resource.h index 000e1a71..c6ee8382 100644 --- a/src/resource.h +++ b/src/resource.h @@ -333,6 +333,8 @@ #define IDS_SAVED_STRING_EXE 773 #define IDS_GET_TOKEN_TUTORIAL 774 #define IDS_CANT_LAUNCH_URL_UNS 775 +#define IDS_CONFIRM_UNPIN 776 +#define IDS_CONFIRM_UNPIN_TITLE 777 #define IDC_OPTIONS_TABS 801 #define IDC_MY_ACCOUNT_BOX 802 #define IDC_MY_ACCOUNT_NAME 803 @@ -513,6 +515,7 @@ #define ID_DUMMY_SAVEIMAGE 1081 #define ID_DUMMY_COPYIMAGE 1082 #define ID_DUMMY_OPENIMAGELINK 1083 +#define ID_DUMMYPOPUP_UNPINMESSAGE 1099 #define IDR_MAIN_ACCELS 1201 #define IDA_SEARCH 1301 #define IDA_QUICKSWITCHER 1302 @@ -536,7 +539,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 -#define _APS_NEXT_COMMAND_VALUE 1099 +#define _APS_NEXT_COMMAND_VALUE 1100 #define _APS_NEXT_CONTROL_VALUE 918 #define _APS_NEXT_SYMED_VALUE 40000 #endif diff --git a/src/resource.rc b/src/resource.rc index 6cee042d..c97c5c1e 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -133,6 +133,7 @@ BEGIN END MENUITEM "Edit Message", ID_DUMMYPOPUP_EDITMESSAGE MENUITEM "Pin Message", ID_DUMMYPOPUP_PINMESSAGE + MENUITEM "Unpin Message", ID_DUMMYPOPUP_UNPINMESSAGE MENUITEM "Reply", ID_DUMMYPOPUP_REPLY MENUITEM "Copy Text", ID_DUMMYPOPUP_COPYTEXT MENUITEM "Mark Unread", ID_DUMMYPOPUP_MARKUNREAD @@ -1687,6 +1688,8 @@ BEGIN IDS_SAVED_STRING_EXE "Your downloaded file was saved at the following path: %s\n\nWARNING: This file might be malicious. Make sure you trust the sender/author of this file.\nWould you like to open it?" IDS_GET_TOKEN_TUTORIAL "Here are a few steps you can use to get your Discord token:\n\no Open a web browser, then navigate to discord.com\n\no Log in using your Discord account.\n\no Navigate to any channel.\n\no Press Ctrl+Shift+I while focused on that channel.\n\no Navigate to the Network tab, and then send a message.\n\no Check the ""messages"" request, and in the Request Headers, find the ""Authorization"" string.\n\no That's your token! Make sure to copy it without any quote marks, then paste it here.\n\nEnjoy!" IDS_CANT_LAUNCH_URL_UNS "The specified link %s couldn't be opened, because Windows returned ""%s"". Most likely, your Windows version does not support directly opening URLs." + IDS_CONFIRM_UNPIN "Are you sure you want to unpin this message?\n\n%s (%s)\n\n%s" + IDS_CONFIRM_UNPIN_TITLE "Discord Messenger - Unpin Message" END #endif // English (United States) resources diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index 89862c11..e2b65684 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -3353,7 +3353,29 @@ void MessageList::HandleRightClickMenuCommand(int command) if (MessageBox(g_Hwnd, xstr, TmGetTString(IDS_CONFIRM_PIN_TITLE), MB_YESNO | MB_ICONQUESTION) == IDYES) { - // TODO + GetDiscordInstance()->RequestPinMessage(m_channelID, rightClickedMessage); + } + + free((void*)xstr); + break; + } + case ID_DUMMYPOPUP_UNPINMESSAGE: + { + Channel* pChan = GetDiscordInstance()->GetCurrentChannel(); + if (!pChan) + break; + + if (!pChan->HasPermission(PERM_MANAGE_MESSAGES)) + break; + + static char buffer[8192]; + snprintf(buffer, sizeof buffer, TmGetString(IDS_CONFIRM_UNPIN).c_str(), pMsg->m_msg->m_author.c_str(), pMsg->m_msg->m_dateFull.c_str(), pMsg->m_msg->m_message.c_str()); + + LPCTSTR xstr = ConvertCppStringToTString(buffer); + + if (MessageBox(g_Hwnd, xstr, TmGetTString(IDS_CONFIRM_UNPIN_TITLE), MB_YESNO | MB_ICONQUESTION) == IDYES) + { + GetDiscordInstance()->RequestUnpinMessage(m_channelID, rightClickedMessage); } free((void*)xstr); @@ -3692,12 +3714,14 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA bool maySendMessages = pChan->HasPermission(PERM_SEND_MESSAGES); bool isActionMessage = IsActionMessage(pRCMsg->m_msg->m_type) || IsClientSideMessage(pRCMsg->m_msg->m_type); bool isForward = pRCMsg->m_msg->m_bIsForward; + bool isPinned = pRCMsg->m_msg->m_bIsPinned; bool isDM = pChan->IsDM(); bool mayCopy = !isForward && !isActionMessage; bool mayDelete = isThisMyMessage || (mayManageMessages && !isDM); bool mayEdit = isThisMyMessage && !isForward && !isActionMessage && maySendMessages; - bool mayPin = mayManageMessages; + bool mayPin = mayManageMessages && !isPinned; + bool mayUnpin = mayManageMessages && isPinned; bool maySpeak = !isActionMessage && !pRCMsg->m_msg->m_message.empty(); bool mayReply = (!isActionMessage || IsReplyableActionMessage(pRCMsg->m_msg->m_type)) && maySendMessages; @@ -3712,6 +3736,7 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (!mayDelete) RemoveMenu(menu, ID_DUMMYPOPUP_DELETEMESSAGE, MF_BYCOMMAND); if (!mayEdit) RemoveMenu(menu, ID_DUMMYPOPUP_EDITMESSAGE, MF_BYCOMMAND); if (!mayPin) RemoveMenu(menu, ID_DUMMYPOPUP_PINMESSAGE, MF_BYCOMMAND); + if (!mayUnpin) RemoveMenu(menu, ID_DUMMYPOPUP_UNPINMESSAGE, MF_BYCOMMAND); if (!maySpeak) RemoveMenu(menu, ID_DUMMYPOPUP_SPEAKMESSAGE, MF_BYCOMMAND); if (!mayCopy) RemoveMenu(menu, ID_DUMMYPOPUP_COPYTEXT, MF_BYCOMMAND); if (!mayReply) RemoveMenu(menu, ID_DUMMYPOPUP_REPLY, MF_BYCOMMAND); From 19733a69f4bdc4a3d69d09947710e7a945b2f020 Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:07:20 +0000 Subject: [PATCH 3/6] Fix oldwindows compat --- src/windows/MessageList.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index e2b65684..92e3e291 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -3721,7 +3721,7 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA bool mayDelete = isThisMyMessage || (mayManageMessages && !isDM); bool mayEdit = isThisMyMessage && !isForward && !isActionMessage && maySendMessages; bool mayPin = mayManageMessages && !isPinned; - bool mayUnpin = mayManageMessages && isPinned; + bool mayUnpin = mayManageMessages && isPinned; bool maySpeak = !isActionMessage && !pRCMsg->m_msg->m_message.empty(); bool mayReply = (!isActionMessage || IsReplyableActionMessage(pRCMsg->m_msg->m_type)) && maySendMessages; @@ -3729,6 +3729,7 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA EnableMenuItem(menu, ID_DUMMYPOPUP_DELETEMESSAGE, mayDelete ? MF_ENABLED : MF_GRAYED); EnableMenuItem(menu, ID_DUMMYPOPUP_EDITMESSAGE, mayEdit ? MF_ENABLED : MF_GRAYED); EnableMenuItem(menu, ID_DUMMYPOPUP_PINMESSAGE, mayPin ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(menu, ID_DUMMYPOPUP_UNPINMESSAGE, mayUnpin ? MF_ENABLED : MF_GRAYED); EnableMenuItem(menu, ID_DUMMYPOPUP_SPEAKMESSAGE, maySpeak ? MF_ENABLED : MF_GRAYED); EnableMenuItem(menu, ID_DUMMYPOPUP_COPYTEXT, mayCopy ? MF_ENABLED : MF_GRAYED); EnableMenuItem(menu, ID_DUMMYPOPUP_REPLY, mayReply ? MF_ENABLED : MF_GRAYED); From 168880427d98a73f2b702016e25fb9ed7ae2c7e5 Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Fri, 30 Jan 2026 20:50:47 +0000 Subject: [PATCH 4/6] Use accurate confirm unpin text --- src/resource.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resource.rc b/src/resource.rc index c97c5c1e..c042b0c4 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -1688,7 +1688,7 @@ BEGIN IDS_SAVED_STRING_EXE "Your downloaded file was saved at the following path: %s\n\nWARNING: This file might be malicious. Make sure you trust the sender/author of this file.\nWould you like to open it?" IDS_GET_TOKEN_TUTORIAL "Here are a few steps you can use to get your Discord token:\n\no Open a web browser, then navigate to discord.com\n\no Log in using your Discord account.\n\no Navigate to any channel.\n\no Press Ctrl+Shift+I while focused on that channel.\n\no Navigate to the Network tab, and then send a message.\n\no Check the ""messages"" request, and in the Request Headers, find the ""Authorization"" string.\n\no That's your token! Make sure to copy it without any quote marks, then paste it here.\n\nEnjoy!" IDS_CANT_LAUNCH_URL_UNS "The specified link %s couldn't be opened, because Windows returned ""%s"". Most likely, your Windows version does not support directly opening URLs." - IDS_CONFIRM_UNPIN "Are you sure you want to unpin this message?\n\n%s (%s)\n\n%s" + IDS_CONFIRM_UNPIN "You sure you want to remove this pinned message?\n\n%s (%s)\n\n%s" IDS_CONFIRM_UNPIN_TITLE "Discord Messenger - Unpin Message" END From 23f0705bed32d522c6ca5b52ba8d3bb263d86751 Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:45:37 +0000 Subject: [PATCH 5/6] Use differentiable DiscordRequest enums for Pin and Unpin --- src/core/DiscordInstance.cpp | 4 ++-- src/core/network/DiscordRequest.hpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/DiscordInstance.cpp b/src/core/DiscordInstance.cpp index d6df7bc5..601e2da9 100644 --- a/src/core/DiscordInstance.cpp +++ b/src/core/DiscordInstance.cpp @@ -1550,7 +1550,7 @@ void DiscordInstance::RequestPinMessage(Snowflake chan, Snowflake msg) NetRequest::PUT, url, 0, - DiscordRequest::SET_PINNED, + DiscordRequest::PIN_MESSAGE, "", m_token ); @@ -1565,7 +1565,7 @@ void DiscordInstance::RequestUnpinMessage(Snowflake chan, Snowflake msg) NetRequest::DELETE_, url, 0, - DiscordRequest::SET_PINNED, + DiscordRequest::UNPIN_MESSAGE, "", m_token ); diff --git a/src/core/network/DiscordRequest.hpp b/src/core/network/DiscordRequest.hpp index 648ed4f2..8ee41b4c 100644 --- a/src/core/network/DiscordRequest.hpp +++ b/src/core/network/DiscordRequest.hpp @@ -16,7 +16,8 @@ namespace DiscordRequest DELETE_MESSAGE, ACK, PINS, - SET_PINNED, + PIN_MESSAGE, + UNPIN_MESSAGE, MESSAGE_CREATE, UPLOAD_ATTACHMENT, UPLOAD_ATTACHMENT_2, From 977d28936b8cdaa81245277e248e5eb50e582003 Mon Sep 17 00:00:00 2001 From: ZyroX <79484310+okzyrox@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:10:19 +0000 Subject: [PATCH 6/6] Prevent pinning action messages --- src/windows/MessageList.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index 92e3e291..4b0c1202 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -3345,6 +3345,9 @@ void MessageList::HandleRightClickMenuCommand(int command) if (!pChan->HasPermission(PERM_MANAGE_MESSAGES)) break; + + if (IsActionMessage(pMsg->m_msg->m_type)) + break; static char buffer[8192]; snprintf(buffer, sizeof buffer, TmGetString(IDS_CONFIRM_PIN).c_str(), pChan->m_name.c_str(), pMsg->m_msg->m_author.c_str(), pMsg->m_msg->m_dateFull.c_str(), pMsg->m_msg->m_message.c_str()); @@ -3367,6 +3370,9 @@ void MessageList::HandleRightClickMenuCommand(int command) if (!pChan->HasPermission(PERM_MANAGE_MESSAGES)) break; + + if (IsActionMessage(pMsg->m_msg->m_type)) + break; static char buffer[8192]; snprintf(buffer, sizeof buffer, TmGetString(IDS_CONFIRM_UNPIN).c_str(), pMsg->m_msg->m_author.c_str(), pMsg->m_msg->m_dateFull.c_str(), pMsg->m_msg->m_message.c_str()); @@ -3720,8 +3726,8 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA bool mayCopy = !isForward && !isActionMessage; bool mayDelete = isThisMyMessage || (mayManageMessages && !isDM); bool mayEdit = isThisMyMessage && !isForward && !isActionMessage && maySendMessages; - bool mayPin = mayManageMessages && !isPinned; - bool mayUnpin = mayManageMessages && isPinned; + bool mayPin = mayManageMessages && !isPinned && !isActionMessage; + bool mayUnpin = mayManageMessages && isPinned && !isActionMessage; bool maySpeak = !isActionMessage && !pRCMsg->m_msg->m_message.empty(); bool mayReply = (!isActionMessage || IsReplyableActionMessage(pRCMsg->m_msg->m_type)) && maySendMessages;