mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-06 03:07:32 +08:00
fix(telegram): accept /pair as a pairing-code alias (#7363)
Users habitually type /pair from the earlier pairing flow. Keep every suggested wording on /start (the vendor deep-link convention) and accept /pair <CODE> as a declared inbound-code-prefix alias so those users pair instead of looping through the connect nudge. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -401,11 +401,13 @@ fn fixture_with(
|
||||
deep_link_template: Option<&str>,
|
||||
template_values: BTreeMap<String, String>,
|
||||
) -> Fixture {
|
||||
// Mirrors the bundled Telegram manifest: `/start` (vendor deep-link
|
||||
// convention, the only suggested wording) plus the `/pair` alias.
|
||||
fixture_with_prefixes(
|
||||
installation,
|
||||
deep_link_template,
|
||||
template_values,
|
||||
&["/start"],
|
||||
&["/start", "/pair"],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1161,7 +1163,7 @@ fn direct_message(text: &str, actor_id: &str) -> NormalizedInboundMessage {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interceptor_services_manifest_declared_start_messages_only() {
|
||||
async fn interceptor_services_manifest_declared_code_prefixes_only() {
|
||||
let fixture = fixture();
|
||||
let issue = fixture
|
||||
.service
|
||||
@@ -1184,10 +1186,10 @@ async fn interceptor_services_manifest_declared_start_messages_only() {
|
||||
fixture.service.intercept(&install(), &group).await,
|
||||
ChannelPairingInterception::NotHandled
|
||||
);
|
||||
// `/pair` remains ordinary text because Telegram declares only `/start`.
|
||||
let pair = direct_message(&format!("/pair {}", issue.code.as_str()), "u-1");
|
||||
// An undeclared command remains ordinary text.
|
||||
let link = direct_message(&format!("/link {}", issue.code.as_str()), "u-1");
|
||||
assert_eq!(
|
||||
fixture.service.intercept(&install(), &pair).await,
|
||||
fixture.service.intercept(&install(), &link).await,
|
||||
ChannelPairingInterception::NotHandled
|
||||
);
|
||||
|
||||
@@ -1208,6 +1210,21 @@ async fn interceptor_services_manifest_declared_start_messages_only() {
|
||||
.expect("lookup"),
|
||||
Some(user("alice"))
|
||||
);
|
||||
|
||||
// The `/pair` alias is equally declared: a bound sender re-sending a
|
||||
// fresh code through it is serviced as the idempotent repair path.
|
||||
let repair = fixture
|
||||
.service
|
||||
.issue_or_rotate(&user("alice"))
|
||||
.await
|
||||
.expect("re-mint");
|
||||
let pair = direct_message(&format!("/pair {}", repair.code.as_str()), "u-1");
|
||||
assert_eq!(
|
||||
fixture.service.intercept(&install(), &pair).await,
|
||||
ChannelPairingInterception::Consumed(ChannelPairingConsumeOutcome::AlreadyPairedSameUser {
|
||||
user_id: user("alice"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -43,7 +43,10 @@ submit_label = "Open pairing"
|
||||
error_message = "Telegram pairing failed. Get a fresh code and try again."
|
||||
connection_success_message = "Telegram is installed as an inbound entrypoint. If WebChat shows a Telegram pairing panel, tell the user to pair via the link, the QR code, or by sending the shown code to the bot in Telegram — nothing is pasted into normal chat. Once paired the user can DM the bot directly. Telegram exposes no tools and cannot read messages or send on the user's behalf."
|
||||
deep_link_template = "https://t.me/{bot_username}?start={code}"
|
||||
inbound_code_prefixes = ["/start"]
|
||||
# `/start` is the vendor deep-link convention and stays the only prefix any
|
||||
# instruction wording suggests; `/pair` is an accepted alias for users who
|
||||
# type it from habit and must never appear in suggested wording.
|
||||
inbound_code_prefixes = ["/start", "/pair"]
|
||||
|
||||
[channel.connection.notices]
|
||||
connect_required = "👋 Pair your Telegram account in the IronClaw web app, then message me here again."
|
||||
|
||||
@@ -2645,7 +2645,11 @@ async fn unbound_telegram_actor_pairs_via_web_minted_code_then_turns_attribute_t
|
||||
|
||||
// 6. Mint through the web-side pairing service and consume through the
|
||||
// real verified webhook again. No direct store/service mutation repairs
|
||||
// the actor binding in this journey.
|
||||
// the actor binding in this journey. This leg pairs via the plain
|
||||
// `/pair CODE` alias (the manifest's second declared prefix — kept for
|
||||
// muscle-memory compatibility while all suggested wording stays
|
||||
// `/start`), so both declared prefixes and the untargeted command shape
|
||||
// stay pinned through the real bundled manifest.
|
||||
let repaired_code = services
|
||||
.pairing_mint_for_test("telegram", &paired_user)
|
||||
.await
|
||||
@@ -2653,7 +2657,7 @@ async fn unbound_telegram_actor_pairs_via_web_minted_code_then_turns_attribute_t
|
||||
let status = ingress
|
||||
.post(
|
||||
TELEGRAM_ROUTE,
|
||||
&targeted_start_body(606, 515151, &repaired_code),
|
||||
&dm_body(606, 515151, &format!("/pair {repaired_code}")),
|
||||
vec![(
|
||||
"X-Telegram-Bot-Api-Secret-Token",
|
||||
TELEGRAM_WEBHOOK_SECRET.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user