fix: telegram allowFrom matching and entity resolution
Fixes three critical bugs in Telegram integration:
1. Case-insensitive username matching
- Telegram usernames are case-insensitive, but the API returns them with original casing
- Now lowercase all usernames when extracting sender identifier to match config entries
- Fixes: messages from @ArneMoor now match config entry telegram:@arnemoor
2. Entity resolution for sending replies
- resolveEntity() now strips telegram: prefix before resolving users/chats
- Fixes: "Could not resolve Telegram entity: telegram:@username" errors when sending replies
3. Improved skip logging
- Messages skipped due to allowFrom filtering now always logged (not just in verbose mode)
- Uses clear emoji indicator: ⏭️ Skipped message from...
This commit is contained in:
parent
7290734f2b
commit
31246f0a34
@ -60,13 +60,14 @@ export async function convertTelegramMessage(
|
|||||||
* Extract sender identifier with telegram: prefix to prevent session ID collisions.
|
* Extract sender identifier with telegram: prefix to prevent session ID collisions.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* - telegram:@username (if username available)
|
* - telegram:@username (if username available, lowercase for case-insensitive matching)
|
||||||
* - telegram:+phone (if phone available, normalized to E.164)
|
* - telegram:+phone (if phone available, normalized to E.164)
|
||||||
* - telegram:id (numeric Telegram ID as fallback)
|
* - telegram:id (numeric Telegram ID as fallback)
|
||||||
*/
|
*/
|
||||||
function extractSenderIdentifier(sender: Api.User | Api.Chat): string {
|
function extractSenderIdentifier(sender: Api.User | Api.Chat): string {
|
||||||
if ("username" in sender && sender.username) {
|
if ("username" in sender && sender.username) {
|
||||||
return `telegram:@${sender.username}`;
|
// Lowercase username for case-insensitive matching (Telegram usernames are case-insensitive)
|
||||||
|
return `telegram:@${sender.username.toLowerCase()}`;
|
||||||
}
|
}
|
||||||
if ("phone" in sender && sender.phone) {
|
if ("phone" in sender && sender.phone) {
|
||||||
// Ensure phone has + prefix for E.164 format to match normalized allowFrom entries
|
// Ensure phone has + prefix for E.164 format to match normalized allowFrom entries
|
||||||
@ -209,6 +210,10 @@ export function isAllowedSender(
|
|||||||
return true; // No whitelist = allow all
|
return true; // No whitelist = allow all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowFrom.includes("*")) {
|
||||||
|
return true; // Wildcard allows all senders
|
||||||
|
}
|
||||||
|
|
||||||
const normalizedFrom = normalizeAllowFromEntry(message.from, "telegram");
|
const normalizedFrom = normalizeAllowFromEntry(message.from, "telegram");
|
||||||
const normalizedAllowList = allowFrom.map((e) =>
|
const normalizedAllowList = allowFrom.map((e) =>
|
||||||
normalizeAllowFromEntry(e, "telegram"),
|
normalizeAllowFromEntry(e, "telegram"),
|
||||||
|
|||||||
@ -170,11 +170,10 @@ async function handleInboundMessage(
|
|||||||
normalizeAllowFromEntry(e, "telegram"),
|
normalizeAllowFromEntry(e, "telegram"),
|
||||||
);
|
);
|
||||||
if (!normalizedAllowList.includes(normalizedFrom)) {
|
if (!normalizedAllowList.includes(normalizedFrom)) {
|
||||||
if (isVerbose()) {
|
// Always log skipped messages (not just in verbose mode)
|
||||||
logVerbose(
|
runtime.log(
|
||||||
`Skipping auto-reply: sender ${message.from} not in allowFrom list`,
|
`⏭️ Skipped message from ${message.from} (not in allowFrom list)`,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,17 @@ type Entity = Api.User | Api.Chat | Api.Channel;
|
|||||||
/**
|
/**
|
||||||
* Resolve Telegram entity (user/chat) from identifier.
|
* Resolve Telegram entity (user/chat) from identifier.
|
||||||
* Supports @username, phone number, or user ID.
|
* Supports @username, phone number, or user ID.
|
||||||
|
* Also handles telegram: prefix (e.g., telegram:@username).
|
||||||
*/
|
*/
|
||||||
export async function resolveEntity(
|
export async function resolveEntity(
|
||||||
client: TelegramClient,
|
client: TelegramClient,
|
||||||
identifier: string,
|
identifier: string,
|
||||||
): Promise<Entity> {
|
): Promise<Entity> {
|
||||||
// Clean identifier
|
// Clean identifier and strip telegram: prefix if present
|
||||||
const clean = identifier.trim();
|
let clean = identifier.trim();
|
||||||
|
if (clean.startsWith("telegram:")) {
|
||||||
|
clean = clean.slice("telegram:".length);
|
||||||
|
}
|
||||||
|
|
||||||
// Try as-is first (handles @username, phone, user ID)
|
// Try as-is first (handles @username, phone, user ID)
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user