refactor(signal): normalize sender identity
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
- Telegram: retry long-polling conflicts with backoff to avoid fatal exits.
|
- Telegram: retry long-polling conflicts with backoff to avoid fatal exits.
|
||||||
- Telegram: fix grammY fetch type mismatch when injecting `fetch`. (#512) — thanks @YuriNachos
|
- Telegram: fix grammY fetch type mismatch when injecting `fetch`. (#512) — thanks @YuriNachos
|
||||||
- WhatsApp: resolve @lid JIDs via Baileys mapping to unblock inbound messages. (#415)
|
- WhatsApp: resolve @lid JIDs via Baileys mapping to unblock inbound messages. (#415)
|
||||||
|
- Signal: accept UUID-only senders for pairing/allowlists/routing when sourceNumber is missing. (#523) — thanks @neist
|
||||||
- Agent system prompt: avoid automatic self-updates unless explicitly requested.
|
- Agent system prompt: avoid automatic self-updates unless explicitly requested.
|
||||||
- Onboarding: tighten QuickStart hint copy for configuring later.
|
- Onboarding: tighten QuickStart hint copy for configuring later.
|
||||||
- Onboarding: avoid “token expired” for Codex CLI when expiry is heuristic.
|
- Onboarding: avoid “token expired” for Codex CLI when expiry is heuristic.
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -460,10 +460,10 @@ Thanks to all clawtributors:
|
|||||||
<a href="https://github.com/claude"><img src="https://avatars.githubusercontent.com/u/81847?v=4&s=48" width="48" height="48" alt="claude" title="claude"/></a> <a href="https://github.com/scald"><img src="https://avatars.githubusercontent.com/u/1215913?v=4&s=48" width="48" height="48" alt="scald" title="scald"/></a> <a href="https://github.com/andranik-sahakyan"><img src="https://avatars.githubusercontent.com/u/8908029?v=4&s=48" width="48" height="48" alt="andranik-sahakyan" title="andranik-sahakyan"/></a> <a href="https://github.com/Nachx639"><img src="https://avatars.githubusercontent.com/u/71144023?v=4&s=48" width="48" height="48" alt="nachx639" title="nachx639"/></a> <a href="https://github.com/sircrumpet"><img src="https://avatars.githubusercontent.com/u/4436535?v=4&s=48" width="48" height="48" alt="sircrumpet" title="sircrumpet"/></a> <a href="https://github.com/rafaelreis-r"><img src="https://avatars.githubusercontent.com/u/57492577?v=4&s=48" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a> <a href="https://github.com/meaningfool"><img src="https://avatars.githubusercontent.com/u/2862331?v=4&s=48" width="48" height="48" alt="meaningfool" title="meaningfool"/></a> <a href="https://github.com/ratulsarna"><img src="https://avatars.githubusercontent.com/u/105903728?v=4&s=48" width="48" height="48" alt="ratulsarna" title="ratulsarna"/></a> <a href="https://github.com/lutr0"><img src="https://avatars.githubusercontent.com/u/76906369?v=4&s=48" width="48" height="48" alt="lutr0" title="lutr0"/></a> <a href="https://github.com/AbhisekBasu1"><img src="https://avatars.githubusercontent.com/u/40645221?v=4&s=48" width="48" height="48" alt="abhisekbasu1" title="abhisekbasu1"/></a>
|
<a href="https://github.com/claude"><img src="https://avatars.githubusercontent.com/u/81847?v=4&s=48" width="48" height="48" alt="claude" title="claude"/></a> <a href="https://github.com/scald"><img src="https://avatars.githubusercontent.com/u/1215913?v=4&s=48" width="48" height="48" alt="scald" title="scald"/></a> <a href="https://github.com/andranik-sahakyan"><img src="https://avatars.githubusercontent.com/u/8908029?v=4&s=48" width="48" height="48" alt="andranik-sahakyan" title="andranik-sahakyan"/></a> <a href="https://github.com/Nachx639"><img src="https://avatars.githubusercontent.com/u/71144023?v=4&s=48" width="48" height="48" alt="nachx639" title="nachx639"/></a> <a href="https://github.com/sircrumpet"><img src="https://avatars.githubusercontent.com/u/4436535?v=4&s=48" width="48" height="48" alt="sircrumpet" title="sircrumpet"/></a> <a href="https://github.com/rafaelreis-r"><img src="https://avatars.githubusercontent.com/u/57492577?v=4&s=48" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a> <a href="https://github.com/meaningfool"><img src="https://avatars.githubusercontent.com/u/2862331?v=4&s=48" width="48" height="48" alt="meaningfool" title="meaningfool"/></a> <a href="https://github.com/ratulsarna"><img src="https://avatars.githubusercontent.com/u/105903728?v=4&s=48" width="48" height="48" alt="ratulsarna" title="ratulsarna"/></a> <a href="https://github.com/lutr0"><img src="https://avatars.githubusercontent.com/u/76906369?v=4&s=48" width="48" height="48" alt="lutr0" title="lutr0"/></a> <a href="https://github.com/AbhisekBasu1"><img src="https://avatars.githubusercontent.com/u/40645221?v=4&s=48" width="48" height="48" alt="abhisekbasu1" title="abhisekbasu1"/></a>
|
||||||
<a href="https://github.com/emanuelst"><img src="https://avatars.githubusercontent.com/u/9994339?v=4&s=48" width="48" height="48" alt="emanuelst" title="emanuelst"/></a> <a href="https://github.com/osolmaz"><img src="https://avatars.githubusercontent.com/u/2453968?v=4&s=48" width="48" height="48" alt="osolmaz" title="osolmaz"/></a> <a href="https://github.com/kiranjd"><img src="https://avatars.githubusercontent.com/u/25822851?v=4&s=48" width="48" height="48" alt="kiranjd" title="kiranjd"/></a> <a href="https://github.com/thewilloftheshadow"><img src="https://avatars.githubusercontent.com/u/35580099?v=4&s=48" width="48" height="48" alt="thewilloftheshadow" title="thewilloftheshadow"/></a> <a href="https://github.com/CashWilliams"><img src="https://avatars.githubusercontent.com/u/613573?v=4&s=48" width="48" height="48" alt="CashWilliams" title="CashWilliams"/></a> <a href="https://github.com/ManuelHettich"><img src="https://avatars.githubusercontent.com/u/17690367?v=4&s=48" width="48" height="48" alt="manuelhettich" title="manuelhettich"/></a> <a href="https://github.com/minghinmatthewlam"><img src="https://avatars.githubusercontent.com/u/14224566?v=4&s=48" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/buddyh"><img src="https://avatars.githubusercontent.com/u/31752869?v=4&s=48" width="48" height="48" alt="buddyh" title="buddyh"/></a> <a href="https://github.com/search?q=sheeek"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="sheeek" title="sheeek"/></a> <a href="https://github.com/timkrase"><img src="https://avatars.githubusercontent.com/u/38947626?v=4&s=48" width="48" height="48" alt="timkrase" title="timkrase"/></a>
|
<a href="https://github.com/emanuelst"><img src="https://avatars.githubusercontent.com/u/9994339?v=4&s=48" width="48" height="48" alt="emanuelst" title="emanuelst"/></a> <a href="https://github.com/osolmaz"><img src="https://avatars.githubusercontent.com/u/2453968?v=4&s=48" width="48" height="48" alt="osolmaz" title="osolmaz"/></a> <a href="https://github.com/kiranjd"><img src="https://avatars.githubusercontent.com/u/25822851?v=4&s=48" width="48" height="48" alt="kiranjd" title="kiranjd"/></a> <a href="https://github.com/thewilloftheshadow"><img src="https://avatars.githubusercontent.com/u/35580099?v=4&s=48" width="48" height="48" alt="thewilloftheshadow" title="thewilloftheshadow"/></a> <a href="https://github.com/CashWilliams"><img src="https://avatars.githubusercontent.com/u/613573?v=4&s=48" width="48" height="48" alt="CashWilliams" title="CashWilliams"/></a> <a href="https://github.com/ManuelHettich"><img src="https://avatars.githubusercontent.com/u/17690367?v=4&s=48" width="48" height="48" alt="manuelhettich" title="manuelhettich"/></a> <a href="https://github.com/minghinmatthewlam"><img src="https://avatars.githubusercontent.com/u/14224566?v=4&s=48" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/buddyh"><img src="https://avatars.githubusercontent.com/u/31752869?v=4&s=48" width="48" height="48" alt="buddyh" title="buddyh"/></a> <a href="https://github.com/search?q=sheeek"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="sheeek" title="sheeek"/></a> <a href="https://github.com/timkrase"><img src="https://avatars.githubusercontent.com/u/38947626?v=4&s=48" width="48" height="48" alt="timkrase" title="timkrase"/></a>
|
||||||
<a href="https://github.com/mcinteerj"><img src="https://avatars.githubusercontent.com/u/3613653?v=4&s=48" width="48" height="48" alt="mcinteerj" title="mcinteerj"/></a> <a href="https://github.com/azade-c"><img src="https://avatars.githubusercontent.com/u/252790079?v=4&s=48" width="48" height="48" alt="azade-c" title="azade-c"/></a> <a href="https://github.com/imfing"><img src="https://avatars.githubusercontent.com/u/5097752?v=4&s=48" width="48" height="48" alt="imfing" title="imfing"/></a> <a href="https://github.com/petter-b"><img src="https://avatars.githubusercontent.com/u/62076402?v=4&s=48" width="48" height="48" alt="petter-b" title="petter-b"/></a> <a href="https://github.com/RandyVentures"><img src="https://avatars.githubusercontent.com/u/149904821?v=4&s=48" width="48" height="48" alt="RandyVentures" title="RandyVentures"/></a> <a href="https://github.com/jalehman"><img src="https://avatars.githubusercontent.com/u/550978?v=4&s=48" width="48" height="48" alt="jalehman" title="jalehman"/></a> <a href="https://github.com/obviyus"><img src="https://avatars.githubusercontent.com/u/22031114?v=4&s=48" width="48" height="48" alt="obviyus" title="obviyus"/></a> <a href="https://github.com/search?q=Yurii%20Chukhlib"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Yurii Chukhlib" title="Yurii Chukhlib"/></a> <a href="https://github.com/dan-dr"><img src="https://avatars.githubusercontent.com/u/6669808?v=4&s=48" width="48" height="48" alt="dan-dr" title="dan-dr"/></a> <a href="https://github.com/Iamadig"><img src="https://avatars.githubusercontent.com/u/102129234?v=4&s=48" width="48" height="48" alt="iamadig" title="iamadig"/></a>
|
<a href="https://github.com/mcinteerj"><img src="https://avatars.githubusercontent.com/u/3613653?v=4&s=48" width="48" height="48" alt="mcinteerj" title="mcinteerj"/></a> <a href="https://github.com/azade-c"><img src="https://avatars.githubusercontent.com/u/252790079?v=4&s=48" width="48" height="48" alt="azade-c" title="azade-c"/></a> <a href="https://github.com/imfing"><img src="https://avatars.githubusercontent.com/u/5097752?v=4&s=48" width="48" height="48" alt="imfing" title="imfing"/></a> <a href="https://github.com/petter-b"><img src="https://avatars.githubusercontent.com/u/62076402?v=4&s=48" width="48" height="48" alt="petter-b" title="petter-b"/></a> <a href="https://github.com/RandyVentures"><img src="https://avatars.githubusercontent.com/u/149904821?v=4&s=48" width="48" height="48" alt="RandyVentures" title="RandyVentures"/></a> <a href="https://github.com/jalehman"><img src="https://avatars.githubusercontent.com/u/550978?v=4&s=48" width="48" height="48" alt="jalehman" title="jalehman"/></a> <a href="https://github.com/obviyus"><img src="https://avatars.githubusercontent.com/u/22031114?v=4&s=48" width="48" height="48" alt="obviyus" title="obviyus"/></a> <a href="https://github.com/search?q=Yurii%20Chukhlib"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Yurii Chukhlib" title="Yurii Chukhlib"/></a> <a href="https://github.com/dan-dr"><img src="https://avatars.githubusercontent.com/u/6669808?v=4&s=48" width="48" height="48" alt="dan-dr" title="dan-dr"/></a> <a href="https://github.com/Iamadig"><img src="https://avatars.githubusercontent.com/u/102129234?v=4&s=48" width="48" height="48" alt="iamadig" title="iamadig"/></a>
|
||||||
<a href="https://github.com/manmal"><img src="https://avatars.githubusercontent.com/u/142797?v=4&s=48" width="48" height="48" alt="manmal" title="manmal"/></a> <a href="https://github.com/VACInc"><img src="https://avatars.githubusercontent.com/u/3279061?v=4&s=48" width="48" height="48" alt="VACInc" title="VACInc"/></a> <a href="https://github.com/zats"><img src="https://avatars.githubusercontent.com/u/2688806?v=4&s=48" width="48" height="48" alt="zats" title="zats"/></a> <a href="https://github.com/djangonavarro220"><img src="https://avatars.githubusercontent.com/u/251162586?v=4&s=48" width="48" height="48" alt="Django Navarro" title="Django Navarro"/></a> <a href="https://github.com/pcty-nextgen-service-account"><img src="https://avatars.githubusercontent.com/u/112553441?v=4&s=48" width="48" height="48" alt="pcty-nextgen-service-account" title="pcty-nextgen-service-account"/></a> <a href="https://github.com/Syhids"><img src="https://avatars.githubusercontent.com/u/671202?v=4&s=48" width="48" height="48" alt="Syhids" title="Syhids"/></a> <a href="https://github.com/fcatuhe"><img src="https://avatars.githubusercontent.com/u/17382215?v=4&s=48" width="48" height="48" alt="fcatuhe" title="fcatuhe"/></a> <a href="https://github.com/jayhickey"><img src="https://avatars.githubusercontent.com/u/1676460?v=4&s=48" width="48" height="48" alt="jayhickey" title="jayhickey"/></a> <a href="https://github.com/jverdi"><img src="https://avatars.githubusercontent.com/u/345050?v=4&s=48" width="48" height="48" alt="jverdi" title="jverdi"/></a> <a href="https://github.com/mitschabaude-bot"><img src="https://avatars.githubusercontent.com/u/247582884?v=4&s=48" width="48" height="48" alt="mitschabaude-bot" title="mitschabaude-bot"/></a>
|
<a href="https://github.com/manmal"><img src="https://avatars.githubusercontent.com/u/142797?v=4&s=48" width="48" height="48" alt="manmal" title="manmal"/></a> <a href="https://github.com/VACInc"><img src="https://avatars.githubusercontent.com/u/3279061?v=4&s=48" width="48" height="48" alt="VACInc" title="VACInc"/></a> <a href="https://github.com/zats"><img src="https://avatars.githubusercontent.com/u/2688806?v=4&s=48" width="48" height="48" alt="zats" title="zats"/></a> <a href="https://github.com/djangonavarro220"><img src="https://avatars.githubusercontent.com/u/251162586?v=4&s=48" width="48" height="48" alt="Django Navarro" title="Django Navarro"/></a> <a href="https://github.com/pcty-nextgen-service-account"><img src="https://avatars.githubusercontent.com/u/112553441?v=4&s=48" width="48" height="48" alt="pcty-nextgen-service-account" title="pcty-nextgen-service-account"/></a> <a href="https://github.com/Syhids"><img src="https://avatars.githubusercontent.com/u/671202?v=4&s=48" width="48" height="48" alt="Syhids" title="Syhids"/></a> <a href="https://github.com/erik-agens"><img src="https://avatars.githubusercontent.com/u/80908960?v=4&s=48" width="48" height="48" alt="erik-agens" title="erik-agens"/></a> <a href="https://github.com/fcatuhe"><img src="https://avatars.githubusercontent.com/u/17382215?v=4&s=48" width="48" height="48" alt="fcatuhe" title="fcatuhe"/></a> <a href="https://github.com/jayhickey"><img src="https://avatars.githubusercontent.com/u/1676460?v=4&s=48" width="48" height="48" alt="jayhickey" title="jayhickey"/></a> <a href="https://github.com/jverdi"><img src="https://avatars.githubusercontent.com/u/345050?v=4&s=48" width="48" height="48" alt="jverdi" title="jverdi"/></a>
|
||||||
<a href="https://github.com/oswalpalash"><img src="https://avatars.githubusercontent.com/u/6431196?v=4&s=48" width="48" height="48" alt="oswalpalash" title="oswalpalash"/></a> <a href="https://github.com/philipp-spiess"><img src="https://avatars.githubusercontent.com/u/458591?v=4&s=48" width="48" height="48" alt="philipp-spiess" title="philipp-spiess"/></a> <a href="https://github.com/pkrmf"><img src="https://avatars.githubusercontent.com/u/1714267?v=4&s=48" width="48" height="48" alt="pkrmf" title="pkrmf"/></a> <a href="https://github.com/search?q=Sash%20Catanzarite"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Sash Catanzarite" title="Sash Catanzarite"/></a> <a href="https://github.com/search?q=VAC"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="VAC" title="VAC"/></a> <a href="https://github.com/search?q=alejandro%20maza"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="alejandro maza" title="alejandro maza"/></a> <a href="https://github.com/antons"><img src="https://avatars.githubusercontent.com/u/129705?v=4&s=48" width="48" height="48" alt="antons" title="antons"/></a> <a href="https://github.com/Asleep123"><img src="https://avatars.githubusercontent.com/u/122379135?v=4&s=48" width="48" height="48" alt="Asleep123" title="Asleep123"/></a> <a href="https://github.com/cash-echo-bot"><img src="https://avatars.githubusercontent.com/u/252747386?v=4&s=48" width="48" height="48" alt="cash-echo-bot" title="cash-echo-bot"/></a> <a href="https://github.com/search?q=Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawd" title="Clawd"/></a>
|
<a href="https://github.com/mitschabaude-bot"><img src="https://avatars.githubusercontent.com/u/247582884?v=4&s=48" width="48" height="48" alt="mitschabaude-bot" title="mitschabaude-bot"/></a> <a href="https://github.com/oswalpalash"><img src="https://avatars.githubusercontent.com/u/6431196?v=4&s=48" width="48" height="48" alt="oswalpalash" title="oswalpalash"/></a> <a href="https://github.com/philipp-spiess"><img src="https://avatars.githubusercontent.com/u/458591?v=4&s=48" width="48" height="48" alt="philipp-spiess" title="philipp-spiess"/></a> <a href="https://github.com/pkrmf"><img src="https://avatars.githubusercontent.com/u/1714267?v=4&s=48" width="48" height="48" alt="pkrmf" title="pkrmf"/></a> <a href="https://github.com/search?q=Sash%20Catanzarite"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Sash Catanzarite" title="Sash Catanzarite"/></a> <a href="https://github.com/search?q=VAC"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="VAC" title="VAC"/></a> <a href="https://github.com/search?q=alejandro%20maza"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="alejandro maza" title="alejandro maza"/></a> <a href="https://github.com/antons"><img src="https://avatars.githubusercontent.com/u/129705?v=4&s=48" width="48" height="48" alt="antons" title="antons"/></a> <a href="https://github.com/Asleep123"><img src="https://avatars.githubusercontent.com/u/122379135?v=4&s=48" width="48" height="48" alt="Asleep123" title="Asleep123"/></a> <a href="https://github.com/cash-echo-bot"><img src="https://avatars.githubusercontent.com/u/252747386?v=4&s=48" width="48" height="48" alt="cash-echo-bot" title="cash-echo-bot"/></a>
|
||||||
<a href="https://github.com/conhecendocontato"><img src="https://avatars.githubusercontent.com/u/82890727?v=4&s=48" width="48" height="48" alt="conhecendocontato" title="conhecendocontato"/></a> <a href="https://github.com/erikpr1994"><img src="https://avatars.githubusercontent.com/u/6299331?v=4&s=48" width="48" height="48" alt="erikpr1994" title="erikpr1994"/></a> <a href="https://github.com/gtsifrikas"><img src="https://avatars.githubusercontent.com/u/8904378?v=4&s=48" width="48" height="48" alt="gtsifrikas" title="gtsifrikas"/></a> <a href="https://github.com/hrdwdmrbl"><img src="https://avatars.githubusercontent.com/u/554881?v=4&s=48" width="48" height="48" alt="hrdwdmrbl" title="hrdwdmrbl"/></a> <a href="https://github.com/hugobarauna"><img src="https://avatars.githubusercontent.com/u/2719?v=4&s=48" width="48" height="48" alt="hugobarauna" title="hugobarauna"/></a> <a href="https://github.com/search?q=Jarvis"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis" title="Jarvis"/></a> <a href="https://github.com/jonasjancarik"><img src="https://avatars.githubusercontent.com/u/2459191?v=4&s=48" width="48" height="48" alt="jonasjancarik" title="jonasjancarik"/></a> <a href="https://github.com/jdrhyne"><img src="https://avatars.githubusercontent.com/u/7828464?v=4&s=48" width="48" height="48" alt="Jonathan D. Rhyne (DJ-D)" title="Jonathan D. Rhyne (DJ-D)"/></a> <a href="https://github.com/search?q=Keith%20the%20Silly%20Goose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Keith the Silly Goose" title="Keith the Silly Goose"/></a> <a href="https://github.com/search?q=Kit"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kit" title="Kit"/></a>
|
<a href="https://github.com/search?q=Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawd" title="Clawd"/></a> <a href="https://github.com/conhecendocontato"><img src="https://avatars.githubusercontent.com/u/82890727?v=4&s=48" width="48" height="48" alt="conhecendocontato" title="conhecendocontato"/></a> <a href="https://github.com/erikpr1994"><img src="https://avatars.githubusercontent.com/u/6299331?v=4&s=48" width="48" height="48" alt="erikpr1994" title="erikpr1994"/></a> <a href="https://github.com/gtsifrikas"><img src="https://avatars.githubusercontent.com/u/8904378?v=4&s=48" width="48" height="48" alt="gtsifrikas" title="gtsifrikas"/></a> <a href="https://github.com/hrdwdmrbl"><img src="https://avatars.githubusercontent.com/u/554881?v=4&s=48" width="48" height="48" alt="hrdwdmrbl" title="hrdwdmrbl"/></a> <a href="https://github.com/hugobarauna"><img src="https://avatars.githubusercontent.com/u/2719?v=4&s=48" width="48" height="48" alt="hugobarauna" title="hugobarauna"/></a> <a href="https://github.com/search?q=Jarvis"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis" title="Jarvis"/></a> <a href="https://github.com/jonasjancarik"><img src="https://avatars.githubusercontent.com/u/2459191?v=4&s=48" width="48" height="48" alt="jonasjancarik" title="jonasjancarik"/></a> <a href="https://github.com/jdrhyne"><img src="https://avatars.githubusercontent.com/u/7828464?v=4&s=48" width="48" height="48" alt="Jonathan D. Rhyne (DJ-D)" title="Jonathan D. Rhyne (DJ-D)"/></a> <a href="https://github.com/search?q=Keith%20the%20Silly%20Goose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Keith the Silly Goose" title="Keith the Silly Goose"/></a>
|
||||||
<a href="https://github.com/kitze"><img src="https://avatars.githubusercontent.com/u/1160594?v=4&s=48" width="48" height="48" alt="kitze" title="kitze"/></a> <a href="https://github.com/kkarimi"><img src="https://avatars.githubusercontent.com/u/875218?v=4&s=48" width="48" height="48" alt="kkarimi" title="kkarimi"/></a> <a href="https://github.com/loukotal"><img src="https://avatars.githubusercontent.com/u/18210858?v=4&s=48" width="48" height="48" alt="loukotal" title="loukotal"/></a> <a href="https://github.com/mrdbstn"><img src="https://avatars.githubusercontent.com/u/58957632?v=4&s=48" width="48" height="48" alt="mrdbstn" title="mrdbstn"/></a> <a href="https://github.com/MSch"><img src="https://avatars.githubusercontent.com/u/7475?v=4&s=48" width="48" height="48" alt="MSch" title="MSch"/></a> <a href="https://github.com/nexty5870"><img src="https://avatars.githubusercontent.com/u/3869659?v=4&s=48" width="48" height="48" alt="nexty5870" title="nexty5870"/></a> <a href="https://github.com/ngutman"><img src="https://avatars.githubusercontent.com/u/1540134?v=4&s=48" width="48" height="48" alt="ngutman" title="ngutman"/></a> <a href="https://github.com/onutc"><img src="https://avatars.githubusercontent.com/u/152018508?v=4&s=48" width="48" height="48" alt="onutc" title="onutc"/></a> <a href="https://github.com/prathamdby"><img src="https://avatars.githubusercontent.com/u/134331217?v=4&s=48" width="48" height="48" alt="prathamdby" title="prathamdby"/></a> <a href="https://github.com/reeltimeapps"><img src="https://avatars.githubusercontent.com/u/637338?v=4&s=48" width="48" height="48" alt="reeltimeapps" title="reeltimeapps"/></a>
|
<a href="https://github.com/search?q=Kit"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kit" title="Kit"/></a> <a href="https://github.com/kitze"><img src="https://avatars.githubusercontent.com/u/1160594?v=4&s=48" width="48" height="48" alt="kitze" title="kitze"/></a> <a href="https://github.com/kkarimi"><img src="https://avatars.githubusercontent.com/u/875218?v=4&s=48" width="48" height="48" alt="kkarimi" title="kkarimi"/></a> <a href="https://github.com/loukotal"><img src="https://avatars.githubusercontent.com/u/18210858?v=4&s=48" width="48" height="48" alt="loukotal" title="loukotal"/></a> <a href="https://github.com/mrdbstn"><img src="https://avatars.githubusercontent.com/u/58957632?v=4&s=48" width="48" height="48" alt="mrdbstn" title="mrdbstn"/></a> <a href="https://github.com/MSch"><img src="https://avatars.githubusercontent.com/u/7475?v=4&s=48" width="48" height="48" alt="MSch" title="MSch"/></a> <a href="https://github.com/nexty5870"><img src="https://avatars.githubusercontent.com/u/3869659?v=4&s=48" width="48" height="48" alt="nexty5870" title="nexty5870"/></a> <a href="https://github.com/ngutman"><img src="https://avatars.githubusercontent.com/u/1540134?v=4&s=48" width="48" height="48" alt="ngutman" title="ngutman"/></a> <a href="https://github.com/onutc"><img src="https://avatars.githubusercontent.com/u/152018508?v=4&s=48" width="48" height="48" alt="onutc" title="onutc"/></a> <a href="https://github.com/prathamdby"><img src="https://avatars.githubusercontent.com/u/134331217?v=4&s=48" width="48" height="48" alt="prathamdby" title="prathamdby"/></a>
|
||||||
<a href="https://github.com/RLTCmpe"><img src="https://avatars.githubusercontent.com/u/10762242?v=4&s=48" width="48" height="48" alt="RLTCmpe" title="RLTCmpe"/></a> <a href="https://github.com/search?q=Rolf%20Fredheim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rolf Fredheim" title="Rolf Fredheim"/></a> <a href="https://github.com/snopoke"><img src="https://avatars.githubusercontent.com/u/249606?v=4&s=48" width="48" height="48" alt="snopoke" title="snopoke"/></a> <a href="https://github.com/wstock"><img src="https://avatars.githubusercontent.com/u/1394687?v=4&s=48" width="48" height="48" alt="wstock" title="wstock"/></a> <a href="https://github.com/YuriNachos"><img src="https://avatars.githubusercontent.com/u/19365375?v=4&s=48" width="48" height="48" alt="YuriNachos" title="YuriNachos"/></a> <a href="https://github.com/search?q=Azade"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Azade" title="Azade"/></a> <a href="https://github.com/search?q=ddyo"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ddyo" title="ddyo"/></a> <a href="https://github.com/search?q=Erik"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Erik" title="Erik"/></a> <a href="https://github.com/search?q=Manuel%20Maly"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Manuel Maly" title="Manuel Maly"/></a> <a href="https://github.com/search?q=Mourad%20Boustani"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mourad Boustani" title="Mourad Boustani"/></a>
|
<a href="https://github.com/reeltimeapps"><img src="https://avatars.githubusercontent.com/u/637338?v=4&s=48" width="48" height="48" alt="reeltimeapps" title="reeltimeapps"/></a> <a href="https://github.com/RLTCmpe"><img src="https://avatars.githubusercontent.com/u/10762242?v=4&s=48" width="48" height="48" alt="RLTCmpe" title="RLTCmpe"/></a> <a href="https://github.com/search?q=Rolf%20Fredheim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rolf Fredheim" title="Rolf Fredheim"/></a> <a href="https://github.com/snopoke"><img src="https://avatars.githubusercontent.com/u/249606?v=4&s=48" width="48" height="48" alt="snopoke" title="snopoke"/></a> <a href="https://github.com/wstock"><img src="https://avatars.githubusercontent.com/u/1394687?v=4&s=48" width="48" height="48" alt="wstock" title="wstock"/></a> <a href="https://github.com/YuriNachos"><img src="https://avatars.githubusercontent.com/u/19365375?v=4&s=48" width="48" height="48" alt="YuriNachos" title="YuriNachos"/></a> <a href="https://github.com/search?q=Azade"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Azade" title="Azade"/></a> <a href="https://github.com/search?q=ddyo"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ddyo" title="ddyo"/></a> <a href="https://github.com/search?q=Erik"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Erik" title="Erik"/></a> <a href="https://github.com/search?q=Manuel%20Maly"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Manuel Maly" title="Manuel Maly"/></a>
|
||||||
<a href="https://github.com/pcty-nextgen-ios-builder"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pcty-nextgen-ios-builder" title="pcty-nextgen-ios-builder"/></a> <a href="https://github.com/search?q=Quentin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Quentin" title="Quentin"/></a> <a href="https://github.com/search?q=Randy%20Torres"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Randy Torres" title="Randy Torres"/></a> <a href="https://github.com/search?q=Tobias%20Bischoff"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Tobias Bischoff" title="Tobias Bischoff"/></a> <a href="https://github.com/search?q=William%20Stock"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="William Stock" title="William Stock"/></a>
|
<a href="https://github.com/search?q=Mourad%20Boustani"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mourad Boustani" title="Mourad Boustani"/></a> <a href="https://github.com/pcty-nextgen-ios-builder"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pcty-nextgen-ios-builder" title="pcty-nextgen-ios-builder"/></a> <a href="https://github.com/search?q=Quentin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Quentin" title="Quentin"/></a> <a href="https://github.com/search?q=Randy%20Torres"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Randy Torres" title="Randy Torres"/></a> <a href="https://github.com/search?q=Tobias%20Bischoff"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Tobias Bischoff" title="Tobias Bischoff"/></a> <a href="https://github.com/search?q=William%20Stock"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="William Stock" title="William Stock"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ DMs:
|
|||||||
- `clawdbot pairing list --provider signal`
|
- `clawdbot pairing list --provider signal`
|
||||||
- `clawdbot pairing approve --provider signal <CODE>`
|
- `clawdbot pairing approve --provider signal <CODE>`
|
||||||
- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing)
|
- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing)
|
||||||
|
- UUID-only senders (from `sourceUuid`) are stored as `uuid:<id>` in `signal.allowFrom`.
|
||||||
|
|
||||||
Groups:
|
Groups:
|
||||||
- `signal.groupPolicy = open | allowlist | disabled`.
|
- `signal.groupPolicy = open | allowlist | disabled`.
|
||||||
@@ -85,7 +86,7 @@ Provider options:
|
|||||||
- `signal.ignoreStories`: ignore stories from the daemon.
|
- `signal.ignoreStories`: ignore stories from the daemon.
|
||||||
- `signal.sendReadReceipts`: forward read receipts.
|
- `signal.sendReadReceipts`: forward read receipts.
|
||||||
- `signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
- `signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
|
||||||
- `signal.allowFrom`: DM allowlist (E.164). `open` requires `"*"`.
|
- `signal.allowFrom`: DM allowlist (E.164 or `uuid:<id>`). `open` requires `"*"`.
|
||||||
- `signal.groupPolicy`: `open | allowlist | disabled` (default: open).
|
- `signal.groupPolicy`: `open | allowlist | disabled` (default: open).
|
||||||
- `signal.groupAllowFrom`: group sender allowlist.
|
- `signal.groupAllowFrom`: group sender allowlist.
|
||||||
- `signal.textChunkLimit`: outbound chunk size (chars).
|
- `signal.textChunkLimit`: outbound chunk size (chars).
|
||||||
|
|||||||
114
src/signal/identity.ts
Normal file
114
src/signal/identity.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { normalizeE164 } from "../utils.js";
|
||||||
|
|
||||||
|
export type SignalSender =
|
||||||
|
| { kind: "phone"; raw: string; e164: string }
|
||||||
|
| { kind: "uuid"; raw: string };
|
||||||
|
|
||||||
|
type SignalAllowEntry =
|
||||||
|
| { kind: "any" }
|
||||||
|
| { kind: "phone"; e164: string }
|
||||||
|
| { kind: "uuid"; raw: string };
|
||||||
|
|
||||||
|
const UUID_HYPHENATED_RE =
|
||||||
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
|
const UUID_COMPACT_RE = /^[0-9a-f]{32}$/i;
|
||||||
|
|
||||||
|
function looksLikeUuid(value: string): boolean {
|
||||||
|
if (UUID_HYPHENATED_RE.test(value) || UUID_COMPACT_RE.test(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const compact = value.replace(/-/g, "");
|
||||||
|
if (!/^[0-9a-f]+$/i.test(compact)) return false;
|
||||||
|
return /[a-f]/i.test(compact);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripSignalPrefix(value: string): string {
|
||||||
|
return value.replace(/^signal:/i, "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveSignalSender(params: {
|
||||||
|
sourceNumber?: string | null;
|
||||||
|
sourceUuid?: string | null;
|
||||||
|
}): SignalSender | null {
|
||||||
|
const sourceNumber = params.sourceNumber?.trim();
|
||||||
|
if (sourceNumber) {
|
||||||
|
return {
|
||||||
|
kind: "phone",
|
||||||
|
raw: sourceNumber,
|
||||||
|
e164: normalizeE164(sourceNumber),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const sourceUuid = params.sourceUuid?.trim();
|
||||||
|
if (sourceUuid) {
|
||||||
|
return { kind: "uuid", raw: sourceUuid };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatSignalSenderId(sender: SignalSender): string {
|
||||||
|
return sender.kind === "phone" ? sender.e164 : `uuid:${sender.raw}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatSignalSenderDisplay(sender: SignalSender): string {
|
||||||
|
return sender.kind === "phone" ? sender.e164 : `uuid:${sender.raw}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveSignalRecipient(sender: SignalSender): string {
|
||||||
|
return sender.kind === "phone" ? sender.e164 : sender.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveSignalPeerId(sender: SignalSender): string {
|
||||||
|
return sender.kind === "phone" ? sender.e164 : `uuid:${sender.raw}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSignalAllowEntry(entry: string): SignalAllowEntry | null {
|
||||||
|
const trimmed = entry.trim();
|
||||||
|
if (!trimmed) return null;
|
||||||
|
if (trimmed === "*") return { kind: "any" };
|
||||||
|
|
||||||
|
const stripped = stripSignalPrefix(trimmed);
|
||||||
|
const lower = stripped.toLowerCase();
|
||||||
|
if (lower.startsWith("uuid:")) {
|
||||||
|
const raw = stripped.slice("uuid:".length).trim();
|
||||||
|
if (!raw) return null;
|
||||||
|
return { kind: "uuid", raw };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (looksLikeUuid(stripped)) {
|
||||||
|
return { kind: "uuid", raw: stripped };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { kind: "phone", e164: normalizeE164(stripped) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSignalSenderAllowed(
|
||||||
|
sender: SignalSender,
|
||||||
|
allowFrom: string[],
|
||||||
|
): boolean {
|
||||||
|
if (allowFrom.length === 0) return false;
|
||||||
|
const parsed = allowFrom
|
||||||
|
.map(parseSignalAllowEntry)
|
||||||
|
.filter((entry): entry is SignalAllowEntry => entry !== null);
|
||||||
|
if (parsed.some((entry) => entry.kind === "any")) return true;
|
||||||
|
return parsed.some((entry) => {
|
||||||
|
if (entry.kind === "phone" && sender.kind === "phone") {
|
||||||
|
return entry.e164 === sender.e164;
|
||||||
|
}
|
||||||
|
if (entry.kind === "uuid" && sender.kind === "uuid") {
|
||||||
|
return entry.raw === sender.raw;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSignalGroupAllowed(params: {
|
||||||
|
groupPolicy: "open" | "disabled" | "allowlist";
|
||||||
|
allowFrom: string[];
|
||||||
|
sender: SignalSender;
|
||||||
|
}): boolean {
|
||||||
|
const { groupPolicy, allowFrom, sender } = params;
|
||||||
|
if (groupPolicy === "disabled") return false;
|
||||||
|
if (groupPolicy === "open") return true;
|
||||||
|
if (allowFrom.length === 0) return false;
|
||||||
|
return isSignalSenderAllowed(sender, allowFrom);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
import { isSignalGroupAllowed } from "./monitor.js";
|
import { isSignalGroupAllowed } from "./identity.js";
|
||||||
|
|
||||||
describe("signal groupPolicy gating", () => {
|
describe("signal groupPolicy gating", () => {
|
||||||
it("allows when policy is open", () => {
|
it("allows when policy is open", () => {
|
||||||
@@ -8,7 +8,7 @@ describe("signal groupPolicy gating", () => {
|
|||||||
isSignalGroupAllowed({
|
isSignalGroupAllowed({
|
||||||
groupPolicy: "open",
|
groupPolicy: "open",
|
||||||
allowFrom: [],
|
allowFrom: [],
|
||||||
sender: "+15550001111",
|
sender: { kind: "phone", raw: "+15550001111", e164: "+15550001111" },
|
||||||
}),
|
}),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -18,7 +18,7 @@ describe("signal groupPolicy gating", () => {
|
|||||||
isSignalGroupAllowed({
|
isSignalGroupAllowed({
|
||||||
groupPolicy: "disabled",
|
groupPolicy: "disabled",
|
||||||
allowFrom: ["+15550001111"],
|
allowFrom: ["+15550001111"],
|
||||||
sender: "+15550001111",
|
sender: { kind: "phone", raw: "+15550001111", e164: "+15550001111" },
|
||||||
}),
|
}),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -28,7 +28,7 @@ describe("signal groupPolicy gating", () => {
|
|||||||
isSignalGroupAllowed({
|
isSignalGroupAllowed({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
allowFrom: [],
|
allowFrom: [],
|
||||||
sender: "+15550001111",
|
sender: { kind: "phone", raw: "+15550001111", e164: "+15550001111" },
|
||||||
}),
|
}),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -38,7 +38,7 @@ describe("signal groupPolicy gating", () => {
|
|||||||
isSignalGroupAllowed({
|
isSignalGroupAllowed({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
allowFrom: ["+15550001111"],
|
allowFrom: ["+15550001111"],
|
||||||
sender: "+15550001111",
|
sender: { kind: "phone", raw: "+15550001111", e164: "+15550001111" },
|
||||||
}),
|
}),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -48,7 +48,20 @@ describe("signal groupPolicy gating", () => {
|
|||||||
isSignalGroupAllowed({
|
isSignalGroupAllowed({
|
||||||
groupPolicy: "allowlist",
|
groupPolicy: "allowlist",
|
||||||
allowFrom: ["*"],
|
allowFrom: ["*"],
|
||||||
sender: "+15550002222",
|
sender: { kind: "phone", raw: "+15550002222", e164: "+15550002222" },
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows allowlist when uuid sender matches", () => {
|
||||||
|
expect(
|
||||||
|
isSignalGroupAllowed({
|
||||||
|
groupPolicy: "allowlist",
|
||||||
|
allowFrom: ["uuid:123e4567-e89b-12d3-a456-426614174000"],
|
||||||
|
sender: {
|
||||||
|
kind: "uuid",
|
||||||
|
raw: "123e4567-e89b-12d3-a456-426614174000",
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -197,6 +197,48 @@ describe("monitorSignalProvider tool results", () => {
|
|||||||
expect(sendMock).toHaveBeenCalledTimes(1);
|
expect(sendMock).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("pairs uuid-only senders with a uuid allowlist entry", async () => {
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
signal: { autoStart: false, dmPolicy: "pairing", allowFrom: [] },
|
||||||
|
};
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const uuid = "123e4567-e89b-12d3-a456-426614174000";
|
||||||
|
|
||||||
|
streamMock.mockImplementation(async ({ onEvent }) => {
|
||||||
|
const payload = {
|
||||||
|
envelope: {
|
||||||
|
sourceUuid: uuid,
|
||||||
|
sourceName: "Ada",
|
||||||
|
timestamp: 1,
|
||||||
|
dataMessage: {
|
||||||
|
message: "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await onEvent({
|
||||||
|
event: "receive",
|
||||||
|
data: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
abortController.abort();
|
||||||
|
});
|
||||||
|
|
||||||
|
await monitorSignalProvider({
|
||||||
|
autoStart: false,
|
||||||
|
baseUrl: "http://127.0.0.1:8080",
|
||||||
|
abortSignal: abortController.signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
await flush();
|
||||||
|
|
||||||
|
expect(replyMock).not.toHaveBeenCalled();
|
||||||
|
expect(upsertPairingRequestMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ provider: "signal", id: `uuid:${uuid}` }),
|
||||||
|
);
|
||||||
|
expect(sendMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(sendMock.mock.calls[0]?.[0]).toBe(`signal:${uuid}`);
|
||||||
|
});
|
||||||
|
|
||||||
it("reconnects after stream errors until aborted", async () => {
|
it("reconnects after stream errors until aborted", async () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ import { normalizeE164 } from "../utils.js";
|
|||||||
import { resolveSignalAccount } from "./accounts.js";
|
import { resolveSignalAccount } from "./accounts.js";
|
||||||
import { signalCheck, signalRpcRequest } from "./client.js";
|
import { signalCheck, signalRpcRequest } from "./client.js";
|
||||||
import { spawnSignalDaemon } from "./daemon.js";
|
import { spawnSignalDaemon } from "./daemon.js";
|
||||||
|
import {
|
||||||
|
formatSignalSenderDisplay,
|
||||||
|
formatSignalSenderId,
|
||||||
|
isSignalSenderAllowed,
|
||||||
|
resolveSignalPeerId,
|
||||||
|
resolveSignalRecipient,
|
||||||
|
resolveSignalSender,
|
||||||
|
} from "./identity.js";
|
||||||
import { sendMessageSignal } from "./send.js";
|
import { sendMessageSignal } from "./send.js";
|
||||||
import { runSignalSseLoop } from "./sse-reconnect.js";
|
import { runSignalSseLoop } from "./sse-reconnect.js";
|
||||||
|
|
||||||
@@ -92,28 +100,6 @@ function normalizeAllowList(raw?: Array<string | number>): string[] {
|
|||||||
return (raw ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
return (raw ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAllowedSender(sender: string, allowFrom: string[]): boolean {
|
|
||||||
if (allowFrom.length === 0) return false;
|
|
||||||
if (allowFrom.includes("*")) return true;
|
|
||||||
const normalizedAllow = allowFrom
|
|
||||||
.map((entry) => entry.replace(/^signal:/i, ""))
|
|
||||||
.map((entry) => normalizeE164(entry));
|
|
||||||
const normalizedSender = normalizeE164(sender);
|
|
||||||
return normalizedAllow.includes(normalizedSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSignalGroupAllowed(params: {
|
|
||||||
groupPolicy: "open" | "disabled" | "allowlist";
|
|
||||||
allowFrom: string[];
|
|
||||||
sender: string;
|
|
||||||
}): boolean {
|
|
||||||
const { groupPolicy, allowFrom, sender } = params;
|
|
||||||
if (groupPolicy === "disabled") return false;
|
|
||||||
if (groupPolicy === "open") return true;
|
|
||||||
if (allowFrom.length === 0) return false;
|
|
||||||
return isAllowedSender(sender, allowFrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function waitForSignalDaemonReady(params: {
|
async function waitForSignalDaemonReady(params: {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
abortSignal?: AbortSignal;
|
abortSignal?: AbortSignal;
|
||||||
@@ -320,11 +306,18 @@ export async function monitorSignalProvider(
|
|||||||
envelope.dataMessage ?? envelope.editMessage?.dataMessage;
|
envelope.dataMessage ?? envelope.editMessage?.dataMessage;
|
||||||
if (!dataMessage) return;
|
if (!dataMessage) return;
|
||||||
|
|
||||||
const sender = envelope.sourceNumber?.trim() || envelope.sourceUuid?.trim();
|
const sender = resolveSignalSender(envelope);
|
||||||
if (!sender) return;
|
if (!sender) return;
|
||||||
if (account && envelope.sourceNumber && normalizeE164(envelope.sourceNumber) === normalizeE164(account)) {
|
if (account && sender.kind === "phone") {
|
||||||
|
if (sender.e164 === normalizeE164(account)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const senderDisplay = formatSignalSenderDisplay(sender);
|
||||||
|
const senderRecipient = resolveSignalRecipient(sender);
|
||||||
|
const senderPeerId = resolveSignalPeerId(sender);
|
||||||
|
const senderAllowId = formatSignalSenderId(sender);
|
||||||
|
if (!senderRecipient) return;
|
||||||
const groupId = dataMessage.groupInfo?.groupId ?? undefined;
|
const groupId = dataMessage.groupInfo?.groupId ?? undefined;
|
||||||
const groupName = dataMessage.groupInfo?.groupName ?? undefined;
|
const groupName = dataMessage.groupInfo?.groupName ?? undefined;
|
||||||
const isGroup = Boolean(groupId);
|
const isGroup = Boolean(groupId);
|
||||||
@@ -334,13 +327,15 @@ export async function monitorSignalProvider(
|
|||||||
const effectiveDmAllow = [...allowFrom, ...storeAllowFrom];
|
const effectiveDmAllow = [...allowFrom, ...storeAllowFrom];
|
||||||
const effectiveGroupAllow = [...groupAllowFrom, ...storeAllowFrom];
|
const effectiveGroupAllow = [...groupAllowFrom, ...storeAllowFrom];
|
||||||
const dmAllowed =
|
const dmAllowed =
|
||||||
dmPolicy === "open" ? true : isAllowedSender(sender, effectiveDmAllow);
|
dmPolicy === "open"
|
||||||
|
? true
|
||||||
|
: isSignalSenderAllowed(sender, effectiveDmAllow);
|
||||||
|
|
||||||
if (!isGroup) {
|
if (!isGroup) {
|
||||||
if (dmPolicy === "disabled") return;
|
if (dmPolicy === "disabled") return;
|
||||||
if (!dmAllowed) {
|
if (!dmAllowed) {
|
||||||
if (dmPolicy === "pairing") {
|
if (dmPolicy === "pairing") {
|
||||||
const senderId = normalizeE164(sender);
|
const senderId = senderAllowId;
|
||||||
const { code, created } = await upsertProviderPairingRequest({
|
const { code, created } = await upsertProviderPairingRequest({
|
||||||
provider: "signal",
|
provider: "signal",
|
||||||
id: senderId,
|
id: senderId,
|
||||||
@@ -352,7 +347,7 @@ export async function monitorSignalProvider(
|
|||||||
logVerbose(`signal pairing request sender=${senderId}`);
|
logVerbose(`signal pairing request sender=${senderId}`);
|
||||||
try {
|
try {
|
||||||
await sendMessageSignal(
|
await sendMessageSignal(
|
||||||
senderId,
|
`signal:${senderRecipient}`,
|
||||||
[
|
[
|
||||||
"Clawdbot: access not configured.",
|
"Clawdbot: access not configured.",
|
||||||
"",
|
"",
|
||||||
@@ -376,7 +371,7 @@ export async function monitorSignalProvider(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logVerbose(
|
logVerbose(
|
||||||
`Blocked signal sender ${sender} (dmPolicy=${dmPolicy})`,
|
`Blocked signal sender ${senderDisplay} (dmPolicy=${dmPolicy})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -393,9 +388,9 @@ export async function monitorSignalProvider(
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isAllowedSender(sender, effectiveGroupAllow)) {
|
if (!isSignalSenderAllowed(sender, effectiveGroupAllow)) {
|
||||||
logVerbose(
|
logVerbose(
|
||||||
`Blocked signal group sender ${sender} (not in groupAllowFrom)`,
|
`Blocked signal group sender ${senderDisplay} (not in groupAllowFrom)`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -403,7 +398,7 @@ export async function monitorSignalProvider(
|
|||||||
|
|
||||||
const commandAuthorized = isGroup
|
const commandAuthorized = isGroup
|
||||||
? effectiveGroupAllow.length > 0
|
? effectiveGroupAllow.length > 0
|
||||||
? isAllowedSender(sender, effectiveGroupAllow)
|
? isSignalSenderAllowed(sender, effectiveGroupAllow)
|
||||||
: true
|
: true
|
||||||
: dmAllowed;
|
: dmAllowed;
|
||||||
const messageText = (dataMessage.message ?? "").trim();
|
const messageText = (dataMessage.message ?? "").trim();
|
||||||
@@ -418,7 +413,7 @@ export async function monitorSignalProvider(
|
|||||||
baseUrl,
|
baseUrl,
|
||||||
account,
|
account,
|
||||||
attachment: firstAttachment,
|
attachment: firstAttachment,
|
||||||
sender,
|
sender: senderRecipient,
|
||||||
groupId,
|
groupId,
|
||||||
maxBytes: mediaMaxBytes,
|
maxBytes: mediaMaxBytes,
|
||||||
});
|
});
|
||||||
@@ -445,7 +440,7 @@ export async function monitorSignalProvider(
|
|||||||
|
|
||||||
const fromLabel = isGroup
|
const fromLabel = isGroup
|
||||||
? `${groupName ?? "Signal Group"} id:${groupId}`
|
? `${groupName ?? "Signal Group"} id:${groupId}`
|
||||||
: `${envelope.sourceName ?? sender} id:${sender}`;
|
: `${envelope.sourceName ?? senderDisplay} id:${senderDisplay}`;
|
||||||
const body = formatAgentEnvelope({
|
const body = formatAgentEnvelope({
|
||||||
provider: "Signal",
|
provider: "Signal",
|
||||||
from: fromLabel,
|
from: fromLabel,
|
||||||
@@ -459,20 +454,24 @@ export async function monitorSignalProvider(
|
|||||||
accountId: accountInfo.accountId,
|
accountId: accountInfo.accountId,
|
||||||
peer: {
|
peer: {
|
||||||
kind: isGroup ? "group" : "dm",
|
kind: isGroup ? "group" : "dm",
|
||||||
id: isGroup ? (groupId ?? "unknown") : normalizeE164(sender),
|
id: isGroup ? (groupId ?? "unknown") : senderPeerId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const signalTo = isGroup ? `group:${groupId}` : `signal:${sender}`;
|
const signalTo = isGroup
|
||||||
|
? `group:${groupId}`
|
||||||
|
: `signal:${senderRecipient}`;
|
||||||
const ctxPayload = {
|
const ctxPayload = {
|
||||||
Body: body,
|
Body: body,
|
||||||
From: isGroup ? `group:${groupId ?? "unknown"}` : `signal:${sender}`,
|
From: isGroup
|
||||||
|
? `group:${groupId ?? "unknown"}`
|
||||||
|
: `signal:${senderRecipient}`,
|
||||||
To: signalTo,
|
To: signalTo,
|
||||||
SessionKey: route.sessionKey,
|
SessionKey: route.sessionKey,
|
||||||
AccountId: route.accountId,
|
AccountId: route.accountId,
|
||||||
ChatType: isGroup ? "group" : "direct",
|
ChatType: isGroup ? "group" : "direct",
|
||||||
GroupSubject: isGroup ? (groupName ?? undefined) : undefined,
|
GroupSubject: isGroup ? (groupName ?? undefined) : undefined,
|
||||||
SenderName: envelope.sourceName ?? sender,
|
SenderName: envelope.sourceName ?? senderDisplay,
|
||||||
SenderId: sender,
|
SenderId: senderDisplay,
|
||||||
Provider: "signal" as const,
|
Provider: "signal" as const,
|
||||||
Surface: "signal" as const,
|
Surface: "signal" as const,
|
||||||
MessageSid: envelope.timestamp ? String(envelope.timestamp) : undefined,
|
MessageSid: envelope.timestamp ? String(envelope.timestamp) : undefined,
|
||||||
@@ -495,7 +494,7 @@ export async function monitorSignalProvider(
|
|||||||
storePath,
|
storePath,
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.mainSessionKey,
|
||||||
provider: "signal",
|
provider: "signal",
|
||||||
to: normalizeE164(sender),
|
to: senderRecipient,
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user