仅用公开用户ID在Telegram发消息(无需Access Hash)的实现方案
Telegram用户客户端发送消息:仅知公开ID时的解决方案
背景
以普通用户账户(非机器人)开发Telegram集成功能,需向仅知道公开数字用户ID的用户发送消息,但MTProto协议通常要求同时提供用户ID和access hash,现有代码在未缓存access hash时执行失败。
具体疑问与解答
1. 是否存在无需access hash即可发送消息的方法?
不行。Telegram的MTProto协议出于安全和反垃圾机制的设计,发送消息到陌生用户时必须提供正确的access hash,无法绕开这一要求。
2. 仅用公开用户ID程序化获取access hash的方法
获取access hash的前提是目标用户对你的账号可见,以下分库给出可行方案:
gram.js实现
如果目标用户有公开username,可通过username关联数字ID获取实体;若已添加为联系人或有过交互,直接用数字ID即可获取:
const { TelegramClient } = require('telegram'); const { StringSession } = require('telegram/sessions'); const apiId = YOUR_API_ID; const apiHash = 'YOUR_API_HASH'; const stringSession = new StringSession('YOUR_SESSION'); const client = new TelegramClient(stringSession, apiId, apiHash, { connectionRetries: 5 }); async function getAccessHash(userId) { await client.connect(); try { // 尝试直接通过ID获取实体(仅当有交互/在联系人时有效) const entity = await client.getEntity(userId); return entity.accessHash; } catch (error) { // 若失败,尝试通过username搜索(需提前知道目标用户的username) const searchResults = await client.invoke(new Api.contacts.Search({ q: 'TARGET_USERNAME', limit: 1 })); if (searchResults.users.length > 0) { const user = searchResults.users[0]; if (user.id === userId) { return user.accessHash; } } throw new Error('无法获取access hash:用户不可见或未找到对应username'); } }
Telethon实现
逻辑与gram.js一致,通过实体查询或搜索获取:
from telethon import TelegramClient from telethon.tl.types import User api_id = YOUR_API_ID api_hash = 'YOUR_API_HASH' session_name = 'telethon_session' client = TelegramClient(session_name, api_id, api_hash) async def get_access_hash(user_id): await client.start() try: # 直接通过ID获取实体 entity = await client.get_entity(user_id) if isinstance(entity, User): return entity.access_hash except: # 通过username搜索匹配 results = await client.search_users('TARGET_USERNAME') for user in results: if user.id == user_id: return user.access_hash raise ValueError('无法获取目标用户的access hash')
TDLib实现
通过getUser或searchPublicChats接口获取用户实体:
#include <td/telegram/Client.h> #include <td/telegram/td_api.h> using namespace td; void get_access_hash(Client &client, int64_t user_id) { // 尝试直接获取用户 auto user_request = make_object<td_api::getUser>(user_id); client.send(std::move(user_request), [&client, user_id](ObjectPtr<Object> object) { if (auto error = object->as_error()) { // 失败则尝试搜索公开username auto search_request = make_object<td_api::searchPublicChats>("TARGET_USERNAME"); client.send(std::move(search_request), [&client, user_id](ObjectPtr<Object> search_obj) { if (auto chats = search_obj->as_chats()) { for (auto &chat : chats->chats_) { if (auto public_chat = chat->as_publicChat()) { if (public_chat->user_id_ == user_id) { // 再次获取用户实体 auto user_req = make_object<td_api::getUser>(user_id); client.send(std::move(user_req), [](ObjectPtr<Object> user_obj) { if (auto user = user_obj->as_user()) { int64_t access_hash = user->access_hash_; } }); } } } } }); } else if (auto user = object->as_user()) { int64_t access_hash = user->access_hash_; } }); }
注意:如果目标用户无公开username,且未与你的账号有过交互、不在联系人列表中,无法仅通过数字ID获取access hash——这是Telegram的隐私限制。
3. 替代方案或变通方法
如果无法直接获取access hash,可尝试以下变通方式:
添加临时联系人
通过contacts.addContact接口将目标用户添加为联系人(无需对方同意),添加成功后即可获取其access hash:
async function addContactAndSend(userId, message) { await client.connect(); // 添加联系人(accessHash填0即可发起请求) await client.invoke(new Api.contacts.AddContact({ id: new Api.InputUser({ userId, accessHash: 0 }), firstName: "临时联系人", lastName: "", phone: "", addPhonePrivacyException: false })); // 添加后获取实体并发送消息 const entity = await client.getEntity(userId); await client.sendMessage(entity, { message }); console.log("消息发送成功"); }
公共群组/频道中转
若目标用户加入了某个公共群组或频道,可通过获取群组参与者列表找到目标用户的实体,进而拿到access hash:
async function getAccessHashFromGroup(userId, groupId) { await client.connect(); const participants = await client.getParticipants(groupId, { limit: 100 }); for (const participant of participants) { if (participant.userId === userId) { return participant.accessHash; } } throw new Error("目标用户不在该群组中"); }
内容的提问来源于stack exchange,提问作者Drop
相关产品推荐
相关产品推荐

