@wrongstack/telegram 0.268.0 → 0.270.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bot.ts","../src/config.ts","../src/format.ts","../src/poll-lock.ts","../src/slash-commands/index.ts","../src/tools/telegram-read.ts","../src/tools/telegram-send.ts","../src/index.ts"],"names":["readFileSync","writeFileSync","expectDefined"],"mappings":";;;;;;;AAQA,SAAS,WAAA,CAAY,KAAa,KAAA,EAAuB;AACvD,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,YAAY,CAAA;AACxC;AA0FO,IAAM,WAAA,GAAN,MAAM,YAAA,CAAY;AAAA,EACN,OAAA;AAAA;AAAA,EAEA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,GAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA,GAAa,IAAI,eAAA,EAAgB;AAAA,EAC1C,SAAA,GAAkD,IAAA;AAAA,EAClD,UAAA,GAAa,KAAA;AAAA,EACb,MAAA,GAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,cAAA,GAAiB,CAAA;AAAA,EACzB,OAAwB,sBAAA,GAAyB,CAAA;AAAA,EACjD,OAAwB,gBAAA,GAAmB,GAAA;AAAA,EACnC,UAAA,GAA4B,IAAA;AAAA;AAAA,EAEnB,iBAAA;AAAA;AAAA,EAEA,IAAA;AAAA,EACA,cAAA;AAAA,EACT,YAAA,GAAqD,IAAA;AAAA,EACrD,gBAAA,GAAmB,KAAA;AAAA;AAAA,EAGV,SAAA;AAAA,EACA,SAAoC,EAAC;AAAA,EAEtD,YAAY,IAAA,EAA0B;AACpC,IAAA,IAAA,CAAK,OAAA,GAAU,CAAA,4BAAA,EAA+B,IAAA,CAAK,KAAK,CAAA,CAAA;AACxD,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA,CAAY,IAAA,CAAK,OAAA,EAAS,KAAK,KAAK,CAAA;AACvD,IAAA,IAAA,CAAK,cAAA,GAAiB,KAAK,eAAA,GAAkB,GAAA;AAC7C,IAAA,IAAA,CAAK,eAAe,IAAA,CAAK,YAAA;AACzB,IAAA,IAAA,CAAK,eAAe,IAAA,CAAK,YAAA;AACzB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,UAAA;AACtB,IAAA,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA;AAChB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,oBAAoB,IAAA,CAAK,iBAAA;AAC9B,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,cAAA,GAAiB,KAAK,cAAA,IAAkB,IAAA;AAC7C,IAAA,IAAI,KAAK,IAAA,EAAM;AACb,MAAA,IAAA,CAAK,IAAA,CAAK,MAAA,GAAS,MAAM,IAAA,CAAK,cAAA,EAAe;AAAA,IAC/C;AAGA,IAAA,IAAI,KAAK,iBAAA,EAAmB;AAC1B,MAAA,KAAK,KAAK,UAAA,EAAW;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAA,GAAc;AACZ,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAK,GAAA,EAAI;AAC3B,IAAA,IAAA,CAAK,cAAA,EAAe;AAAA,EACtB;AAAA;AAAA,EAGA,IAAA,GAAa;AACX,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAClB,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AACtB,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,YAAA,CAAa,KAAK,SAAS,CAAA;AAC3B,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB;AACA,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,YAAA,CAAa,KAAK,YAAY,CAAA;AAC9B,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AACA,IAAA,IAAA,CAAK,MAAM,OAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,GAAA,CAAI,KAAK,sBAAsB,CAAA;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,OAAA,GAAmB;AACrB,IAAA,OAAO,KAAK,UAAA,IAAc,IAAA,CAAK,SAAS,MAAA,IAAa,CAAC,KAAK,IAAA,CAAK,IAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAA,GAAuB;AAC7B,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACtB,IAAA,IAAI,KAAK,IAAA,IAAQ,CAAC,IAAA,CAAK,IAAA,CAAK,YAAW,EAAG;AACxC,MAAA,IAAI,CAAC,KAAK,gBAAA,EAAkB;AAC1B,QAAA,IAAA,CAAK,gBAAA,GAAmB,IAAA;AACxB,QAAA,IAAA,CAAK,GAAA,CAAI,IAAA;AAAA,UACP;AAAA,SACF;AAAA,MACF;AACA,MAAA,IAAA,CAAK,eAAe,UAAA,CAAW,MAAM,KAAK,cAAA,EAAe,EAAG,KAAK,cAAc,CAAA;AAC/E,MAAA,IAAA,CAAK,aAAa,KAAA,IAAQ;AAC1B,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAK,gBAAA,EAAkB;AACzB,MAAA,IAAA,CAAK,gBAAA,GAAmB,KAAA;AACxB,MAAA,IAAA,CAAK,GAAA,CAAI,KAAK,0DAAqD,CAAA;AAAA,IACrE,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAA,8BAAA,EAAiC,IAAA,CAAK,WAAW,CAAA,CAAA,CAAG,CAAA;AAAA,IACpE;AACA,IAAA,IAAA,CAAK,YAAA,EAAa;AAAA,EACpB;AAAA;AAAA,EAGQ,cAAA,GAAuB;AAC7B,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACtB,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,YAAA,CAAa,KAAK,SAAS,CAAA;AAC3B,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,IACnB;AACA,IAAA,IAAA,CAAK,GAAA,CAAI,KAAK,sFAAiF,CAAA;AAC/F,IAAA,IAAA,CAAK,gBAAA,GAAmB,IAAA;AACxB,IAAA,IAAA,CAAK,eAAe,UAAA,CAAW,MAAM,KAAK,cAAA,EAAe,EAAG,KAAK,cAAc,CAAA;AAC/E,IAAA,IAAA,CAAK,aAAa,KAAA,IAAQ;AAAA,EAC5B;AAAA,EAEA,IAAI,SAAA,GAA2B;AAC7B,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA,EAEA,IAAI,OAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,IAAA,EAAwG;AAClH,IAAA,IAAI,OAAO,CAAC,GAAG,IAAA,CAAK,MAAM,EAAE,OAAA,EAAQ;AACpC,IAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAC9B,MAAA,IAAA,GAAO,IAAA,CAAK,OAAO,CAAC,CAAA,KAAM,OAAO,CAAA,CAAE,MAAM,MAAM,GAAG,CAAA;AAAA,IACpD;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,IAAS,EAAA;AAC7B,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA;AAAA,EAC5B;AAAA;AAAA,EAGA,YAAY,aAAA,EAA+B;AACzC,IAAA,MAAM,MAAA,GAAS,KAAK,MAAA,CAAO,MAAA;AAC3B,IAAA,IAAI,CAAA,GAAI,KAAK,MAAA,CAAO,MAAA;AACpB,IAAA,OAAO,MAAM,CAAA,EAAG;AACd,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA;AAC9B,MAAA,IAAI,QAAA,IAAY,QAAA,CAAS,SAAA,IAAa,aAAA,EAAe;AACnD,QAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA;AAC3B,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAO,MAAA,GAAS,KAAK,MAAA,CAAO,MAAA;AAAA,EAC9B;AAAA,EAEA,IAAI,WAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,MAAA,CAAO,MAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAA,CAAY,MAAA,EAAyB,IAAA,EAA8C;AACvF,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,YAAA,CAAA;AAC3B,IAAA,MAAM,IAAA,GAAO,KAAK,SAAA,CAAU;AAAA,MAC1B,OAAA,EAAS,OAAO,MAAM,CAAA;AAAA,MACtB,IAAA;AAAA,MACA,wBAAA,EAA0B;AAAA,KAC3B,CAAA;AAED,IAAA,IAAA,CAAK,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,MAAM,CAAA,EAAA,EAAK,IAAA,CAAK,MAAM,CAAA,OAAA,CAAS,CAAA;AAE7E,IAAA,IAAI,OAAA;AACJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,CAAA,EAAG,OAAA,EAAA,EAAW;AAC7C,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,UAC3B,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,IAAA;AAAA,UACA,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAM;AAAA,SACnC,CAAA;AACD,QAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,QAAA,IAAI,CAAC,KAAK,EAAA,EAAI;AACZ,UAAA,MAAM,IAAI,MAAM,CAAA,mBAAA,EAAsB,IAAA,CAAK,UAAU,CAAA,EAAA,EAAK,IAAA,CAAK,WAAW,CAAA,CAAE,CAAA;AAAA,QAC9E;AACA,QAAA,OAAO,IAAA;AAAA,MACT,SAAS,GAAA,EAAK;AACZ,QAAA,OAAA,GAAU,GAAA;AACV,QAAA,IAAI,UAAU,CAAA,EAAG;AACf,UAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,6BAAA,EAAgC,OAAO,CAAA,0BAAA,CAA4B,CAAA;AAClF,UAAA,MAAM,MAAM,GAAI,CAAA;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,IAAA,MAAM,OAAA;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAA,GAA8F;AAClG,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,MAAA,CAAA;AAC3B,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,QAAQ,WAAA,CAAY,OAAA,CAAQ,GAAI,CAAA,EAAG,CAAA;AAClE,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,IAAM,CAAC,KAAK,MAAA,EAAQ;AAC5B,QAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAO,IAAA,CAAK,eAAe,eAAA,EAAgB;AAAA,MACjE;AACA,MAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,QAAA,EAAU,IAAA,CAAK,OAAO,QAAA,EAAS;AAAA,IACpD,SAAS,GAAA,EAAK;AACZ,MAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,KAAA,EAAQ,IAAc,OAAA,EAAQ;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAA,GAAqB;AAC3B,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AAEtB,IAAA,IAAI,IAAA,CAAK,IAAA,IAAQ,CAAC,IAAA,CAAK,KAAK,IAAA,EAAM;AAClC,IAAA,MAAM,QACJ,IAAA,CAAK,cAAA,IAAkB,aAAY,sBAAA,GAC/B,YAAA,CAAY,mBACZ,IAAA,CAAK,cAAA;AACX,IAAA,IAAA,CAAK,SAAA,GAAY,WAAW,MAAM;AAChC,MAAA,KAAK,KAAK,IAAA,EAAK,CAAE,QAAQ,MAAM,IAAA,CAAK,cAAc,CAAA;AAAA,IACpD,GAAG,KAAK,CAAA;AAAA,EACV;AAAA,EAEA,MAAc,IAAA,GAAsB;AAClC,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,mBAAA,EAAsB,KAAK,MAAM,CAAA,WAAA,CAAA;AAC5D,MAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,IAAA,CAAK,UAAA,CAAW,MAAA,EAAQ,CAAA;AAC/D,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAE7B,MAAA,IAAI,CAAC,KAAK,EAAA,EAAI;AACZ,QAAA,IAAI,IAAA,CAAK,eAAe,GAAA,EAAK;AAC3B,UAAA,IAAA,CAAK,cAAA,EAAA;AACL,UAAA,IAAI,IAAA,CAAK,cAAA,KAAmB,YAAA,CAAY,sBAAA,EAAwB;AAC9D,YAAA,IAAA,CAAK,GAAA,CAAI,IAAA;AAAA,cACP,IAAA,CAAK,OACD,4MAAA,GACA;AAAA,aACN;AAAA,UACF;AAAA,QACF;AACA,QAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,IAAA,CAAK,WAAW,CAAA,CAAE,CAAA;AAChE,QAAA;AAAA,MACF;AACA,MAAA,IAAA,CAAK,cAAA,GAAiB,CAAA;AAEtB,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,IAAU,EAAC;AAChC,MAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AACzB,QAAA,IAAA,CAAK,MAAA,GAAS,IAAI,SAAA,GAAY,CAAA;AAC9B,QAAA,MAAM,GAAA,GAAM,GAAA,CAAI,OAAA,IAAW,GAAA,CAAI,cAAA;AAC/B,QAAA,IAAI,CAAC,KAAK,IAAA,EAAM;AAChB,QAAA,MAAM,MAAM,EAAE,GAAG,GAAA,EAAK,IAAA,EAAM,IAAI,IAAA,EAAK;AACrC,QAAA,IAAA,CAAK,eAAe,GAAG,CAAA;AAAA,MACzB;AAIA,MAAA,IAAI,IAAA,CAAK,iBAAA,IAAqB,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC7C,QAAA,KAAK,KAAK,UAAA,EAAW;AAAA,MACvB;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,IAAK,GAAA,CAAc,SAAS,YAAA,EAAc;AAC1C,MAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,qBAAA,EAAyB,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,eAAe,GAAA,EAAyC;AAC9D,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AACjC,IAAA,MAAM,SAAS,GAAA,CAAI,IAAA,GAAO,OAAO,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,GAAI,MAAA;AAGhD,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,IAAA,GAAO,CAAA,IAAK,MAAA,IAAU,CAAC,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA,EAAG;AAC1E,MAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,MAAM,CAAA,sBAAA,CAAwB,CAAA;AAC3E,MAAA,KAAK,IAAA,CAAK,WAAA,CAAY,MAAA,EAAQ,0DAAqD,CAAA;AACnF,MAAA;AAAA,IACF;AACA,IAAA,IAAI,IAAA,CAAK,aAAa,IAAA,GAAO,CAAA,IAAK,CAAC,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA,EAAG;AAChE,MAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,MAAM,CAAA,sBAAA,CAAwB,CAAA;AAC3E,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAA,GAAoC;AAAA,MACxC,WAAW,GAAA,CAAI,UAAA;AAAA,MACf,MAAA,EAAQ,IAAI,IAAA,CAAK,EAAA;AAAA,MACjB,QAAA,EAAU,IAAI,IAAA,CAAK,IAAA;AAAA,MACnB,MAAA,EAAQ,IAAI,IAAA,EAAM,EAAA;AAAA,MAClB,QAAA,EAAU,GAAA,CAAI,IAAA,EAAM,QAAA,IAAY,IAAI,IAAA,EAAM,UAAA;AAAA,MAC1C,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,SAAA,EAAW,IAAI,IAAA,GAAO;AAAA,KACxB;AAGA,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,QAAQ,CAAA;AACzB,IAAA,OAAO,KAAK,MAAA,CAAO,MAAA,GAAS,KAAK,SAAA,EAAW,IAAA,CAAK,OAAO,KAAA,EAAM;AAE9D,IAAA,IAAA,CAAK,UAAU,QAAQ,CAAA;AAAA,EACzB;AAAA,EAEA,MAAc,UAAA,GAA4B;AACxC,IAAA,IAAI,CAAC,KAAK,iBAAA,EAAmB;AAC7B,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,YAAA,EAAAA,aAAAA,EAAa,GAAI,MAAM,OAAO,IAAS,CAAA;AAC/C,MAAA,MAAM,MAAMA,aAAAA,CAAa,IAAA,CAAK,iBAAA,EAAmB,MAAM,EAAE,IAAA,EAAK;AAC9D,MAAA,MAAM,CAAA,GAAI,MAAA,CAAO,QAAA,CAAS,GAAA,EAAK,EAAE,CAAA;AACjC,MAAA,IAAI,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,IAAK,KAAK,CAAA,EAAG;AAChC,QAAA,IAAA,CAAK,MAAA,GAAS,CAAA;AACd,QAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,IAAA,CAAK,MAAM,CAAA,CAAE,CAAA;AAAA,MACnE;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAc,UAAA,GAA4B;AACxC,IAAA,IAAI,CAAC,KAAK,iBAAA,EAAmB;AAC7B,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,aAAA,EAAAC,cAAAA,EAAc,GAAI,MAAM,OAAO,IAAS,CAAA;AAEhD,MAAAA,eAAc,IAAA,CAAK,iBAAA,EAAmB,OAAO,IAAA,CAAK,MAAM,GAAG,MAAM,CAAA;AAAA,IACnE,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,GAAG,CAAA,CAAE,CAAA;AAAA,IAC5D;AAAA,EACF;AACF,CAAA;AAgBO,SAAS,mBAAA,CAAoB,IAAA,EAAc,MAAA,GAAS,GAAA,EAAc;AACvE,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,MAAA,EAAQ,OAAO,IAAA;AAGlC,EAAA,MAAM,SAAS,MAAA,GAAS,EAAA;AACxB,EAAA,IAAI,MAAA,IAAU,GAAG,OAAO,CAAA,EAAG,KAAK,KAAA,CAAM,CAAA,EAAG,MAAA,GAAS,CAAC,CAAC,CAAA,MAAA,CAAA;AAEpD,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,QAAQ,MAAM,CAAA;AAG9C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,CAAY,MAAA,EAAQ,SAAS,CAAA;AAClD,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,OAAO,CAAC;;AAAA,MAAA,CAAA;AAAA,EAClC;AAGA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,WAAA,CAAY,IAAA,EAAM,SAAS,CAAA;AAC9C,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,KAAK,CAAC;AAAA,MAAA,CAAA;AAAA,EAChC;AAGA,EAAA,MAAM,UAAA,GAAa,cAAA;AACnB,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,WAAA,GAAc,EAAA;AAClB,EAAA,KAAA,GAAQ,UAAA,CAAW,KAAK,IAAI,CAAA;AAC5B,EAAA,OAAO,UAAU,IAAA,EAAM;AACrB,IAAA,IAAI,KAAA,CAAM,SAAS,SAAA,EAAW;AAC9B,IAAA,IAAI,KAAA,CAAM,KAAA,GAAQ,MAAA,EAAQ,WAAA,GAAc,MAAM,KAAA,GAAQ,CAAA;AACtD,IAAA,KAAA,GAAQ,UAAA,CAAW,KAAK,IAAI,CAAA;AAAA,EAC9B;AACA,EAAA,IAAI,cAAc,MAAA,EAAQ;AACxB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,WAAW,CAAC,CAAA,MAAA,CAAA;AAAA,EACtC;AAGA,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,WAAA,CAAY,GAAA,EAAK,SAAS,CAAA;AAChD,EAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAC,CAAA,OAAA,CAAA;AAAA,EACnC;AAGA,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,MAAA,GAAS,EAAE,CAAC,CAAA,QAAA,EAAM,IAAA,CAAK,MAAA,GAAS,MAAA,GAAS,EAAE,CAAA,OAAA,CAAA;AACrE;;;ACrfO,IAAM,WAAA,GAAc,UAAA;AA+CpB,IAAM,cAAA,GAA0G;AAAA,EACrH,cAAc,EAAC;AAAA,EACf,cAAc,EAAC;AAAA,EACf,eAAA,EAAiB,CAAA;AAAA,EACjB,kBAAA,EAAoB,KAAA;AAAA,EACpB,mBAAA,EAAqB,GAAA;AAAA,EACrB,gBAAA,EAAkB,IAAA;AAAA,EAClB,gBAAA,EAAkB,GAAA;AAAA,EAClB,kBAAA,EAAoB;AACtB,CAAA;AAEO,IAAM,oBAAA,GAAuB;AAAA,EAClC,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,QAAA,EAAU,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,wCAAA,EAAyC;AAAA,IAClF,YAAA,EAAc;AAAA,MACZ,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,UAAS,EAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,MAC/C,WAAA,EAAa;AAAA,KACf;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,IAAA,EAAM,OAAA;AAAA,MACN,KAAA,EAAO,EAAE,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,QAAA,EAAS,EAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA,EAAE;AAAA,MAC1D,WAAA,EAAa;AAAA,KACf;AAAA,IACA,YAAA,EAAc;AAAA,MACZ,IAAA,EAAM,OAAA;AAAA,MACN,KAAA,EAAO,EAAE,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,QAAA,EAAS,EAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA,EAAE;AAAA,MAC1D,WAAA,EAAa;AAAA,KACf;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM,SAAA;AAAA,MACN,OAAA,EAAS,CAAA;AAAA,MACT,OAAA,EAAS,EAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA,IACA,kBAAA,EAAoB,EAAE,IAAA,EAAM,SAAA,EAAU;AAAA,IACtC,mBAAA,EAAqB,EAAE,IAAA,EAAM,SAAA,EAAW,SAAS,CAAA,EAAE;AAAA,IACnD,gBAAA,EAAkB,EAAE,IAAA,EAAM,SAAA,EAAU;AAAA,IACpC,kBAAkB,EAAE,IAAA,EAAM,WAAW,OAAA,EAAS,GAAA,EAAK,SAAS,IAAA,EAAK;AAAA,IACjE,kBAAA,EAAoB;AAAA,MAClB,IAAA,EAAM,SAAA;AAAA,MACN,WAAA,EAAa;AAAA;AACf,GACF;AAAA,EACA,QAAA,EAAU,CAAC,UAAU;AACvB,CAAA;AAEO,SAAS,mBACd,GAAA,EAEiE;AACjE,EAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,EAAA,MAAM,aAAa,MAAA,CAAO,UAAA;AAC1B,EAAA,MAAM,gBAAgB,MAAA,CAAO,OAAA;AAC7B,EAAA,MAAM,aAAA,GAAgB,aAAA;AACtB,EAAA,MAAM,UAAA,GACJ,iBAAiB,CAAC,KAAA,CAAM,QAAQ,aAAa,CAAA,GAAI,aAAA,CAAc,WAAW,CAAA,GAAI,MAAA;AAChF,EAAA,MAAM,SAAA,GAAY,yBAAyB,aAAa,CAAA;AACxD,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,GAAK,UAAA,IAAc,SAAA;AAAA,IACnB,GAAK,UAAA,GAAa,WAAW,CAAA,IAAK;AAAC,GACrC;AACA,EAAA,OAAO;AAAA,IACL,GAAG,cAAA;AAAA,IACH,GAAG;AAAA,GACL;AACF;AAEA,SAAS,yBAAyB,OAAA,EAAoD;AACpF,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,OAAO,GAAG,OAAO,MAAA;AACpC,EAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA;AAAA,IACpB,CAAC,KAAA,KACC,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,MAAA,IAAU,KAAA,KACR,KAAA,CAAyC,IAAA,KAAS,sBAAA,IACjD,MAAyC,IAAA,KAAS,WAAA;AAAA,GACzD;AACA,EAAA,OAAO,OAAO,OAAA,IAAW,OAAO,MAAM,OAAA,KAAY,QAAA,GAC7C,MAAM,OAAA,GACP,MAAA;AACN;;;AC3EO,SAAS,YAAY,EAAA,EAAoB;AAC9C,EAAA,IAAI,EAAA,GAAK,KAAQ,OAAO,CAAA,EAAG,KAAK,KAAA,CAAM,EAAA,GAAK,GAAI,CAAC,CAAA,CAAA,CAAA;AAChD,EAAA,IAAI,EAAA,GAAK,MAAW,OAAO,CAAA,EAAG,KAAK,KAAA,CAAM,EAAA,GAAK,GAAM,CAAC,CAAA,CAAA,CAAA;AACrD,EAAA,OAAO,CAAA,EAAA,CAAI,EAAA,GAAK,IAAA,EAAW,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AACvC;AAMO,SAAS,UAAU,CAAA,EAAmB;AAC3C,EAAA,OAAO,CAAA,CAAE,eAAe,OAAO,CAAA;AACjC;AAMO,SAAS,cAAc,GAAA,EAAiC;AAC7D,EAAA,IAAI,CAAC,KAAK,OAAO,aAAA;AACjB,EAAA,MAAM,OAAA,GAAU,IACb,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA,CACtB,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAA,CACvB,OAAA,CAAQ,eAAe,MAAM,CAAA,CAC7B,OAAA,CAAQ,MAAA,EAAQ,IAAI,CAAA,CACpB,QAAQ,MAAA,EAAQ,GAAG,CAAA,CACnB,IAAA,EAAK,IACH,GAAA;AAGL,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAK,CAAE,MAAA,GAAS,CAAC,CAAA;AACnE,EAAA,IAAI,UAAU,KAAA,CAAM,KAAA,CAAM,GAAG,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AACzC,EAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,OAAA,IAAW;AAAA,QAAA,EAAQ,KAAA,CAAM,SAAS,CAAC,CAAA,WAAA,CAAA;AACzD,EAAA,IAAI,OAAA,CAAQ,SAAS,GAAA,EAAK,OAAA,GAAU,GAAG,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA,MAAA,CAAA;AAC5D,EAAA,OAAO,OAAA;AACT;AAcO,SAAS,wBAAwB,CAAA,EAAkC;AACxE,EAAA,MAAM,IAAA,GAAO,CAAA,CAAE,EAAA,GAAK,QAAA,GAAM,QAAA;AAC1B,EAAA,MAAM,MAAA,GAAS,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,KAAK,SAAA,GAAY,QAAA,CAAA;AAC/C,EAAA,MAAM,IAAA,GAAO,CAAA,CAAE,IAAA,CAAK,MAAA,GAAS,GAAA,GAAM,CAAA,EAAG,CAAA,CAAE,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,WAAM,CAAA,CAAE,IAAA;AAIlE,EAAA,MAAM,OAAO,CAAA,CAAE,OAAA,EAAS,IAAA,EAAK,IAAK,uBAAkB,IAAI,CAAA,CAAA;AAExD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,CAAA,OAAA,EAAK,WAAA,CAAY,CAAA,CAAE,UAAU,CAAC,CAAA,CAAA;AAAA,IAC9B,CAAA,EAAG,EAAE,UAAU,CAAA,KAAA,CAAA;AAAA,IACf,CAAA,EAAG,EAAE,SAAS,CAAA,MAAA;AAAA,GAChB;AACA,EAAA,IAAI,OAAO,CAAA,CAAE,OAAA,KAAY,QAAA,IAAY,CAAA,CAAE,UAAU,CAAA,EAAG;AAClD,IAAA,KAAA,CAAM,KAAK,CAAA,SAAA,EAAK,CAAA,CAAE,QAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,EACxC;AAEA,EAAA,OAAO,CAAC,CAAA,EAAG,IAAI,CAAA,iBAAA,EAAe,CAAA,CAAE,MAAM,CAAA,MAAA,EAAM,MAAM,CAAA,CAAA,EAAI,IAAA,EAAM,MAAM,IAAA,CAAK,QAAK,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AAC1F;AAUO,SAAS,mBAAmB,CAAA,EAA6B;AAC9D,EAAA,MAAM,IAAA,GAAO,CAAA,CAAE,EAAA,GAAK,QAAA,GAAM,QAAA;AAC1B,EAAA,MAAM,GAAA,GAAA,CAAO,CAAA,CAAE,UAAA,GAAa,GAAA,EAAM,QAAQ,CAAC,CAAA;AAC3C,EAAA,MAAM,WAAW,CAAA,EAAG,IAAI,IAAI,CAAA,CAAE,IAAI,iBAAiB,GAAG,CAAA,CAAA,CAAA;AAEtD,EAAA,MAAM,MAAA,GAAS,aAAA,CAAc,CAAA,CAAE,MAAM,CAAA;AAErC,EAAA,IAAI,MAAA,KAAW,eAAe,OAAO,QAAA;AACrC,EAAA,OAAO,GAAG,QAAQ;AAAA,EAAK,MAAM,CAAA,CAAA;AAC/B;AAUO,SAAS,mBAAmB,CAAA,EAA6B;AAC9D,EAAA,MAAM,EAAA,GAAK,CAAA,CAAE,EAAA,CAAG,MAAA,GAAS,CAAA,GAAI,CAAA,CAAE,EAAA,CAAG,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,GAAI,CAAA,CAAE,EAAA;AAClD,EAAA,MAAM,KAAA,GAAQ,CAAA,CAAE,WAAA,GAAc,CAAA,CAAE,YAAA;AAEhC,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,qBAAc,EAAE,CAAA,MAAA,CAAA;AAAA,IAChB,CAAA,OAAA,EAAK,SAAA,CAAU,CAAA,CAAE,WAAW,CAAC,CAAA,gBAAA,EAAW,SAAA,CAAU,CAAA,CAAE,YAAY,CAAC,CAAA,UAAA,EAAU,SAAA,CAAU,KAAK,CAAC,CAAA,MAAA;AAAA,GAC7F;AAGA,EAAA,IAAI,CAAA,CAAE,SAAA,IAAa,CAAA,CAAE,UAAA,EAAY;AAC/B,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,IAAI,CAAA,CAAE,SAAA,IAAa,CAAA,CAAE,SAAA,GAAY,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,SAAA,CAAU,CAAA,CAAE,SAAS,CAAC,CAAA,WAAA,CAAa,CAAA;AACrF,IAAA,IAAI,CAAA,CAAE,UAAA,IAAc,CAAA,CAAE,UAAA,GAAa,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,SAAA,CAAU,CAAA,CAAE,UAAU,CAAC,CAAA,cAAA,CAAgB,CAAA;AAC3F,IAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,aAAM,KAAA,CAAM,IAAA,CAAK,QAAK,CAAC,CAAA,CAAE,CAAA;AAAA,EAC5D;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;ACxIO,SAAS,gBAAA,CAAiB,KAAA,EAAe,UAAA,GAAa,gBAAA,EAAiB,EAAW;AACvF,EAAA,MAAM,IAAA,GAAO,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACzE,EAAA,OAAO,IAAA,CAAK,UAAA,EAAY,UAAA,EAAY,CAAA,KAAA,EAAQ,IAAI,CAAA,KAAA,CAAO,CAAA;AACzD;AAEO,IAAM,WAAN,MAAe;AAAA,EAWpB,WAAA,CACW,UACT,IAAA,EACA;AAFS,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAGT,IAAA,IAAA,CAAK,WAAA,GAAc,MAAM,WAAA,IAAe,IAAA;AACxC,IAAA,IAAA,CAAK,OAAA,GAAU,MAAM,OAAA,IAAW,IAAA;AAChC,IAAA,IAAA,CAAK,MAAM,IAAA,EAAM,GAAA;AAAA,EACnB;AAAA,EANW,QAAA;AAAA,EAXM,KAAK,CAAA,EAAG,OAAA,CAAQ,GAAG,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA;AAAA,EACnC,WAAA;AAAA,EACA,OAAA;AAAA,EACA,GAAA;AAAA,EACT,cAAA,GAAwD,IAAA;AAAA,EACxD,KAAA,GAAQ,KAAA;AAAA;AAAA,EAGhB,MAAA;AAAA,EAWA,IAAI,IAAA,GAAgB;AAClB,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAA,GAAsB;AACpB,IAAA,IAAI,IAAA,CAAK,OAAO,OAAO,IAAA;AAEvB,IAAA,MAAM,QAAA,GAAW,KAAK,QAAA,EAAS;AAC/B,IAAA,IAAI,YAAY,CAAC,IAAA,CAAK,OAAA,CAAQ,QAAQ,GAAG,OAAO,KAAA;AAEhD,IAAA,IAAI;AACF,MAAA,SAAA,CAAU,QAAQ,IAAA,CAAK,QAAQ,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAGrD,MAAA,IAAI;AACF,QAAA,UAAA,CAAW,KAAK,QAAQ,CAAA;AAAA,MAC1B,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,MAAA,MAAM,OAAA,GAA2B;AAAA,QAC/B,IAAI,IAAA,CAAK,EAAA;AAAA,QACT,KAAK,OAAA,CAAQ,GAAA;AAAA,QACb,UAAA,EAAY,GAAA;AAAA,QACZ,WAAA,EAAa;AAAA,OACf;AACA,MAAA,aAAA,CAAc,IAAA,CAAK,UAAU,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,IACtE,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,IAAA,IAAA,CAAK,cAAA,EAAe;AACpB,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA,EAGA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,aAAA,EAAc;AACnB,IAAA,IAAI,CAAC,KAAK,KAAA,EAAO;AACjB,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,IAAI;AACF,MAAA,IAAI,IAAA,CAAK,UAAS,EAAG,EAAA,KAAO,KAAK,EAAA,EAAI,UAAA,CAAW,KAAK,QAAQ,CAAA;AAAA,IAC/D,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAA,GAAuB;AAC7B,IAAA,IAAA,CAAK,aAAA,EAAc;AACnB,IAAA,IAAA,CAAK,iBAAiB,WAAA,CAAY,MAAM,KAAK,aAAA,EAAc,EAAG,KAAK,WAAW,CAAA;AAC9E,IAAA,IAAA,CAAK,eAAe,KAAA,IAAQ;AAAA,EAC9B;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,aAAA,CAAc,KAAK,cAAc,CAAA;AACjC,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,aAAA,GAAsB;AAC5B,IAAA,MAAM,OAAA,GAAU,KAAK,QAAA,EAAS;AAC9B,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,EAAA,KAAO,KAAK,EAAA,EAAI;AAGtC,MAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,MAAA,IAAA,CAAK,aAAA,EAAc;AACnB,MAAA,IAAA,CAAK,GAAA,EAAK,KAAK,yDAAyD,CAAA;AACxE,MAAA,IAAA,CAAK,MAAA,IAAS;AACd,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,MAAM,UAA2B,EAAE,GAAG,SAAS,WAAA,EAAa,IAAA,CAAK,KAAI,EAAE;AAEvE,MAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAI,QAAQ,GAAG,CAAA,IAAA,CAAA;AAC3C,MAAA,aAAA,CAAc,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAC1C,MAAA,UAAA,CAAW,GAAA,EAAK,KAAK,QAAQ,CAAA;AAAA,IAC/B,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAA,EAAK,KAAA,CAAM,CAAA,4CAAA,EAA+C,GAAG,CAAA,CAAE,CAAA;AAAA,IACtE;AAAA,EACF;AAAA,EAEQ,QAAA,GAAmC;AACzC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,YAAA,CAAa,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AAC9C,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,MAAA,IAAI,OAAO,OAAO,EAAA,KAAO,QAAA,IAAY,OAAO,MAAA,CAAO,GAAA,KAAQ,UAAU,OAAO,IAAA;AAC5E,MAAA,OAAO,MAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAA,EAAmC;AACjD,IAAA,IAAI,KAAK,GAAA,EAAI,GAAI,QAAQ,WAAA,GAAc,IAAA,CAAK,SAAS,OAAO,IAAA;AAC5D,IAAA,OAAO,CAAC,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,GAAG,CAAA;AAAA,EACrC;AAAA,EAEQ,WAAW,GAAA,EAAsB;AACvC,IAAA,IAAI,GAAA,KAAQ,OAAA,CAAQ,GAAA,EAAK,OAAO,IAAA;AAChC,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAC,CAAA;AACnB,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AAEZ,MAAA,OAAQ,IAA8B,IAAA,KAAS,OAAA;AAAA,IACjD;AAAA,EACF;AACF,CAAA;ACvKO,SAAS,eAAA,CAAgB,KAAkB,GAAA,EAAyC;AACzF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS,CAAC,QAAA,EAAU,KAAK,CAAA;AAAA,IACzB,WAAA,EAAa,gDAAA;AAAA,IACb,IAAA,EAAM,CAAA;;AAAA;AAAA,4CAAA,CAAA;AAAA,IAIN,MAAM,GAAA,CAAI,KAAA,EAAO,IAAA,EAAM;AACrB,MAAA,MAAM,MAAA,GAAS,MAAM,GAAA,CAAI,MAAA,EAAO;AAChC,MAAA,MAAM,KAAA,GAAQ;AAAA,QACZ,8DAAA;AAAA,QACA,EAAA;AAAA,QACA,CAAA,WAAA,EAAc,MAAA,CAAO,EAAA,GAAK,CAAA,QAAA,EAAM,MAAA,CAAO,QAAA,IAAY,WAAW,CAAA,CAAA,GAAK,CAAA,OAAA,EAAK,MAAA,CAAO,KAAA,IAAS,SAAS,CAAA,CAAE,CAAA,CAAA;AAAA,QACnG,CAAA,WAAA,EAAc,GAAA,CAAI,OAAA,GAAU,KAAA,GAAQ,IAAI,CAAA,CAAA;AAAA,QACxC,CAAA,WAAA,EAAc,GAAA,CAAI,SAAA,GAAY,IAAI,IAAA,CAAK,IAAI,SAAS,CAAA,CAAE,kBAAA,EAAmB,GAAI,KAAK,CAAA,CAAA;AAAA,QAClF,CAAA,iBAAA,EAAoB,GAAA,CAAI,eAAA,IAAmB,CAAC,CAAA,CAAA,CAAA;AAAA,QAC5C,CAAA,WAAA,EAAA,CAAe,IAAI,YAAA,EAAc,MAAA,IAAU,KAAK,CAAA,GAAI,CAAA,EAAG,GAAA,CAAI,YAAA,EAAc,MAAM,CAAA,MAAA,CAAA,GAAW,kBAAkB,CAAA,GAAA,EAAA,CAAO,GAAA,CAAI,YAAA,EAAc,MAAA,IAAU,CAAA,IAAK,CAAA,GAAI,GAAG,GAAA,CAAI,YAAA,EAAc,MAAM,CAAA,MAAA,CAAA,GAAW,kBAAkB,CAAA,CAAA;AAAA,QAChN,CAAA,sBAAA,EAAyB,GAAA,CAAI,kBAAA,IAAsB,KAAK,CAAA,WAAA,EAAc,GAAA,CAAI,mBAAA,GAAsB,CAAA,EAAG,GAAA,CAAI,mBAAmB,CAAA,EAAA,CAAA,GAAO,KAAK,CAAA;AAAA,OACxI;AAEA,MAAA,OAAO,EAAE,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,EAAE;AAAA,IACrC;AAAA,GACF;AACF;AAMO,SAAS,aAAA,CACd,KACA,aAAA,EACc;AACd,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,MAAA;AAAA,IACN,WAAA,EAAa,mCAAA;AAAA,IACb,IAAA,EAAM,CAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,qDAAA,CAAA;AAAA,IASN,MAAM,GAAA,CAAI,IAAA,EAAM,IAAA,EAAM;AACpB,MAAA,IAAI,CAAC,IAAA,CAAK,IAAA,EAAK,EAAG;AAChB,QAAA,OAAO,EAAE,SAAS,2CAAA,EAA4C;AAAA,MAChE;AAEA,MAAA,IAAI,MAAA;AACJ,MAAA,IAAI,IAAA;AAGJ,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,EAAK,CAAE,MAAM,KAAK,CAAA;AACrC,MAAA,MAAM,OAAA,GAAU,MAAM,CAAC,CAAA;AACvB,MAAA,IAAI,OAAA,CAAQ,KAAK,aAAA,CAAc,OAAO,CAAC,CAAA,IAAK,KAAA,CAAM,SAAS,CAAA,EAAG;AAC5D,QAAA,MAAA,GAAS,cAAc,OAAO,CAAA;AAC9B,QAAA,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,MAChC,WAAW,aAAA,EAAe;AACxB,QAAA,MAAA,GAAS,aAAA;AACT,QAAA,IAAA,GAAO,KAAK,IAAA,EAAK;AAAA,MACnB,CAAA,MAAO;AACL,QAAA,OAAO;AAAA,UACL,OAAA,EACE;AAAA,SACJ;AAAA,MACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,GAAA,GAAM,MAAM,GAAA,CAAI,WAAA,CAAY,QAAQ,IAAI,CAAA;AAC9C,QAAA,OAAO;AAAA,UACL,SAAS,CAAA,uBAAA,EAAqB,MAAM,YAAY,GAAA,CAAI,MAAA,EAAQ,cAAc,GAAG,CAAA,CAAA;AAAA,SAC/E;AAAA,MACF,SAAS,GAAA,EAAK;AACZ,QAAA,OAAO,EAAE,OAAA,EAAS,CAAA,uBAAA,EAAsB,GAAA,CAAc,OAAO,CAAA,CAAA,EAAG;AAAA,MAClE;AAAA,IACF;AAAA,GACF;AACF;AAMO,SAAS,gBAAgB,aAAA,EAA+C;AAC7E,EAAA,MAAM,SAAA,GAAY,aAAA,GAAgB,MAAA,CAAO,aAAa,CAAA,GAAI,IAAA;AAC1D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,qCAAA;AAAA,IACb,IAAA,EAAM,CAAA;;AAAA;AAAA,4DAAA,CAAA;AAAA,IAIN,MAAM,GAAA,CAAI,KAAA,EAAO,IAAA,EAAM;AACrB,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,OAAO,EAAE,OAAA,EAAS,CAAA,yBAAA,EAA4B,SAAS,CAAA,CAAA,EAAG;AAAA,MAC5D;AACA,MAAA,OAAO,EAAE,SAAS,sGAAA,EAAuG;AAAA,IAC3H;AAAA,GACF;AACF;AAMO,SAAS,qBAAA,CACd,GAAA,EACA,GAAA,EACA,GAAA,EACU;AACV,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,eAAA,CAAgB,KAAK,GAAG,CAAA;AAAA,IACxB,aAAA,CAAc,GAAA,EAAK,GAAA,CAAI,YAAY,CAAA;AAAA,IACnC,eAAA,CAAgB,IAAI,YAAY;AAAA,GAClC;AACA,EAAA,KAAA,MAAW,GAAA,IAAO,IAAA,EAAM,GAAA,CAAI,aAAA,CAAc,SAAS,GAAG,CAAA;AACtD,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAC/B;;;ACnHO,SAAS,qBAAqB,IAAA,EAET;AAC1B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,eAAA;AAAA,IACN,WAAA,EACE,6VAAA;AAAA,IACF,SAAA,EAAW,kIAAA;AAAA,IACX,QAAA,EAAU,UAAA;AAAA,IACV,WAAA,EAAa;AAAA,MACX,IAAA,EAAM,QAAA;AAAA,MACN,UAAA,EAAY;AAAA,QACV,OAAA,EAAS;AAAA,UACP,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,UAAS,EAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,UAC/C,WAAA,EAAa;AAAA,SACf;AAAA,QACA,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,SAAA;AAAA,UACN,OAAA,EAAS,CAAA;AAAA,UACT,OAAA,EAAS,EAAA;AAAA,UACT,WAAA,EAAa;AAAA,SACf;AAAA,QACA,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,SAAA;AAAA,UACN,WAAA,EACE;AAAA;AACJ;AACF,KACF;AAAA,IACA,UAAA,EAAY,MAAA;AAAA,IACZ,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,GAAA;AAAA,IACX,MAAM,QAAQ,KAAA,EAAO;AACnB,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,WAAA,CAAY;AAAA,QAChC,QAAQ,KAAA,CAAM,OAAA;AAAA,QACd,KAAA,EAAO,MAAM,KAAA,IAAS;AAAA,OACvB,CAAA;AAED,MAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,MAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,IAAa,KAAA,CAAM,WAAW,CAAA,EAAG;AACtD,QAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,WAAA,CAAY,KAAA,CAAM,QAAQ,CAAA;AAAA,MAC7C;AAEA,MAAA,OAAO;AAAA,QACL,YAAA,EAAc,KAAK,GAAA,CAAI,WAAA;AAAA,QACvB,QAAA,EAAU,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,UACzB,YAAY,CAAA,CAAE,SAAA;AAAA,UACd,SAAS,CAAA,CAAE,MAAA;AAAA,UACX,WAAW,CAAA,CAAE,QAAA;AAAA,UACb,MAAM,CAAA,CAAE,QAAA,IAAY,CAAA,KAAA,EAAQ,CAAA,CAAE,UAAU,SAAS,CAAA,CAAA;AAAA,UACjD,MAAM,CAAA,CAAE,IAAA;AAAA,UACR,IAAI,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,EAAE,WAAA;AAAY,SACxC,CAAE,CAAA;AAAA,QACF,KAAA;AAAA,QACA,IAAA,EAAM,KAAA,GAAQ,CAAA,GACV,MAAA,GACA;AAAA,OACN;AAAA,IACF;AAAA,GACF;AACF;;;AC/DO,SAAS,qBAAqB,IAAA,EAMT;AAC1B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,eAAA;AAAA,IACN,WAAA,EACE,wQAAA;AAAA,IACF,SAAA,EAAW,6HAAA;AAAA,IACX,QAAA,EAAU,UAAA;AAAA,IACV,WAAA,EAAa;AAAA,MACX,IAAA,EAAM,QAAA;AAAA,MACN,UAAA,EAAY;AAAA,QACV,OAAA,EAAS;AAAA,UACP,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,UAAS,EAAG,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,UAC/C,WAAA,EAAa;AAAA,SACf;AAAA,QACA,OAAA,EAAS;AAAA,UACP,IAAA,EAAM,QAAA;AAAA,UACN,WAAA,EACE;AAAA;AACJ,OACF;AAAA,MACA,QAAA,EAAU,CAAC,SAAS;AAAA,KACtB;AAAA,IACA,UAAA,EAAY,SAAA;AAAA,IACZ,QAAA,EAAU,IAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,MAAM,OAAA,CAAQ,KAAA,EAAO,IAAA,EAAM,KAAA,EAAO;AAChC,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,IAAW,IAAA,CAAK,gBAAA,EAAiB;AACtD,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAGA,MAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,KAAA,CAAM,OAAA,EAAS,KAAK,gBAAgB,CAAA;AAE1E,MAAA,IAAA,CAAK,IAAI,IAAA,CAAK,CAAA,6BAAA,EAA2B,MAAM,CAAA,EAAA,EAAK,SAAA,CAAU,MAAM,CAAA,OAAA,CAAS,CAAA;AAE7E,MAAA,MAAM,MAAM,MAAM,IAAA,CAAK,GAAA,CAAI,WAAA,CAAY,QAAQ,SAAS,CAAA;AAExD,MAAA,OAAO;AAAA,QACL,IAAI,GAAA,CAAI,EAAA;AAAA,QACR,UAAA,EAAY,IAAI,MAAA,EAAQ,UAAA;AAAA,QACxB,IAAA,EAAM,GAAA,CAAI,MAAA,EAAQ,IAAA,GACd;AAAA,UACE,EAAA,EAAI,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,EAAA;AAAA,UACpB,IAAA,EAAM,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,IAAA;AAAA,UACtB,KAAA,EAAO,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK;AAAA,SACzB,GACA;AAAA,OACN;AAAA,IACF;AAAA,GACF;AACF;;;AC7CA,IAAI,aAAA,GAMO,IAAA;AAGX,SAAS,mBAAmB,GAAA,EAM1B;AACA,EAAA,MAAM,GAAA,GAAO,GAAA,CAAI,UAAA,GAAqE,WAAW,KAAK,EAAC;AACvG,EAAA,OAAO;AAAA,IACL,cACE,GAAA,CAAI,YAAA,KAAiB,SAAY,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,GAAI,MAAA;AAAA,IAC9D,kBAAA,EAAoB,IAAI,kBAAA,KAAuB,IAAA;AAAA,IAC/C,gBAAA,EAAkB,IAAI,gBAAA,KAAqB,KAAA;AAAA;AAAA,IAC3C,qBACE,OAAO,GAAA,CAAI,mBAAA,KAAwB,QAAA,GAAW,IAAI,mBAAA,GAAsB,GAAA;AAAA,IAC1E,kBACE,OAAO,GAAA,CAAI,gBAAA,KAAqB,QAAA,GAAW,IAAI,gBAAA,GAAmB;AAAA,GACtE;AACF;AAMA,IAAM,MAAA,GAAiB;AAAA,EACrB,IAAA,EAAM,WAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,WAAA,EAAa,wEAAA;AAAA,EACb,UAAA,EAAY,SAAA;AAAA,EACZ,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO,IAAA;AAAA,IACP,aAAA,EAAe,IAAA;AAAA,IACf,WAAW;AAAC,GACd;AAAA,EACA,YAAA,EAAc,oBAAA;AAAA,EACd,aAAA,EAAe;AAAA,IACb,eAAA,EAAiB,CAAA;AAAA,IACjB,kBAAA,EAAoB,KAAA;AAAA,IACpB,mBAAA,EAAqB,GAAA;AAAA,IACrB,gBAAA,EAAkB;AAAA,GACpB;AAAA,EAEA,MAAM,MAAM,GAAA,EAAK;AACf,IAAA,MAAM,GAAA,GAAM,mBAAmB,GAAG,CAAA;AAClC,IAAA,MAAM,MAAM,GAAA,CAAI,GAAA;AAEhB,IAAA,GAAA,CAAI,KAAK,6BAA6B,CAAA;AAGtC,IAAA,MAAM,UAAA,GAA4B;AAAA,MAChC,cAAc,GAAA,CAAI,YAAA;AAAA,MAClB,kBAAA,EAAoB,IAAI,kBAAA,IAAsB,KAAA;AAAA,MAC9C,gBAAA,EAAkB,IAAI,gBAAA,IAAoB,IAAA;AAAA,MAC1C,mBAAA,EAAqB,IAAI,mBAAA,IAAuB,GAAA;AAAA,MAChD,gBAAA,EAAkB,IAAI,gBAAA,IAAoB;AAAA,KAC5C;AAMA,IAAA,MAAM,IAAA,GACJ,GAAA,CAAI,kBAAA,KAAuB,KAAA,GACvB,MAAA,GACA,IAAI,QAAA,CAAS,gBAAA,CAAiB,GAAA,CAAI,QAAQ,CAAA,EAAG,EAAE,KAAK,CAAA;AAC1D,IAAA,MAAM,GAAA,GAAM,IAAI,WAAA,CAAY;AAAA,MAC1B,OAAO,GAAA,CAAI,QAAA;AAAA,MACX,eAAA,EAAiB,IAAI,eAAA,IAAmB,CAAA;AAAA,MACxC,YAAA,EAAc,IAAI,GAAA,CAAA,CAAK,GAAA,CAAI,gBAAgB,EAAC,EAAG,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MAC1D,YAAA,EAAc,IAAI,GAAA,CAAA,CAAK,GAAA,CAAI,gBAAgB,EAAC,EAAG,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MAC1D,UAAA,EAAY,EAAA;AAAA,MACZ,GAAA;AAAA,MACA,mBAAmB,GAAA,CAAI,iBAAA;AAAA,MACvB,IAAA;AAAA,MACA,UAAU,GAAA,EAA8B;AAGtC,QAAA,GAAA,CAAI,UAAA,CAAW,6BAA6B,GAAG,CAAA;AAG/C,QAAA,MAAM,GAAA,GAAM,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,MAAA,IAAU,SAAA;AAC1C,QAAA,GAAA,CAAI,IAAA,CAAK,CAAA,oBAAA,EAAgB,GAAG,CAAA,OAAA,EAAU,GAAA,CAAI,MAAM,CAAA,GAAA,EAAM,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,CAAA;AAAA,MAChF;AAAA,KACD,CAAA;AAGD,IAAA,MAAM,WAAW,oBAAA,CAAqB;AAAA,MACpC,GAAA;AAAA,MACA,gBAAA,EAAkB,MAAM,UAAA,CAAW,YAAA;AAAA,MACnC,kBAAkB,UAAA,CAAW,gBAAA;AAAA,MAC7B;AAAA,KACD,CAAA;AACD,IAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,EAAE,GAAA,EAAK,CAAA;AAC7C,IAAA,GAAA,CAAI,KAAA,CAAM,SAAS,QAAQ,CAAA;AAC3B,IAAA,GAAA,CAAI,KAAA,CAAM,SAAS,QAAQ,CAAA;AAG3B,IAAA,MAAM,OAA0B,EAAC;AAGjC,IAAA,MAAM,gBAAA,GAAmB,GAAA,CAAI,+BAAA,CAAgC,YAAY;AACvE,MAAA,MAAM,OAAO,GAAA,CAAI,WAAA,CAAY,EAAE,KAAA,EAAO,GAAG,CAAA;AACzC,MAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAE/B,MAAA,MAAM,MAAA,GAAgD;AAAA,QACpD;AAAA,UACE,IAAA,EAAM,MAAA;AAAA,UACN,IAAA,EAAM;AAAA,YACJ,mBAAA;AAAA,YACA,CAAA,SAAA,EAAY,IAAI,WAAW,CAAA,4BAAA,CAAA;AAAA,YAC3B,gEAAA;AAAA,YACA,EAAA;AAAA,YACA,kBAAA;AAAA,YACA,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM;AACjB,cAAA,MAAM,MAAM,CAAA,CAAE,QAAA,IAAY,CAAA,KAAA,EAAQ,CAAA,CAAE,UAAU,SAAS,CAAA,CAAA;AACvD,cAAA,MAAM,KAAK,IAAI,IAAA,CAAK,CAAA,CAAE,SAAS,EAAE,kBAAA,EAAmB;AACpD,cAAA,OAAO,CAAA,GAAA,EAAM,EAAE,CAAA,IAAA,EAAO,GAAG,CAAA,SAAA,EAAY,CAAA,CAAE,MAAM,CAAA,GAAA,EAAM,CAAA,CAAE,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAAA,YACzE,CAAC,CAAA;AAAA,YACD;AAAA,WACF,CAAE,KAAK,IAAI;AAAA;AACb,OACF;AACA,MAAA,OAAO,MAAA;AAAA,IACT,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,KAAK,gBAAgB,CAAA;AAG1B,IAAA,MAAM,YAAA,GAAe,qBAAA,CAAsB,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA;AAMxD,IAAA,IAAA,CAAK,IAAA;AAAA,MACH,GAAA,CAAI,MAAA,CAAO,EAAA,CAAG,eAAA,EAAiB,CAAC,KAAA,KAAU;AACxC,QAAA,IAAI,CAAC,UAAA,CAAW,kBAAA,IAAsB,CAAC,WAAW,YAAA,EAAc;AAChE,QAAA,MAAM,OAAA,GAA4B;AAAA,UAChC,IAAI,KAAA,CAAM,EAAA;AAAA,UACV,WAAA,EAAa,MAAM,KAAA,CAAM,KAAA;AAAA,UACzB,YAAA,EAAc,MAAM,KAAA,CAAM,MAAA;AAAA,UAC1B,SAAA,EAAW,MAAM,KAAA,CAAM,SAAA;AAAA,UACvB,UAAA,EAAY,MAAM,KAAA,CAAM;AAAA,SAC1B;AACA,QAAA,MAAM,GAAA,GAAM,mBAAA;AAAA,UACV,mBAAmB,OAAO,CAAA;AAAA,UAC1B,UAAA,CAAW;AAAA,SACb;AACA,QAAA,KAAK,GAAA,CAAI,WAAA,CAAYC,aAAAA,CAAc,UAAA,CAAW,YAAY,GAAG,GAAG,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAC/E,UAAA,GAAA,CAAI,KAAA,CAAM,CAAA,yCAAA,EAA6C,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,QAChF,CAAC,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAEA,IAAA,IAAA,CAAK,IAAA;AAAA,MACH,GAAA,CAAI,MAAA,CAAO,EAAA,CAAG,eAAA,EAAiB,CAAC,KAAA,KAAU;AACxC,QAAA,IACE,CAAC,WAAW,YAAA,IACZ,UAAA,CAAW,uBAAuB,CAAA,IAClC,KAAA,CAAM,UAAA,GAAa,UAAA,CAAW,mBAAA,EAC9B;AACF,QAAA,MAAM,OAAA,GAA4B;AAAA,UAChC,MAAM,KAAA,CAAM,IAAA;AAAA,UACZ,IAAI,KAAA,CAAM,EAAA;AAAA,UACV,YAAY,KAAA,CAAM,UAAA;AAAA,UAClB,QAAQ,KAAA,CAAM;AAAA,SAChB;AACA,QAAA,MAAM,GAAA,GAAM,mBAAA;AAAA,UACV,mBAAmB,OAAO,CAAA;AAAA,UAC1B,UAAA,CAAW;AAAA,SACb;AACA,QAAA,KAAK,GAAA,CAAI,WAAA,CAAYA,aAAAA,CAAc,UAAA,CAAW,YAAY,GAAG,GAAG,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAC/E,UAAA,GAAA,CAAI,KAAA,CAAM,CAAA,kCAAA,EAAsC,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,QACzE,CAAC,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAEA,IAAA,IAAA,CAAK,IAAA;AAAA,MACH,GAAA,CAAI,MAAA,CAAO,EAAA,CAAG,oBAAA,EAAsB,CAAC,KAAA,KAAU;AAC7C,QAAA,IAAI,CAAC,UAAA,CAAW,gBAAA,IAAoB,CAAC,WAAW,YAAA,EAAc;AAC9D,QAAA,MAAM,GAAA,GAAM,mBAAA;AAAA,UACV,wBAAwB,KAAK,CAAA;AAAA,UAC7B,UAAA,CAAW;AAAA,SACb;AACA,QAAA,KAAK,GAAA,CAAI,WAAA,CAAYA,aAAAA,CAAc,UAAA,CAAW,YAAY,GAAG,GAAG,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AAC/E,UAAA,GAAA,CAAI,KAAA,CAAM,CAAA,sCAAA,EAA0C,GAAA,CAAc,OAAO,CAAA,CAAE,CAAA;AAAA,QAC7E,CAAC,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAOA,IAAA,MAAM,cAAA,GAAiB,GAAA,CAAI,cAAA,CAAe,CAAC,MAAM,KAAA,KAAU;AACzD,MAAA,MAAM,KAAA,GAAQ,mBAAmB,IAAI,CAAA;AACrC,MAAA,UAAA,CAAW,eAAe,KAAA,CAAM,YAAA;AAChC,MAAA,UAAA,CAAW,qBAAqB,KAAA,CAAM,kBAAA;AACtC,MAAA,UAAA,CAAW,mBAAmB,KAAA,CAAM,gBAAA;AACpC,MAAA,UAAA,CAAW,sBAAsB,KAAA,CAAM,mBAAA;AACvC,MAAA,UAAA,CAAW,mBAAmB,KAAA,CAAM,gBAAA;AACpC,MAAA,GAAA,CAAI,MAAM,oDAAA,EAAsD;AAAA,QAC9D,oBAAoB,UAAA,CAAW,kBAAA;AAAA,QAC/B,kBAAkB,UAAA,CAAW,gBAAA;AAAA,QAC7B,qBAAqB,UAAA,CAAW,mBAAA;AAAA,QAChC,YAAA,EAAc,WAAW,YAAA,IAAgB;AAAA,OAC1C,CAAA;AAAA,IACH,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,KAAK,cAAc,CAAA;AAGxB,IAAA,GAAA,CAAI,KAAA,EAAM;AAEV,IAAA,aAAA,GAAgB;AAAA,MACd,IAAA;AAAA,MACA,SAAA,EAAW,CAAC,QAAA,CAAS,IAAA,EAAM,SAAS,IAAI,CAAA;AAAA,MACxC,YAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,GAAA,CAAI,KAAK,uBAAuB,CAAA;AAAA,EAClC,CAAA;AAAA,EAEA,MAAM,SAAS,GAAA,EAAK;AAClB,IAAA,MAAM,KAAA,GAAQ,aAAA;AACd,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,aAAA,GAAgB,IAAA;AAEhB,IAAA,KAAA,CAAM,IAAI,IAAA,EAAK;AACf,IAAA,KAAA,MAAW,GAAA,IAAO,KAAA,CAAM,IAAA,EAAM,GAAA,EAAI;AAClC,IAAA,KAAA,MAAW,QAAQ,KAAA,CAAM,SAAA,EAAW,GAAA,CAAI,KAAA,CAAM,WAAW,IAAI,CAAA;AAC7D,IAAA,KAAA,MAAW,IAAA,IAAQ,MAAM,YAAA,EAAc;AACrC,MAAA,GAAA,CAAI,cAAc,UAAA,CAAW,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA;AAAA,IACvD;AAEA,IAAA,GAAA,CAAI,GAAA,CAAI,KAAK,2BAA2B,CAAA;AAAA,EAC1C,CAAA;AAAA,EAEA,MAAM,MAAA,GAAS;AACb,IAAA,MAAM,KAAA,GAAQ,aAAA;AACd,IAAA,IAAI,CAAC,OAAO,GAAA,EAAK,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,SAAS,wBAAA,EAAyB;AACvE,IAAA,MAAM,CAAA,GAAI,MAAM,KAAA,CAAM,GAAA,CAAI,MAAA,EAAO;AACjC,IAAA,OAAO,CAAA;AAAA,EACT;AACF,CAAA;AAEA,IAAO,aAAA,GAAQ","file":"index.js","sourcesContent":["import type { Logger } from '@wrongstack/core';\nimport { sleep } from '@wrongstack/core/utils';\nimport type { PollLock } from './poll-lock.js';\n\n// ---------------------------------------------------------------------------\n// Redaction helpers\n// ---------------------------------------------------------------------------\n/** Redact the bot token from a URL for safe logging. */\nfunction redactToken(url: string, token: string): string {\n return url.replace(token, '[REDACTED]');\n}\n\n// ---------------------------------------------------------------------------\n// Telegram Bot API types (subset used by this plugin)\n// ---------------------------------------------------------------------------\n\ninterface TgUser {\n id: number;\n is_bot: boolean;\n first_name: string;\n username?: string | undefined;\n}\n\ninterface TgChat {\n id: number;\n type: 'private' | 'group' | 'supergroup' | 'channel';\n title?: string | undefined;\n username?: string | undefined;\n}\n\ninterface TgMessage {\n message_id: number;\n from?: TgUser | undefined;\n chat: TgChat;\n date: number;\n text?: string | undefined;\n}\n\ninterface TgUpdate {\n update_id: number;\n message?: TgMessage | undefined;\n edited_message?: TgMessage | undefined;\n}\n\ninterface TgResponse<T> {\n ok: boolean;\n result?: T | undefined;\n description?: string | undefined;\n error_code?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Incoming message shape emitted as a custom event\n// ---------------------------------------------------------------------------\n\nexport interface TelegramIncomingMessage {\n messageId: number;\n chatId: number;\n chatType: string;\n userId?: number | undefined;\n userName?: string | undefined;\n text: string;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bot options\n// ---------------------------------------------------------------------------\n\nexport interface TelegramBotOptions {\n token: string;\n pollIntervalSec: number;\n allowedUsers: Set<string>;\n allowedChats: Set<string>;\n /** Max messages to buffer for the agent to read. Default: 50. */\n bufferSize: number;\n log: Logger;\n /** Called for each incoming message that passes allowlist checks. */\n onMessage(msg: TelegramIncomingMessage): void;\n /**\n * Optional path to a file that stores the polling offset. When provided,\n * the offset is persisted on every successful poll and restored on startup,\n * preventing message replay after crashes or restarts.\n */\n offsetStoragePath?: string | undefined;\n /**\n * Optional cross-process single-poller lock. Telegram allows one\n * `getUpdates` consumer per token; when another wstack instance holds the\n * lock, this bot stands by (no polling) and takes over once the holder\n * stops or its heartbeat goes stale.\n */\n lock?: PollLock | undefined;\n /** How often a standby instance retries acquiring the lock. Default: 15s. */\n standbyRetryMs?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Bot\n// ---------------------------------------------------------------------------\n\nexport class TelegramBot {\n private readonly baseUrl: string;\n /** Base URL with token redacted, safe to use in log calls. */\n private readonly safeBaseUrl: string;\n private readonly pollIntervalMs: number;\n private readonly allowedUsers: Set<string>;\n private readonly allowedChats: Set<string>;\n private readonly log: Logger;\n private readonly onMessage: (msg: TelegramIncomingMessage) => void;\n private readonly controller = new AbortController();\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private pollActive = false;\n private offset = 0;\n /**\n * Consecutive HTTP 409 (\"another getUpdates in flight\") responses. Two\n * wstack instances polling the same bot token used to fight at full poll\n * speed forever, erroring on every cycle. After CONFLICT_BACKOFF_AFTER\n * consecutive conflicts this instance backs off to a slow poll and warns\n * once; any successful poll resets to the normal cadence.\n */\n private conflictStreak = 0;\n private static readonly CONFLICT_BACKOFF_AFTER = 3;\n private static readonly CONFLICT_POLL_MS = 60_000;\n private _startedAt: number | null = null;\n /** If set, the offset is persisted here after each successful poll. */\n private readonly offsetStoragePath?: string | undefined;\n /** Single-poller election across wstack instances sharing this token. */\n private readonly lock?: PollLock | undefined;\n private readonly standbyRetryMs: number;\n private standbyTimer: ReturnType<typeof setTimeout> | null = null;\n private standbyAnnounced = false;\n\n // Circular buffer for incoming messages\n private readonly bufferMax: number;\n private readonly buffer: TelegramIncomingMessage[] = [];\n\n constructor(opts: TelegramBotOptions) {\n this.baseUrl = `https://api.telegram.org/bot${opts.token}`;\n this.safeBaseUrl = redactToken(this.baseUrl, opts.token);\n this.pollIntervalMs = opts.pollIntervalSec * 1000;\n this.allowedUsers = opts.allowedUsers;\n this.allowedChats = opts.allowedChats;\n this.bufferMax = opts.bufferSize;\n this.log = opts.log;\n this.onMessage = opts.onMessage;\n this.offsetStoragePath = opts.offsetStoragePath;\n this.lock = opts.lock;\n this.standbyRetryMs = opts.standbyRetryMs ?? 15_000;\n if (this.lock) {\n this.lock.onLost = () => this.handleLockLost();\n }\n\n // Restore persisted offset so a crash/restart doesn't cause message replay.\n if (this.offsetStoragePath) {\n void this.loadOffset();\n }\n }\n\n // ------------------------------------------------------------------\n // Lifecycle\n // ------------------------------------------------------------------\n\n /** Start polling for updates. Idempotent. */\n start(): void {\n if (this.pollActive) return;\n this.pollActive = true;\n this._startedAt = Date.now();\n this.acquireAndPoll();\n }\n\n /** Stop polling and cancel all in-flight requests. */\n stop(): void {\n this.pollActive = false;\n this.controller.abort();\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.standbyTimer) {\n clearTimeout(this.standbyTimer);\n this.standbyTimer = null;\n }\n this.lock?.release();\n this.log.info('Telegram bot stopped');\n }\n\n /** True when the bot is started but waiting for the poll lock. */\n get standby(): boolean {\n return this.pollActive && this.lock !== undefined && !this.lock.held;\n }\n\n /**\n * Acquire the poll lock (when configured) and start the poll loop, or\n * stand by and retry until the current holder releases it.\n */\n private acquireAndPoll(): void {\n if (!this.pollActive) return;\n if (this.lock && !this.lock.tryAcquire()) {\n if (!this.standbyAnnounced) {\n this.standbyAnnounced = true;\n this.log.info(\n 'Telegram: another wstack instance is already polling this bot token — standing by; will take over when it stops.',\n );\n }\n this.standbyTimer = setTimeout(() => this.acquireAndPoll(), this.standbyRetryMs);\n this.standbyTimer.unref?.();\n return;\n }\n if (this.standbyAnnounced) {\n this.standbyAnnounced = false;\n this.log.info('Telegram: poll lock acquired — taking over polling.');\n } else {\n this.log.info(`Telegram bot polling started (${this.safeBaseUrl})`);\n }\n this.schedulePoll();\n }\n\n /** The lock was stolen while we held it — pause polling and stand by. */\n private handleLockLost(): void {\n if (!this.pollActive) return;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.log.warn('Telegram: poll lock lost to another instance — pausing polling and standing by.');\n this.standbyAnnounced = true; // acquireAndPoll already announced via this warn\n this.standbyTimer = setTimeout(() => this.acquireAndPoll(), this.standbyRetryMs);\n this.standbyTimer.unref?.();\n }\n\n get startedAt(): number | null {\n return this._startedAt;\n }\n\n get running(): boolean {\n return this.pollActive;\n }\n\n // ------------------------------------------------------------------\n // Buffer — incoming messages the agent can read\n // ------------------------------------------------------------------\n\n /** Return buffered messages, newest first. Optionally filter by chat. */\n getMessages(opts?: { chatId?: string | number | undefined; limit?: number | undefined }): TelegramIncomingMessage[] {\n let msgs = [...this.buffer].reverse();\n if (opts?.chatId) {\n const cid = String(opts.chatId);\n msgs = msgs.filter((m) => String(m.chatId) === cid);\n }\n const limit = opts?.limit ?? 20;\n return msgs.slice(0, limit);\n }\n\n /** Drop messages older than the given message ID from the buffer. */\n acknowledge(lastMessageId: number): number {\n const before = this.buffer.length;\n let i = this.buffer.length;\n while (i-- > 0) {\n const buffered = this.buffer[i];\n if (buffered && buffered.messageId <= lastMessageId) {\n this.buffer.splice(0, i + 1);\n break;\n }\n }\n return before - this.buffer.length;\n }\n\n get bufferCount(): number {\n return this.buffer.length;\n }\n\n // ------------------------------------------------------------------\n // Outgoing — send a message\n // ------------------------------------------------------------------\n\n async sendMessage(chatId: string | number, text: string): Promise<TgResponse<TgMessage>> {\n const url = `${this.baseUrl}/sendMessage`;\n const body = JSON.stringify({\n chat_id: String(chatId),\n text,\n disable_web_page_preview: true,\n });\n\n this.log.debug(`Sending Telegram message to ${chatId} (${text.length} chars)`);\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(10_000),\n });\n const data = (await res.json()) as TgResponse<TgMessage>;\n if (!data.ok) {\n throw new Error(`Telegram API error ${data.error_code}: ${data.description}`);\n }\n return data;\n } catch (err) {\n lastErr = err;\n if (attempt < 3) {\n this.log.debug(`Telegram sendMessage attempt ${attempt} failed, retrying in 1s...`);\n await sleep(1000);\n }\n }\n }\n throw lastErr;\n }\n\n // ------------------------------------------------------------------\n // Health\n // ------------------------------------------------------------------\n\n async health(): Promise<{ ok: boolean; username?: string | undefined; error?: string | undefined }> {\n try {\n const url = `${this.baseUrl}/getMe`;\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) });\n const data = (await res.json()) as TgResponse<TgUser>;\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description ?? 'Unknown error' };\n }\n return { ok: true, username: data.result.username };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n }\n\n // ------------------------------------------------------------------\n // Polling\n // ------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (!this.pollActive) return;\n // Lost the poll lock mid-flight — the standby retry loop owns recovery.\n if (this.lock && !this.lock.held) return;\n const delay =\n this.conflictStreak >= TelegramBot.CONFLICT_BACKOFF_AFTER\n ? TelegramBot.CONFLICT_POLL_MS\n : this.pollIntervalMs;\n this.pollTimer = setTimeout(() => {\n void this.poll().finally(() => this.schedulePoll());\n }, delay);\n }\n\n private async poll(): Promise<void> {\n try {\n const url = `${this.baseUrl}/getUpdates?offset=${this.offset}&timeout=10`;\n const res = await fetch(url, { signal: this.controller.signal });\n const data = (await res.json()) as TgResponse<TgUpdate[]>;\n\n if (!data.ok) {\n if (data.error_code === 409) {\n this.conflictStreak++;\n if (this.conflictStreak === TelegramBot.CONFLICT_BACKOFF_AFTER) {\n this.log.warn(\n this.lock\n ? 'Telegram: another consumer outside this machine is polling this bot token (HTTP 409) — backing off to 60s polls. Check other machines/bots using this token, or a registered webhook (deleteWebhook).'\n : 'Telegram: another instance is polling this bot token (HTTP 409) — backing off to 60s polls until it stops.',\n );\n }\n }\n this.log.debug(`Telegram getUpdates failed: ${data.description}`);\n return;\n }\n this.conflictStreak = 0;\n\n const updates = data.result ?? [];\n for (const upd of updates) {\n this.offset = upd.update_id + 1;\n const raw = upd.message ?? upd.edited_message;\n if (!raw?.text) continue;\n const msg = { ...raw, text: raw.text };\n this.processMessage(msg);\n }\n\n // Persist offset after each successful poll to prevent message replay\n // after crashes or restarts.\n if (this.offsetStoragePath && this.offset > 0) {\n void this.saveOffset();\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n this.log.debug(`Telegram poll error: ${(err as Error).message}`);\n }\n }\n\n private processMessage(msg: TgMessage & { text: string }): void {\n const chatId = String(msg.chat.id);\n const userId = msg.from ? String(msg.from.id) : undefined;\n\n // Allowlist checks\n if (this.allowedUsers.size > 0 && userId && !this.allowedUsers.has(userId)) {\n this.log.debug(`Ignoring message from user ${userId} (not in allowedUsers)`);\n void this.sendMessage(chatId, '⛔ You are not authorized to interact with this bot.');\n return;\n }\n if (this.allowedChats.size > 0 && !this.allowedChats.has(chatId)) {\n this.log.debug(`Ignoring message from chat ${chatId} (not in allowedChats)`);\n return;\n }\n\n const incoming: TelegramIncomingMessage = {\n messageId: msg.message_id,\n chatId: msg.chat.id,\n chatType: msg.chat.type,\n userId: msg.from?.id,\n userName: msg.from?.username ?? msg.from?.first_name,\n text: msg.text,\n timestamp: msg.date * 1000,\n };\n\n // Push to circular buffer\n this.buffer.push(incoming);\n while (this.buffer.length > this.bufferMax) this.buffer.shift();\n\n this.onMessage(incoming);\n }\n\n private async loadOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { readFileSync } = await import('node:fs');\n const raw = readFileSync(this.offsetStoragePath, 'utf8').trim();\n const n = Number.parseInt(raw, 10);\n if (Number.isFinite(n) && n >= 0) {\n this.offset = n;\n this.log.debug(`Telegram polling offset restored: ${this.offset}`);\n }\n } catch {\n // File doesn't exist yet — start from 0, which is correct.\n }\n }\n\n private async saveOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { writeFileSync } = await import('node:fs');\n // Write atomically so a crash mid-write can't leave a corrupt file.\n writeFileSync(this.offsetStoragePath, String(this.offset), 'utf8');\n } catch (err) {\n this.log.debug(`Failed to persist Telegram offset: ${err}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Truncate text to fit Telegram's 4096-char message limit.\n * Preserves semantic boundaries in this priority order:\n * 1. Paragraph break (double newline)\n * 2. Sentence break (. ! ? followed by space/newline)\n * 3. Word break (space)\n * 4. Hard cut with ellipsis\n *\n * When a clean boundary is found, appends \"…\" to signal intentional truncation.\n */\nexport function truncateForTelegram(text: string, maxLen = 4000): string {\n if (text.length <= maxLen) return text;\n\n // Reserve room for truncation suffix\n const cutoff = maxLen - 30;\n if (cutoff <= 0) return `${text.slice(0, maxLen - 1)}…`;\n\n const searchEnd = Math.min(text.length, maxLen);\n\n // 1. Paragraph boundary (double newline)\n const paraIdx = text.lastIndexOf('\\n\\n', searchEnd);\n if (paraIdx > cutoff) {\n return `${text.slice(0, paraIdx)}\\n\\n…`;\n }\n\n // 2. Single newline boundary\n const nlIdx = text.lastIndexOf('\\n', searchEnd);\n if (nlIdx > cutoff) {\n return `${text.slice(0, nlIdx)}\\n…`;\n }\n\n // 3. Sentence boundary (. ! ? followed by space or newline)\n const sentenceRe = /[.!?](?=\\s)/g;\n let match: RegExpExecArray | null;\n let sentenceIdx = -1;\n match = sentenceRe.exec(text);\n while (match !== null) {\n if (match.index >= searchEnd) break;\n if (match.index > cutoff) sentenceIdx = match.index + 1;\n match = sentenceRe.exec(text);\n }\n if (sentenceIdx > cutoff) {\n return `${text.slice(0, sentenceIdx)}…`;\n }\n\n // 4. Word boundary (space)\n const spaceIdx = text.lastIndexOf(' ', searchEnd);\n if (spaceIdx > cutoff) {\n return `${text.slice(0, spaceIdx)} …`;\n }\n\n // 5. Hard cut\n return `${text.slice(0, maxLen - 20)}…[+${text.length - maxLen + 20} chars]`;\n}\n\n/**\n * Escape HTML special chars for Telegram's HTML parse mode.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","import type { PluginAPI } from '@wrongstack/core';\r\n\r\nexport const PLUGIN_NAME = 'telegram';\r\n\r\nexport interface TelegramPluginConfig {\r\n /** Telegram Bot API token (from @BotFather). */\r\n botToken: string;\r\n /**\r\n * Default chat ID for outgoing notifications.\r\n * The agent's `telegram_send` tool can override per-call.\r\n */\r\n notifyChatId?: string | number | undefined;\r\n /**\r\n * List of user/chat IDs allowed to interact with the bot.\r\n * Empty = allow all. Recommended to set in production.\r\n */\r\n allowedUsers?: Array<string | number> | undefined;\r\n /**\r\n * List of group/chat IDs the bot is allowed to read from.\r\n * Empty = allow all. Narrow this to prevent noise.\r\n */\r\n allowedChats?: Array<string | number> | undefined;\r\n /** Polling interval in seconds (default: 2). */\r\n pollIntervalSec?: number | undefined;\r\n /** Notify on Telegram when a session ends. */\r\n notifyOnSessionEnd?: boolean | undefined;\r\n /** Notify when a tool runs longer than this threshold (ms). Set 0 to disable. */\r\n longToolThresholdMs?: number | undefined;\r\n /** Notify (humanized) when a `delegate` subagent finishes. Default: true. */\r\n notifyOnDelegate?: boolean | undefined;\r\n /** Maximum message length for Telegram (Telegram caps at 4096). */\r\n maxMessageLength?: number | undefined;\r\n /**\r\n * Path to a file that stores the Telegram polling offset. When set,\r\n * the offset is persisted on every successful poll and restored on startup,\r\n * preventing message replay after crashes or restarts.\r\n * The directory must already exist and be writable.\r\n */\r\n offsetStoragePath?: string | undefined;\r\n /**\r\n * Elect a single poller per bot token across wstack instances (default:\r\n * true). Telegram allows one `getUpdates` consumer per token; without this,\r\n * two instances sharing a token fight and get HTTP 409 on every poll.\r\n * Extra instances stand by and take over when the active poller stops.\r\n * Set false only if this is guaranteed to be the sole consumer.\r\n */\r\n singleInstanceLock?: boolean | undefined;\r\n}\r\n\r\nexport const DEFAULT_CONFIG: Required<Omit<TelegramPluginConfig, 'botToken' | 'notifyChatId' | 'offsetStoragePath'>> = {\r\n allowedUsers: [],\r\n allowedChats: [],\r\n pollIntervalSec: 2,\r\n notifyOnSessionEnd: false,\r\n longToolThresholdMs: 30_000,\r\n notifyOnDelegate: true,\r\n maxMessageLength: 4000,\r\n singleInstanceLock: true,\r\n};\r\n\r\nexport const telegramConfigSchema = {\r\n type: 'object',\r\n properties: {\r\n botToken: { type: 'string', description: 'Telegram Bot API token from @BotFather' },\r\n notifyChatId: {\r\n oneOf: [{ type: 'string' }, { type: 'integer' }],\r\n description: 'Default chat ID for outgoing notifications',\r\n },\r\n allowedUsers: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'User IDs allowed to interact with the bot',\r\n },\r\n allowedChats: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'Chat IDs the bot is allowed to read from',\r\n },\r\n pollIntervalSec: {\r\n type: 'integer',\r\n minimum: 1,\r\n maximum: 60,\r\n description: 'Polling interval in seconds',\r\n },\r\n notifyOnSessionEnd: { type: 'boolean' },\r\n longToolThresholdMs: { type: 'integer', minimum: 0 },\r\n notifyOnDelegate: { type: 'boolean' },\r\n maxMessageLength: { type: 'integer', minimum: 100, maximum: 4096 },\r\n singleInstanceLock: {\r\n type: 'boolean',\r\n description: 'Elect a single getUpdates poller per bot token across wstack instances (default true)',\r\n },\r\n },\r\n required: ['botToken'],\r\n};\r\n\r\nexport function readTelegramConfig(\r\n api: Pick<PluginAPI, 'config'>,\r\n): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> &\r\n Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'> {\r\n const config = api.config as unknown as Record<string, unknown>;\r\n const extensions = config.extensions as Record<string, unknown> | undefined;\r\n const pluginEntries = config.plugins;\r\n const legacyPlugins = pluginEntries as Record<string, unknown> | undefined;\r\n const legacyOpts =\r\n legacyPlugins && !Array.isArray(legacyPlugins) ? legacyPlugins[PLUGIN_NAME] : undefined;\r\n const entryOpts = pluginOptionsFromEntries(pluginEntries);\r\n const opts = {\r\n ...((legacyOpts ?? entryOpts) as TelegramPluginConfig),\r\n ...((extensions?.[PLUGIN_NAME] ?? {}) as TelegramPluginConfig),\r\n };\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...opts,\r\n };\r\n}\r\n\r\nfunction pluginOptionsFromEntries(entries: unknown): TelegramPluginConfig | undefined {\r\n if (!Array.isArray(entries)) return undefined;\r\n const found = entries.find(\r\n (entry) =>\r\n typeof entry === 'object' &&\r\n entry !== null &&\r\n 'name' in entry &&\r\n ((entry as { name?: unknown | undefined }).name === '@wrongstack/telegram' ||\r\n (entry as { name?: unknown | undefined }).name === PLUGIN_NAME),\r\n ) as { name?: unknown | undefined; options?: unknown | undefined } | undefined;\r\n return found?.options && typeof found.options === 'object'\r\n ? (found.options as TelegramPluginConfig)\r\n : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\n// Humanizers for agent events forwarded to Telegram.\n//\n// The host emits rich structured events; this module turns them into short,\n// readable chat messages. Kept pure (no bot / IO) so it's trivially testable.\n//\n// Design rules for Telegram readability:\n// - Start with an emoji status icon so the outcome is scannable.\n// - Lead with the *headline* (what happened), then context, then stats.\n// - Never embed raw JSON. Never concatenate object dumps.\n// - Keep messages under 2000 chars so they fit one mobile screen.\n// - Use emoji sparingly — status markers only, no decoration.\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Payload types (subsets of core event shapes)\n// ---------------------------------------------------------------------------\n\n/** Subset of the core `delegate.completed` event payload we render. */\nexport interface DelegateCompletedLike {\n target: string;\n task: string;\n ok: boolean;\n status?: string | undefined;\n summary: string;\n durationMs: number;\n iterations: number;\n toolCalls: number;\n costUsd?: number | undefined;\n subagentId?: string | undefined;\n}\n\n/** Subset of core `tool.executed` event payload. */\nexport interface ToolExecutedLike {\n name: string;\n ok: boolean;\n durationMs: number;\n /** Raw tool output — only the first 300 chars are rendered. */\n output?: string | undefined;\n}\n\n/** Subset of core `session.ended` event payload (from Usage). */\nexport interface SessionEndedLike {\n id: string;\n inputTokens: number;\n outputTokens: number;\n cacheRead?: number | undefined;\n cacheWrite?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\n/** Compact human duration: `42s`, `3m`, `1.5h`. */\nexport function fmtDuration(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/**\n * Format a numeric count of tokens for human readability.\n * Uses comma-separated thousands: 1,234, 56,789.\n */\nexport function fmtTokens(n: number): string {\n return n.toLocaleString('en-US');\n}\n\n/**\n * Try to render a tool's output as a short human-readable snippet.\n * Strips JSON braces/quoting, limits to ~300 chars, preserves first/last lines.\n */\nexport function fmtToolOutput(raw: string | undefined): string {\n if (!raw) return '(no output)';\n const cleaned = raw\n .replace(/^[{[]\\s*/, '') // strip leading JSON opening\n .replace(/\\s*[}\\]]$/, '') // strip trailing JSON closing\n .replace(/\"([^\"]+)\":/g, '$1: ') // unquote JSON keys, add space for readability\n .replace(/\\\\n/g, '\\n') // expand escaped newlines\n .replace(/\\\\\"/g, '\"') // expand escaped quotes\n .trim()\n || raw;\n\n // Try to split into short lines; show the first 3 meaningful ones.\n const lines = cleaned.split('\\n').filter((l) => l.trim().length > 0);\n let preview = lines.slice(0, 3).join('\\n');\n if (lines.length > 3) preview += `\\n… +${lines.length - 3} more lines`;\n if (preview.length > 300) preview = `${preview.slice(0, 297)}…`;\n return preview;\n}\n\n// ---------------------------------------------------------------------------\n// Event → message formatters\n// ---------------------------------------------------------------------------\n\n/**\n * Render a finished delegation as a readable Telegram message.\n *\n * Example:\n * ✅ Delegate → bug-hunter · success\n * Found 3 null-deref risks in auth.ts and patched the worst one…\n * ⏱ 3m · 4 iter · 37 tools · 💲0.0820\n */\nexport function formatDelegateCompleted(e: DelegateCompletedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const status = e.status ?? (e.ok ? 'success' : 'failed');\n const task = e.task.length > 160 ? `${e.task.slice(0, 159)}…` : e.task;\n\n // Prefer the host's one-line summary; fall back to echoing the task when a\n // failure produced no summary.\n const body = e.summary?.trim() || `(no summary) — ${task}`;\n\n const stats = [\n `⏱ ${fmtDuration(e.durationMs)}`,\n `${e.iterations} iter`,\n `${e.toolCalls} tools`,\n ];\n if (typeof e.costUsd === 'number' && e.costUsd > 0) {\n stats.push(`💲${e.costUsd.toFixed(4)}`);\n }\n\n return [`${icon} Delegate → ${e.target} · ${status}`, body, stats.join(' · ')].join('\\n');\n}\n\n/**\n * Render a long-running tool execution notification.\n *\n * Example:\n * ✅ bash completed in 45.2s\n * pnpm test — 12 suites, 47 tests passed\n * …\n */\nexport function formatToolExecuted(e: ToolExecutedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const sec = (e.durationMs / 1000).toFixed(1);\n const headline = `${icon} ${e.name} completed in ${sec}s`;\n\n const output = fmtToolOutput(e.output);\n // Only include output if it's short enough to be readable on mobile\n if (output === '(no output)') return headline;\n return `${headline}\\n${output}`;\n}\n\n/**\n * Render a session-end notification.\n *\n * Example:\n * 🏁 Session sess_abcd ended\n * ⬇ 8,234 in · ⬆ 3,456 out · 11,690 total\n * Cache: 1,200 read · 800 written\n */\nexport function formatSessionEnded(e: SessionEndedLike): string {\n const id = e.id.length > 8 ? e.id.slice(0, 8) : e.id;\n const total = e.inputTokens + e.outputTokens;\n\n const lines = [\n `🏁 Session ${id} ended`,\n `⬇ ${fmtTokens(e.inputTokens)} in · ⬆ ${fmtTokens(e.outputTokens)} out · ${fmtTokens(total)} total`,\n ];\n\n // Show cache stats when available\n if (e.cacheRead || e.cacheWrite) {\n const parts: string[] = [];\n if (e.cacheRead && e.cacheRead > 0) parts.push(`${fmtTokens(e.cacheRead)} cache read`);\n if (e.cacheWrite && e.cacheWrite > 0) parts.push(`${fmtTokens(e.cacheWrite)} cache written`);\n if (parts.length > 0) lines.push(`📦 ${parts.join(' · ')}`);\n }\n\n return lines.join('\\n');\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { Logger } from '@wrongstack/core';\nimport { wstackGlobalRoot } from '@wrongstack/core/utils';\n\n/**\n * Cross-process single-poller lock for a Telegram bot token.\n *\n * Telegram allows exactly one `getUpdates` consumer per token; two wstack\n * instances (TUI + WebUI, or two projects) polling the same token fight each\n * other and every cycle returns HTTP 409. This lock elects one poller: the\n * holder writes a heartbeat to a lock file under `~/.wrongstack/telegram/`,\n * other instances stand by and take over when the heartbeat goes stale or\n * the file disappears.\n */\n\ninterface LockFilePayload {\n /** Unique per PollLock instance — `pid` alone can't distinguish two locks in one process. */\n id: string;\n pid: number;\n acquiredAt: number;\n heartbeatAt: number;\n}\n\nexport interface PollLockOptions {\n log?: Logger | undefined;\n /** How often the holder refreshes its heartbeat. Default: 15s. */\n heartbeatMs?: number | undefined;\n /** A lock whose heartbeat is older than this is considered stale. Default: 45s. */\n staleMs?: number | undefined;\n}\n\n/** Lock file path for a bot token. The token itself never appears in the path. */\nexport function lockPathForToken(token: string, globalRoot = wstackGlobalRoot()): string {\n const hash = createHash('sha256').update(token).digest('hex').slice(0, 12);\n return join(globalRoot, 'telegram', `poll-${hash}.lock`);\n}\n\nexport class PollLock {\n private readonly id = `${process.pid}:${randomUUID()}`;\n private readonly heartbeatMs: number;\n private readonly staleMs: number;\n private readonly log?: Logger | undefined;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private _held = false;\n\n /** Invoked when the lock is stolen by another instance while held. */\n onLost?: (() => void) | undefined;\n\n constructor(\n readonly lockPath: string,\n opts?: PollLockOptions,\n ) {\n this.heartbeatMs = opts?.heartbeatMs ?? 15_000;\n this.staleMs = opts?.staleMs ?? 45_000;\n this.log = opts?.log;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n /**\n * Try to acquire the lock. Returns true when this instance is now (or was\n * already) the holder. Safe to call repeatedly from a standby retry loop.\n */\n tryAcquire(): boolean {\n if (this._held) return true;\n\n const existing = this.readLock();\n if (existing && !this.isStale(existing)) return false;\n\n try {\n mkdirSync(dirname(this.lockPath), { recursive: true });\n // Remove any stale or corrupt file first, then create exclusively: when\n // two standby instances race for a stale lock, `wx` makes exactly one win.\n try {\n unlinkSync(this.lockPath);\n } catch {\n // Nothing to remove, or a competing instance already removed it.\n }\n const now = Date.now();\n const payload: LockFilePayload = {\n id: this.id,\n pid: process.pid,\n acquiredAt: now,\n heartbeatAt: now,\n };\n writeFileSync(this.lockPath, JSON.stringify(payload), { flag: 'wx' });\n } catch {\n return false; // Lost the race or the directory is unwritable.\n }\n\n this._held = true;\n this.startHeartbeat();\n return true;\n }\n\n /** Release the lock and stop the heartbeat. Idempotent. */\n release(): void {\n this.stopHeartbeat();\n if (!this._held) return;\n this._held = false;\n try {\n if (this.readLock()?.id === this.id) unlinkSync(this.lockPath);\n } catch {\n // Best effort — a stale file is reclaimed via the staleness check anyway.\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => this.heartbeatTick(), this.heartbeatMs);\n this.heartbeatTimer.unref?.();\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private heartbeatTick(): void {\n const current = this.readLock();\n if (!current || current.id !== this.id) {\n // Another instance stole the lock (e.g. this process was suspended past\n // the staleness window). Stop claiming it and notify the owner.\n this._held = false;\n this.stopHeartbeat();\n this.log?.warn('Telegram: poll lock was taken over by another instance.');\n this.onLost?.();\n return;\n }\n try {\n const payload: LockFilePayload = { ...current, heartbeatAt: Date.now() };\n // Write via temp + rename so a reader never sees a half-written file.\n const tmp = `${this.lockPath}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(payload));\n renameSync(tmp, this.lockPath);\n } catch (err) {\n this.log?.debug(`Telegram: poll lock heartbeat write failed: ${err}`);\n }\n }\n\n private readLock(): LockFilePayload | null {\n try {\n const raw = readFileSync(this.lockPath, 'utf8');\n const parsed = JSON.parse(raw) as LockFilePayload;\n if (typeof parsed.id !== 'string' || typeof parsed.pid !== 'number') return null;\n return parsed;\n } catch {\n return null; // Missing or corrupt — treated as stale/absent.\n }\n }\n\n private isStale(payload: LockFilePayload): boolean {\n if (Date.now() - payload.heartbeatAt > this.staleMs) return true;\n return !this.isPidAlive(payload.pid);\n }\n\n private isPidAlive(pid: number): boolean {\n if (pid === process.pid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means the process exists but belongs to another user.\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n }\n}\n","import { expectDefined } from '@wrongstack/core';\nimport type { PluginAPI, SlashCommand } from '@wrongstack/core';\r\nimport type { TelegramBot } from '../bot.js';\r\nimport type { TelegramPluginConfig } from '../config.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:status\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgStatusCommand(bot: TelegramBot, cfg: TelegramPluginConfig): SlashCommand {\r\n return {\r\n name: 'status',\r\n aliases: ['tgstat', 'tgs'],\r\n description: 'Show Telegram bot connection status and config',\r\n help: `Usage: /telegram:status\r\n\r\nShows whether the bot is connected, its username, polling interval,\r\nallowlist status, and notification settings.`,\r\n async run(_args, _ctx) {\r\n const health = await bot.health();\r\n const lines = [\r\n '═══ Telegram Plugin Status ═══',\r\n '',\r\n `Bot: ${health.ok ? `✅ @${health.username ?? 'connected'}` : `❌ ${health.error ?? 'offline'}`}`,\r\n `Running: ${bot.running ? 'yes' : 'no'}`,\r\n `Started: ${bot.startedAt ? new Date(bot.startedAt).toLocaleTimeString() : 'N/A'}`,\r\n `Poll: every ${cfg.pollIntervalSec ?? 2}s`,\r\n `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers?.length} users` : 'everyone (users)'} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats?.length} chats` : 'everyone (chats)'}`,\r\n `Notify: sessionEnd=${cfg.notifyOnSessionEnd ?? false}, longTool=${cfg.longToolThresholdMs ? `${cfg.longToolThresholdMs}ms` : 'off'}`,\r\n ];\r\n\r\n return { message: lines.join('\\n') };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:send\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgSendCommand(\r\n bot: TelegramBot,\r\n defaultChatId: string | number | undefined,\r\n): SlashCommand {\r\n return {\r\n name: 'send',\r\n description: 'Send a message to a Telegram chat',\r\n help: `Usage: /telegram:send [chat_id] <message>\r\n\r\nSend a message to a Telegram chat.\r\n- First argument (optional): chat or user ID. Uses notifyChatId from config when omitted.\r\n- Everything else: the message text.\r\n\r\nExamples:\r\n /telegram:send 123456789 Build completed successfully ✓\r\n /telegram:send Deploy finished — check staging`,\r\n async run(args, _ctx) {\r\n if (!args.trim()) {\r\n return { message: 'Usage: /telegram:send [chat_id] <message>' };\r\n }\r\n\r\n let chatId: string | number;\r\n let text: string;\r\n\r\n // First token might be a numeric chat_id\r\n const parts = args.trim().split(/\\s+/);\r\n const maybeId = parts[0];\r\n if (/^\\d+$/.test(expectDefined(maybeId)) && parts.length > 1) {\r\n chatId = expectDefined(maybeId);\r\n text = parts.slice(1).join(' ');\r\n } else if (defaultChatId) {\r\n chatId = defaultChatId;\r\n text = args.trim();\r\n } else {\r\n return {\r\n message:\r\n 'No chat_id provided and no default notifyChatId configured.\\nUsage: /telegram:send <chat_id> <message>',\r\n };\r\n }\r\n\r\n try {\r\n const res = await bot.sendMessage(chatId, text);\r\n return {\r\n message: `✅ Message sent to ${chatId} (msg_id=${res.result?.message_id ?? '?'})`,\r\n };\r\n } catch (err) {\r\n return { message: `❌ Failed to send: ${(err as Error).message}` };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:chatid\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgChatIdCommand(defaultChatId?: string | number): SlashCommand {\r\n const chatIdStr = defaultChatId ? String(defaultChatId) : null;\r\n return {\r\n name: 'chatid',\r\n description: 'Show the configured default chat ID',\r\n help: `Usage: /telegram:chatid\r\n\r\nShows the current default notifyChatId used for notifications\r\nand the \\`telegram_send\\` tool when no chat_id is specified.`,\r\n async run(_args, _ctx) {\r\n if (chatIdStr) {\r\n return { message: `Configured notifyChatId: ${chatIdStr}` };\r\n }\r\n return { message: 'No notifyChatId configured. Set it in the plugin config or pass chat_id explicitly to telegram_send.' };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Register all\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function registerSlashCommands(\r\n api: PluginAPI,\r\n bot: TelegramBot,\r\n cfg: TelegramPluginConfig,\r\n): string[] {\r\n const cmds = [\r\n tgStatusCommand(bot, cfg),\r\n tgSendCommand(bot, cfg.notifyChatId),\r\n tgChatIdCommand(cfg.notifyChatId),\r\n ];\r\n for (const cmd of cmds) api.slashCommands.register(cmd);\r\n return cmds.map((c) => c.name);\r\n}\r\n","import type { Tool } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\n\ninterface TelegramReadInput {\n /** Filter to messages from a specific chat/user ID. Omit to see all chats. */\n chat_id?: string | number | undefined;\n /** Max messages to return (default: 10, max: 50). */\n limit?: number | undefined;\n /**\n * If a message_id is provided, acknowledge all messages up to and\n * including this ID (mark them as processed / remove from buffer).\n */\n ack_last?: number | undefined;\n}\n\nexport function makeTelegramReadTool(opts: {\n bot: TelegramBot;\n}): Tool<TelegramReadInput> {\n return {\n name: 'telegram_read',\n description:\n 'Read recent incoming Telegram messages the bot has received, newest first. Returns messages with sender, text, and timestamp. After reading, acknowledge them with ack_last so they are cleared. When responding to a user via telegram_send, format your reply as natural prose — summarize findings, report outcomes clearly, do not paste raw data.',\n usageHint: 'telegram_read(chat_id: \"123456789\", limit: 5, ack_last: 42) — read messages, then ack the highest message_id to clear them.',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Read messages only from this chat/user.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n description: 'Max messages to return (default: 10).',\n },\n ack_last: {\n type: 'integer',\n description:\n 'After processing messages, pass the highest message_id to clear them from the buffer.',\n },\n },\n },\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n async execute(input) {\n const msgs = opts.bot.getMessages({\n chatId: input.chat_id,\n limit: input.limit ?? 10,\n });\n\n let acked = 0;\n if (input.ack_last !== undefined && input.ack_last > 0) {\n acked = opts.bot.acknowledge(input.ack_last);\n }\n\n return {\n buffer_total: opts.bot.bufferCount,\n messages: msgs.map((m) => ({\n message_id: m.messageId,\n chat_id: m.chatId,\n chat_type: m.chatType,\n from: m.userName ?? `user_${m.userId ?? 'unknown'}`,\n text: m.text,\n ts: new Date(m.timestamp).toISOString(),\n })),\n acked,\n hint: acked > 0\n ? undefined\n : 'Use ack_last with the highest message_id to clear processed messages.',\n };\n },\n };\n}\n","import type { Tool } from '@wrongstack/core';\nimport type { Logger } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\nimport { truncateForTelegram } from '../bot.js';\n\ninterface TelegramSendInput {\n /** Chat or user ID to send the message to. Falls back to config.notifyChatId when omitted. */\n chat_id?: string | number | undefined;\n /** Message text. */\n message: string;\n}\n\nexport function makeTelegramSendTool(opts: {\n bot: TelegramBot;\n /** Resolved at every execute() call so config changes take effect without restart. */\n getDefaultChatId(): string | number | undefined;\n maxMessageLength: number;\n log: Logger;\n}): Tool<TelegramSendInput> {\n return {\n name: 'telegram_send',\n description:\n 'Send a message to a Telegram chat. Write the message in natural prose — a human reads it. Summarize results, state what happened, and include only the key details. Never paste raw JSON, object dumps, or truncated tool output directly into the message field.',\n usageHint: 'telegram_send(chat_id: \"123456789\", message: \"Build completed — 12 tests passed, 0 failed. Deploying to staging now.\")',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Target chat or user ID. Uses the plugin default when omitted.',\n },\n message: {\n type: 'string',\n description:\n 'Message text in natural, human-readable prose. Summarize results, include only key details. Do NOT paste raw JSON, object dumps, or unformatted tool output. Target 1–4 lines for readability on mobile.',\n },\n },\n required: ['message'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 15_000,\n async execute(input, _ctx, _opts) {\n const chatId = input.chat_id ?? opts.getDefaultChatId();\n if (!chatId) {\n throw new Error(\n 'No chat_id provided and no default notifyChatId configured. Set notifyChatId in plugin config or pass chat_id.',\n );\n }\n\n // Truncate message to fit Telegram's 4096 char limit\n const truncated = truncateForTelegram(input.message, opts.maxMessageLength);\n\n opts.log.info(`telegram_send → chat_id=${chatId} (${truncated.length} chars)`);\n\n const res = await opts.bot.sendMessage(chatId, truncated);\n\n return {\n ok: res.ok,\n message_id: res.result?.message_id,\n chat: res.result?.chat\n ? {\n id: res.result.chat.id,\n type: res.result.chat.type,\n title: res.result.chat.title,\n }\n : undefined,\n };\n },\n };\n}\n","import { expectDefined } from '@wrongstack/core';\nimport type { Config, Plugin } from '@wrongstack/core';\nimport { TelegramBot } from './bot.js';\nimport type { TelegramIncomingMessage } from './bot.js';\nimport { truncateForTelegram } from './bot.js';\nimport { PLUGIN_NAME, readTelegramConfig, telegramConfigSchema } from './config.js';\nimport { formatDelegateCompleted, formatSessionEnded, formatToolExecuted } from './format.js';\nimport type { SessionEndedLike, ToolExecutedLike } from './format.js';\nimport { PollLock, lockPathForToken } from './poll-lock.js';\nimport { registerSlashCommands } from './slash-commands/index.js';\nimport { makeTelegramReadTool } from './tools/telegram-read.js';\nimport { makeTelegramSendTool } from './tools/telegram-send.js';\n// ---------------------------------------------------------------------------\n// Teardown state\n// ---------------------------------------------------------------------------\n\n/** Mutable runtime config — updated via api.onConfigChange so changes take\n * effect without restarting the plugin. */\ninterface RuntimeConfig {\n notifyChatId: string | number | undefined;\n notifyOnSessionEnd: boolean;\n notifyOnDelegate: boolean;\n longToolThresholdMs: number;\n maxMessageLength: number;\n}\n\nlet teardownState: {\n offs: Array<() => void>;\n toolNames: string[];\n commandNames: string[];\n bot: TelegramBot;\n runtimeCfg: RuntimeConfig;\n} | null = null;\n\n/** Read the Telegram section from a full Config object. */\nfunction telegramFromConfig(cfg: Config): {\n notifyChatId: string | number | undefined;\n notifyOnSessionEnd: boolean;\n notifyOnDelegate: boolean;\n longToolThresholdMs: number;\n maxMessageLength: number;\n} {\n const ext = (cfg.extensions as Record<string, Record<string, unknown>> | undefined)?.[PLUGIN_NAME] ?? {};\n return {\n notifyChatId:\n ext.notifyChatId !== undefined ? String(ext.notifyChatId) : undefined,\n notifyOnSessionEnd: ext.notifyOnSessionEnd === true,\n notifyOnDelegate: ext.notifyOnDelegate !== false, // default true\n longToolThresholdMs:\n typeof ext.longToolThresholdMs === 'number' ? ext.longToolThresholdMs : 30_000,\n maxMessageLength:\n typeof ext.maxMessageLength === 'number' ? ext.maxMessageLength : 4000,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Plugin\n// ---------------------------------------------------------------------------\n\nconst plugin: Plugin = {\n name: PLUGIN_NAME,\n version: '0.3.4',\n description: 'Telegram bridge — send/receive messages, get agent notifications.',\n apiVersion: '^0.1.10',\n capabilities: {\n tools: true,\n slashCommands: true,\n pipelines: [],\n },\n configSchema: telegramConfigSchema,\n defaultConfig: {\n pollIntervalSec: 2,\n notifyOnSessionEnd: false,\n longToolThresholdMs: 30_000,\n maxMessageLength: 4000,\n },\n\n async setup(api) {\n const cfg = readTelegramConfig(api);\n const log = api.log;\n\n log.info('Starting Telegram plugin...');\n\n // ---- Mutable runtime config (updated via onConfigChange) ----\n const runtimeCfg: RuntimeConfig = {\n notifyChatId: cfg.notifyChatId,\n notifyOnSessionEnd: cfg.notifyOnSessionEnd ?? false,\n notifyOnDelegate: cfg.notifyOnDelegate ?? true,\n longToolThresholdMs: cfg.longToolThresholdMs ?? 30_000,\n maxMessageLength: cfg.maxMessageLength ?? 4000,\n };\n\n // ---- Bot ----\n // Telegram allows one getUpdates consumer per token: elect a single\n // poller across wstack instances so concurrent TUI/WebUI/projects don't\n // fight over the token (HTTP 409 on every poll).\n const lock =\n cfg.singleInstanceLock === false\n ? undefined\n : new PollLock(lockPathForToken(cfg.botToken), { log });\n const bot = new TelegramBot({\n token: cfg.botToken,\n pollIntervalSec: cfg.pollIntervalSec ?? 2,\n allowedUsers: new Set((cfg.allowedUsers ?? []).map(String)),\n allowedChats: new Set((cfg.allowedChats ?? []).map(String)),\n bufferSize: 50,\n log,\n offsetStoragePath: cfg.offsetStoragePath,\n lock,\n onMessage(msg: TelegramIncomingMessage) {\n // Emit custom event so other plugins or the host can react.\n // The TUI can subscribe and surface it (future hook).\n api.emitCustom('telegram:message_received', msg);\n\n // Log it for the user in the TUI\n const who = msg.userName ?? msg.userId ?? 'unknown';\n log.info(`📨 Telegram: ${who} (chat=${msg.chatId}): ${msg.text.slice(0, 200)}`);\n },\n });\n\n // ---- Register tools ----\n const sendTool = makeTelegramSendTool({\n bot,\n getDefaultChatId: () => runtimeCfg.notifyChatId,\n maxMessageLength: runtimeCfg.maxMessageLength,\n log,\n });\n const readTool = makeTelegramReadTool({ bot });\n api.tools.register(sendTool);\n api.tools.register(readTool);\n\n // ---- Event subscriptions ----\n const offs: Array<() => void> = [];\n\n // System prompt contributor — inject unread Telegram messages\n const unregisterPrompt = api.registerSystemPromptContributor(async () => {\n const msgs = bot.getMessages({ limit: 5 });\n if (msgs.length === 0) return [];\n\n const blocks: Array<{ type: 'text'; text: string }> = [\n {\n type: 'text',\n text: [\n '## Telegram Inbox',\n `You have ${bot.bufferCount} unread Telegram message(s).`,\n 'Read them with `telegram_read` and reply with `telegram_send`.',\n '',\n 'Recent messages:',\n ...msgs.map((m) => {\n const who = m.userName ?? `user_${m.userId ?? 'unknown'}`;\n const ts = new Date(m.timestamp).toLocaleTimeString();\n return `- [${ts}] **${who}** (chat=${m.chatId}): ${m.text.slice(0, 200)}`;\n }),\n '',\n ].join('\\n'),\n },\n ];\n return blocks;\n });\n offs.push(unregisterPrompt);\n\n // Register slash commands\n const commandNames = registerSlashCommands(api, bot, cfg);\n\n // ---- Notification event handlers ----\n // Always subscribed; guard at event time against runtime flags so changes\n // take effect immediately without needing to restart the plugin.\n\n offs.push(\n api.events.on('session.ended', (event) => {\n if (!runtimeCfg.notifyOnSessionEnd || !runtimeCfg.notifyChatId) return;\n const payload: SessionEndedLike = {\n id: event.id,\n inputTokens: event.usage.input,\n outputTokens: event.usage.output,\n cacheRead: event.usage.cacheRead,\n cacheWrite: event.usage.cacheWrite,\n };\n const msg = truncateForTelegram(\n formatSessionEnded(payload),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send session end notification: ${(err as Error).message}`);\n });\n }),\n );\n\n offs.push(\n api.events.on('tool.executed', (event) => {\n if (\n !runtimeCfg.notifyChatId ||\n runtimeCfg.longToolThresholdMs <= 0 ||\n event.durationMs < runtimeCfg.longToolThresholdMs\n ) return;\n const payload: ToolExecutedLike = {\n name: event.name,\n ok: event.ok,\n durationMs: event.durationMs,\n output: event.output,\n };\n const msg = truncateForTelegram(\n formatToolExecuted(payload),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send tool notification: ${(err as Error).message}`);\n });\n }),\n );\n\n offs.push(\n api.events.on('delegate.completed', (event) => {\n if (!runtimeCfg.notifyOnDelegate || !runtimeCfg.notifyChatId) return;\n const msg = truncateForTelegram(\n formatDelegateCompleted(event),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send delegate notification: ${(err as Error).message}`);\n });\n }),\n );\n\n // ---- Live config updates ----\n // api.config is frozen at setup, but onConfigChange fires whenever the\n // ConfigStore is updated (from CLI /settings, WebUI prefSync, /telegram-settings).\n // Update the mutable runtime refs so all handlers pick up the new values\n // on the next event — no restart needed.\n const unlistenConfig = api.onConfigChange((next, _prev) => {\n const fresh = telegramFromConfig(next);\n runtimeCfg.notifyChatId = fresh.notifyChatId;\n runtimeCfg.notifyOnSessionEnd = fresh.notifyOnSessionEnd;\n runtimeCfg.notifyOnDelegate = fresh.notifyOnDelegate;\n runtimeCfg.longToolThresholdMs = fresh.longToolThresholdMs;\n runtimeCfg.maxMessageLength = fresh.maxMessageLength;\n log.debug('Telegram notification settings updated from config', {\n notifyOnSessionEnd: runtimeCfg.notifyOnSessionEnd,\n notifyOnDelegate: runtimeCfg.notifyOnDelegate,\n longToolThresholdMs: runtimeCfg.longToolThresholdMs,\n notifyChatId: runtimeCfg.notifyChatId ?? 'not set',\n });\n });\n offs.push(unlistenConfig);\n\n // ---- Start polling ----\n bot.start();\n\n teardownState = {\n offs,\n toolNames: [sendTool.name, readTool.name],\n commandNames,\n bot,\n runtimeCfg,\n };\n\n log.info('Telegram plugin ready');\n },\n\n async teardown(api) {\n const state = teardownState;\n if (!state) return;\n teardownState = null;\n\n state.bot.stop();\n for (const off of state.offs) off();\n for (const name of state.toolNames) api.tools.unregister(name);\n for (const name of state.commandNames) {\n api.slashCommands.unregister(`${PLUGIN_NAME}:${name}`);\n }\n\n api.log.info('Telegram plugin torn down');\n },\n\n async health() {\n const state = teardownState;\n if (!state?.bot) return { ok: false, message: 'Plugin not initialized' };\n const h = await state.bot.health();\n return h;\n },\n};\n\nexport default plugin;\n\n// Re-export the types consumers may want\nexport type { TelegramIncomingMessage } from './bot.js';\nexport type { TelegramPluginConfig } from './config.js';\n"]}
1
+ {"version":3,"sources":["../src/bot.ts","../src/config.ts","../src/format.ts","../src/poll-lock.ts","../src/slash-commands/index.ts","../src/tools/telegram-read.ts","../src/tools/telegram-send.ts","../src/index.ts"],"names":["readFileSync","writeFileSync","expectDefined"],"mappings":"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","file":"index.js","sourcesContent":["import type { Logger } from '@wrongstack/core';\nimport { sleep } from '@wrongstack/core/utils';\nimport type { PollLock } from './poll-lock.js';\n\n// ---------------------------------------------------------------------------\n// Redaction helpers\n// ---------------------------------------------------------------------------\n/** Redact the bot token from a URL for safe logging. */\nfunction redactToken(url: string, token: string): string {\n return url.replace(token, '[REDACTED]');\n}\n\n// ---------------------------------------------------------------------------\n// Telegram Bot API types (subset used by this plugin)\n// ---------------------------------------------------------------------------\n\ninterface TgUser {\n id: number;\n is_bot: boolean;\n first_name: string;\n username?: string | undefined;\n}\n\ninterface TgChat {\n id: number;\n type: 'private' | 'group' | 'supergroup' | 'channel';\n title?: string | undefined;\n username?: string | undefined;\n}\n\ninterface TgMessage {\n message_id: number;\n from?: TgUser | undefined;\n chat: TgChat;\n date: number;\n text?: string | undefined;\n}\n\ninterface TgUpdate {\n update_id: number;\n message?: TgMessage | undefined;\n edited_message?: TgMessage | undefined;\n}\n\ninterface TgResponse<T> {\n ok: boolean;\n result?: T | undefined;\n description?: string | undefined;\n error_code?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Incoming message shape emitted as a custom event\n// ---------------------------------------------------------------------------\n\nexport interface TelegramIncomingMessage {\n messageId: number;\n chatId: number;\n chatType: string;\n userId?: number | undefined;\n userName?: string | undefined;\n text: string;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bot options\n// ---------------------------------------------------------------------------\n\nexport interface TelegramBotOptions {\n token: string;\n pollIntervalSec: number;\n allowedUsers: Set<string>;\n allowedChats: Set<string>;\n /** Max messages to buffer for the agent to read. Default: 50. */\n bufferSize: number;\n log: Logger;\n /** Called for each incoming message that passes allowlist checks. */\n onMessage(msg: TelegramIncomingMessage): void;\n /**\n * Optional path to a file that stores the polling offset. When provided,\n * the offset is persisted on every successful poll and restored on startup,\n * preventing message replay after crashes or restarts.\n */\n offsetStoragePath?: string | undefined;\n /**\n * Optional cross-process single-poller lock. Telegram allows one\n * `getUpdates` consumer per token; when another wstack instance holds the\n * lock, this bot stands by (no polling) and takes over once the holder\n * stops or its heartbeat goes stale.\n */\n lock?: PollLock | undefined;\n /** How often a standby instance retries acquiring the lock. Default: 15s. */\n standbyRetryMs?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Bot\n// ---------------------------------------------------------------------------\n\nexport class TelegramBot {\n private readonly baseUrl: string;\n /** Base URL with token redacted, safe to use in log calls. */\n private readonly safeBaseUrl: string;\n private readonly pollIntervalMs: number;\n private readonly allowedUsers: Set<string>;\n private readonly allowedChats: Set<string>;\n private readonly log: Logger;\n private readonly onMessage: (msg: TelegramIncomingMessage) => void;\n private readonly controller = new AbortController();\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private pollActive = false;\n private offset = 0;\n /**\n * Consecutive HTTP 409 (\"another getUpdates in flight\") responses. Two\n * wstack instances polling the same bot token used to fight at full poll\n * speed forever, erroring on every cycle. After CONFLICT_BACKOFF_AFTER\n * consecutive conflicts this instance backs off to a slow poll and warns\n * once; any successful poll resets to the normal cadence.\n */\n private conflictStreak = 0;\n private static readonly CONFLICT_BACKOFF_AFTER = 3;\n private static readonly CONFLICT_POLL_MS = 60_000;\n private _startedAt: number | null = null;\n /** If set, the offset is persisted here after each successful poll. */\n private readonly offsetStoragePath?: string | undefined;\n /** Single-poller election across wstack instances sharing this token. */\n private readonly lock?: PollLock | undefined;\n private readonly standbyRetryMs: number;\n private standbyTimer: ReturnType<typeof setTimeout> | null = null;\n private standbyAnnounced = false;\n\n // Circular buffer for incoming messages\n private readonly bufferMax: number;\n private readonly buffer: TelegramIncomingMessage[] = [];\n\n constructor(opts: TelegramBotOptions) {\n this.baseUrl = `https://api.telegram.org/bot${opts.token}`;\n this.safeBaseUrl = redactToken(this.baseUrl, opts.token);\n this.pollIntervalMs = opts.pollIntervalSec * 1000;\n this.allowedUsers = opts.allowedUsers;\n this.allowedChats = opts.allowedChats;\n this.bufferMax = opts.bufferSize;\n this.log = opts.log;\n this.onMessage = opts.onMessage;\n this.offsetStoragePath = opts.offsetStoragePath;\n this.lock = opts.lock;\n this.standbyRetryMs = opts.standbyRetryMs ?? 15_000;\n if (this.lock) {\n this.lock.onLost = () => this.handleLockLost();\n }\n\n // Restore persisted offset so a crash/restart doesn't cause message replay.\n if (this.offsetStoragePath) {\n void this.loadOffset();\n }\n }\n\n // ------------------------------------------------------------------\n // Lifecycle\n // ------------------------------------------------------------------\n\n /** Start polling for updates. Idempotent. */\n start(): void {\n if (this.pollActive) return;\n this.pollActive = true;\n this._startedAt = Date.now();\n this.acquireAndPoll();\n }\n\n /** Stop polling and cancel all in-flight requests. */\n stop(): void {\n this.pollActive = false;\n this.controller.abort();\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.standbyTimer) {\n clearTimeout(this.standbyTimer);\n this.standbyTimer = null;\n }\n this.lock?.release();\n this.log.info('Telegram bot stopped');\n }\n\n /** True when the bot is started but waiting for the poll lock. */\n get standby(): boolean {\n return this.pollActive && this.lock !== undefined && !this.lock.held;\n }\n\n /**\n * Acquire the poll lock (when configured) and start the poll loop, or\n * stand by and retry until the current holder releases it.\n */\n private acquireAndPoll(): void {\n if (!this.pollActive) return;\n if (this.lock && !this.lock.tryAcquire()) {\n if (!this.standbyAnnounced) {\n this.standbyAnnounced = true;\n this.log.info(\n 'Telegram: another wstack instance is already polling this bot token — standing by; will take over when it stops.',\n );\n }\n this.standbyTimer = setTimeout(() => this.acquireAndPoll(), this.standbyRetryMs);\n this.standbyTimer.unref?.();\n return;\n }\n if (this.standbyAnnounced) {\n this.standbyAnnounced = false;\n this.log.info('Telegram: poll lock acquired — taking over polling.');\n } else {\n this.log.info(`Telegram bot polling started (${this.safeBaseUrl})`);\n }\n this.schedulePoll();\n }\n\n /** The lock was stolen while we held it — pause polling and stand by. */\n private handleLockLost(): void {\n if (!this.pollActive) return;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.log.warn('Telegram: poll lock lost to another instance — pausing polling and standing by.');\n this.standbyAnnounced = true; // acquireAndPoll already announced via this warn\n this.standbyTimer = setTimeout(() => this.acquireAndPoll(), this.standbyRetryMs);\n this.standbyTimer.unref?.();\n }\n\n get startedAt(): number | null {\n return this._startedAt;\n }\n\n get running(): boolean {\n return this.pollActive;\n }\n\n // ------------------------------------------------------------------\n // Buffer — incoming messages the agent can read\n // ------------------------------------------------------------------\n\n /** Return buffered messages, newest first. Optionally filter by chat. */\n getMessages(opts?: { chatId?: string | number | undefined; limit?: number | undefined }): TelegramIncomingMessage[] {\n let msgs = [...this.buffer].reverse();\n if (opts?.chatId) {\n const cid = String(opts.chatId);\n msgs = msgs.filter((m) => String(m.chatId) === cid);\n }\n const limit = opts?.limit ?? 20;\n return msgs.slice(0, limit);\n }\n\n /** Drop messages older than the given message ID from the buffer. */\n acknowledge(lastMessageId: number): number {\n const before = this.buffer.length;\n let i = this.buffer.length;\n while (i-- > 0) {\n const buffered = this.buffer[i];\n if (buffered && buffered.messageId <= lastMessageId) {\n this.buffer.splice(0, i + 1);\n break;\n }\n }\n return before - this.buffer.length;\n }\n\n get bufferCount(): number {\n return this.buffer.length;\n }\n\n // ------------------------------------------------------------------\n // Outgoing — send a message\n // ------------------------------------------------------------------\n\n async sendMessage(chatId: string | number, text: string): Promise<TgResponse<TgMessage>> {\n const url = `${this.baseUrl}/sendMessage`;\n const body = JSON.stringify({\n chat_id: String(chatId),\n text,\n disable_web_page_preview: true,\n });\n\n this.log.debug(`Sending Telegram message to ${chatId} (${text.length} chars)`);\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(10_000),\n });\n const data = (await res.json()) as TgResponse<TgMessage>;\n if (!data.ok) {\n throw new Error(`Telegram API error ${data.error_code}: ${data.description}`);\n }\n return data;\n } catch (err) {\n lastErr = err;\n if (attempt < 3) {\n this.log.debug(`Telegram sendMessage attempt ${attempt} failed, retrying in 1s...`);\n await sleep(1000);\n }\n }\n }\n throw lastErr;\n }\n\n // ------------------------------------------------------------------\n // Health\n // ------------------------------------------------------------------\n\n async health(): Promise<{ ok: boolean; username?: string | undefined; error?: string | undefined }> {\n try {\n const url = `${this.baseUrl}/getMe`;\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) });\n const data = (await res.json()) as TgResponse<TgUser>;\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description ?? 'Unknown error' };\n }\n return { ok: true, username: data.result.username };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n }\n\n // ------------------------------------------------------------------\n // Polling\n // ------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (!this.pollActive) return;\n // Lost the poll lock mid-flight — the standby retry loop owns recovery.\n if (this.lock && !this.lock.held) return;\n const delay =\n this.conflictStreak >= TelegramBot.CONFLICT_BACKOFF_AFTER\n ? TelegramBot.CONFLICT_POLL_MS\n : this.pollIntervalMs;\n this.pollTimer = setTimeout(() => {\n void this.poll().finally(() => this.schedulePoll());\n }, delay);\n }\n\n private async poll(): Promise<void> {\n try {\n const url = `${this.baseUrl}/getUpdates?offset=${this.offset}&timeout=10`;\n const res = await fetch(url, { signal: this.controller.signal });\n const data = (await res.json()) as TgResponse<TgUpdate[]>;\n\n if (!data.ok) {\n if (data.error_code === 409) {\n this.conflictStreak++;\n if (this.conflictStreak === TelegramBot.CONFLICT_BACKOFF_AFTER) {\n this.log.warn(\n this.lock\n ? 'Telegram: another consumer outside this machine is polling this bot token (HTTP 409) — backing off to 60s polls. Check other machines/bots using this token, or a registered webhook (deleteWebhook).'\n : 'Telegram: another instance is polling this bot token (HTTP 409) — backing off to 60s polls until it stops.',\n );\n }\n }\n this.log.debug(`Telegram getUpdates failed: ${data.description}`);\n return;\n }\n this.conflictStreak = 0;\n\n const updates = data.result ?? [];\n for (const upd of updates) {\n this.offset = upd.update_id + 1;\n const raw = upd.message ?? upd.edited_message;\n if (!raw?.text) continue;\n const msg = { ...raw, text: raw.text };\n this.processMessage(msg);\n }\n\n // Persist offset after each successful poll to prevent message replay\n // after crashes or restarts.\n if (this.offsetStoragePath && this.offset > 0) {\n void this.saveOffset();\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n this.log.debug(`Telegram poll error: ${(err as Error).message}`);\n }\n }\n\n private processMessage(msg: TgMessage & { text: string }): void {\n const chatId = String(msg.chat.id);\n const userId = msg.from ? String(msg.from.id) : undefined;\n\n // Allowlist checks\n if (this.allowedUsers.size > 0 && userId && !this.allowedUsers.has(userId)) {\n this.log.debug(`Ignoring message from user ${userId} (not in allowedUsers)`);\n void this.sendMessage(chatId, '⛔ You are not authorized to interact with this bot.');\n return;\n }\n if (this.allowedChats.size > 0 && !this.allowedChats.has(chatId)) {\n this.log.debug(`Ignoring message from chat ${chatId} (not in allowedChats)`);\n return;\n }\n\n const incoming: TelegramIncomingMessage = {\n messageId: msg.message_id,\n chatId: msg.chat.id,\n chatType: msg.chat.type,\n userId: msg.from?.id,\n userName: msg.from?.username ?? msg.from?.first_name,\n text: msg.text,\n timestamp: msg.date * 1000,\n };\n\n // Push to circular buffer\n this.buffer.push(incoming);\n while (this.buffer.length > this.bufferMax) this.buffer.shift();\n\n this.onMessage(incoming);\n }\n\n private async loadOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { readFileSync } = await import('node:fs');\n const raw = readFileSync(this.offsetStoragePath, 'utf8').trim();\n const n = Number.parseInt(raw, 10);\n if (Number.isFinite(n) && n >= 0) {\n this.offset = n;\n this.log.debug(`Telegram polling offset restored: ${this.offset}`);\n }\n } catch {\n // File doesn't exist yet — start from 0, which is correct.\n }\n }\n\n private async saveOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { writeFileSync } = await import('node:fs');\n // Write atomically so a crash mid-write can't leave a corrupt file.\n writeFileSync(this.offsetStoragePath, String(this.offset), 'utf8');\n } catch (err) {\n this.log.debug(`Failed to persist Telegram offset: ${err}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Truncate text to fit Telegram's 4096-char message limit.\n * Preserves semantic boundaries in this priority order:\n * 1. Paragraph break (double newline)\n * 2. Sentence break (. ! ? followed by space/newline)\n * 3. Word break (space)\n * 4. Hard cut with ellipsis\n *\n * When a clean boundary is found, appends \"…\" to signal intentional truncation.\n */\nexport function truncateForTelegram(text: string, maxLen = 4000): string {\n if (text.length <= maxLen) return text;\n\n // Reserve room for truncation suffix\n const cutoff = maxLen - 30;\n if (cutoff <= 0) return `${text.slice(0, maxLen - 1)}…`;\n\n const searchEnd = Math.min(text.length, maxLen);\n\n // 1. Paragraph boundary (double newline)\n const paraIdx = text.lastIndexOf('\\n\\n', searchEnd);\n if (paraIdx > cutoff) {\n return `${text.slice(0, paraIdx)}\\n\\n…`;\n }\n\n // 2. Single newline boundary\n const nlIdx = text.lastIndexOf('\\n', searchEnd);\n if (nlIdx > cutoff) {\n return `${text.slice(0, nlIdx)}\\n…`;\n }\n\n // 3. Sentence boundary (. ! ? followed by space or newline)\n const sentenceRe = /[.!?](?=\\s)/g;\n let match: RegExpExecArray | null;\n let sentenceIdx = -1;\n match = sentenceRe.exec(text);\n while (match !== null) {\n if (match.index >= searchEnd) break;\n if (match.index > cutoff) sentenceIdx = match.index + 1;\n match = sentenceRe.exec(text);\n }\n if (sentenceIdx > cutoff) {\n return `${text.slice(0, sentenceIdx)}…`;\n }\n\n // 4. Word boundary (space)\n const spaceIdx = text.lastIndexOf(' ', searchEnd);\n if (spaceIdx > cutoff) {\n return `${text.slice(0, spaceIdx)} …`;\n }\n\n // 5. Hard cut\n return `${text.slice(0, maxLen - 20)}…[+${text.length - maxLen + 20} chars]`;\n}\n\n/**\n * Escape HTML special chars for Telegram's HTML parse mode.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","import type { PluginAPI } from '@wrongstack/core';\r\n\r\nexport const PLUGIN_NAME = 'telegram';\r\n\r\nexport interface TelegramPluginConfig {\r\n /** Telegram Bot API token (from @BotFather). */\r\n botToken: string;\r\n /**\r\n * Default chat ID for outgoing notifications.\r\n * The agent's `telegram_send` tool can override per-call.\r\n */\r\n notifyChatId?: string | number | undefined;\r\n /**\r\n * List of user/chat IDs allowed to interact with the bot.\r\n * Empty = allow all. Recommended to set in production.\r\n */\r\n allowedUsers?: Array<string | number> | undefined;\r\n /**\r\n * List of group/chat IDs the bot is allowed to read from.\r\n * Empty = allow all. Narrow this to prevent noise.\r\n */\r\n allowedChats?: Array<string | number> | undefined;\r\n /** Polling interval in seconds (default: 2). */\r\n pollIntervalSec?: number | undefined;\r\n /** Notify on Telegram when a session ends. */\r\n notifyOnSessionEnd?: boolean | undefined;\r\n /** Notify when a tool runs longer than this threshold (ms). Set 0 to disable. */\r\n longToolThresholdMs?: number | undefined;\r\n /** Notify (humanized) when a `delegate` subagent finishes. Default: true. */\r\n notifyOnDelegate?: boolean | undefined;\r\n /** Maximum message length for Telegram (Telegram caps at 4096). */\r\n maxMessageLength?: number | undefined;\r\n /**\r\n * Path to a file that stores the Telegram polling offset. When set,\r\n * the offset is persisted on every successful poll and restored on startup,\r\n * preventing message replay after crashes or restarts.\r\n * The directory must already exist and be writable.\r\n */\r\n offsetStoragePath?: string | undefined;\r\n /**\r\n * Elect a single poller per bot token across wstack instances (default:\r\n * true). Telegram allows one `getUpdates` consumer per token; without this,\r\n * two instances sharing a token fight and get HTTP 409 on every poll.\r\n * Extra instances stand by and take over when the active poller stops.\r\n * Set false only if this is guaranteed to be the sole consumer.\r\n */\r\n singleInstanceLock?: boolean | undefined;\r\n}\r\n\r\nexport const DEFAULT_CONFIG: Required<Omit<TelegramPluginConfig, 'botToken' | 'notifyChatId' | 'offsetStoragePath'>> = {\r\n allowedUsers: [],\r\n allowedChats: [],\r\n pollIntervalSec: 2,\r\n notifyOnSessionEnd: false,\r\n longToolThresholdMs: 30_000,\r\n notifyOnDelegate: true,\r\n maxMessageLength: 4000,\r\n singleInstanceLock: true,\r\n};\r\n\r\nexport const telegramConfigSchema = {\r\n type: 'object',\r\n properties: {\r\n botToken: { type: 'string', description: 'Telegram Bot API token from @BotFather' },\r\n notifyChatId: {\r\n oneOf: [{ type: 'string' }, { type: 'integer' }],\r\n description: 'Default chat ID for outgoing notifications',\r\n },\r\n allowedUsers: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'User IDs allowed to interact with the bot',\r\n },\r\n allowedChats: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'Chat IDs the bot is allowed to read from',\r\n },\r\n pollIntervalSec: {\r\n type: 'integer',\r\n minimum: 1,\r\n maximum: 60,\r\n description: 'Polling interval in seconds',\r\n },\r\n notifyOnSessionEnd: { type: 'boolean' },\r\n longToolThresholdMs: { type: 'integer', minimum: 0 },\r\n notifyOnDelegate: { type: 'boolean' },\r\n maxMessageLength: { type: 'integer', minimum: 100, maximum: 4096 },\r\n singleInstanceLock: {\r\n type: 'boolean',\r\n description: 'Elect a single getUpdates poller per bot token across wstack instances (default true)',\r\n },\r\n },\r\n required: ['botToken'],\r\n};\r\n\r\nexport function readTelegramConfig(\r\n api: Pick<PluginAPI, 'config'>,\r\n): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> &\r\n Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'> {\r\n const config = api.config as never as Record<string, unknown>;\r\n const extensions = config.extensions as Record<string, unknown> | undefined;\r\n const pluginEntries = config.plugins;\r\n const legacyPlugins = pluginEntries as Record<string, unknown> | undefined;\r\n const legacyOpts =\r\n legacyPlugins && !Array.isArray(legacyPlugins) ? legacyPlugins[PLUGIN_NAME] : undefined;\r\n const entryOpts = pluginOptionsFromEntries(pluginEntries);\r\n const opts = {\r\n ...((legacyOpts ?? entryOpts) as TelegramPluginConfig),\r\n ...((extensions?.[PLUGIN_NAME] ?? {}) as TelegramPluginConfig),\r\n };\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...opts,\r\n };\r\n}\r\n\r\nfunction pluginOptionsFromEntries(entries: unknown): TelegramPluginConfig | undefined {\r\n if (!Array.isArray(entries)) return undefined;\r\n const found = entries.find(\r\n (entry) =>\r\n typeof entry === 'object' &&\r\n entry !== null &&\r\n 'name' in entry &&\r\n ((entry as { name?: unknown | undefined }).name === '@wrongstack/telegram' ||\r\n (entry as { name?: unknown | undefined }).name === PLUGIN_NAME),\r\n ) as { name?: unknown | undefined; options?: unknown | undefined } | undefined;\r\n return found?.options && typeof found.options === 'object'\r\n ? (found.options as TelegramPluginConfig)\r\n : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\n// Humanizers for agent events forwarded to Telegram.\n//\n// The host emits rich structured events; this module turns them into short,\n// readable chat messages. Kept pure (no bot / IO) so it's trivially testable.\n//\n// Design rules for Telegram readability:\n// - Start with an emoji status icon so the outcome is scannable.\n// - Lead with the *headline* (what happened), then context, then stats.\n// - Never embed raw JSON. Never concatenate object dumps.\n// - Keep messages under 2000 chars so they fit one mobile screen.\n// - Use emoji sparingly — status markers only, no decoration.\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Payload types (subsets of core event shapes)\n// ---------------------------------------------------------------------------\n\n/** Subset of the core `delegate.completed` event payload we render. */\nexport interface DelegateCompletedLike {\n target: string;\n task: string;\n ok: boolean;\n status?: string | undefined;\n summary: string;\n durationMs: number;\n iterations: number;\n toolCalls: number;\n costUsd?: number | undefined;\n subagentId?: string | undefined;\n}\n\n/** Subset of core `tool.executed` event payload. */\nexport interface ToolExecutedLike {\n name: string;\n ok: boolean;\n durationMs: number;\n /** Raw tool output — only the first 300 chars are rendered. */\n output?: string | undefined;\n}\n\n/** Subset of core `session.ended` event payload (from Usage). */\nexport interface SessionEndedLike {\n id: string;\n inputTokens: number;\n outputTokens: number;\n cacheRead?: number | undefined;\n cacheWrite?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\n/** Compact human duration: `42s`, `3m`, `1.5h`. */\nexport function fmtDuration(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/**\n * Format a numeric count of tokens for human readability.\n * Uses comma-separated thousands: 1,234, 56,789.\n */\nexport function fmtTokens(n: number): string {\n return n.toLocaleString('en-US');\n}\n\n/**\n * Try to render a tool's output as a short human-readable snippet.\n * Strips JSON braces/quoting, limits to ~300 chars, preserves first/last lines.\n */\nexport function fmtToolOutput(raw: string | undefined): string {\n if (!raw) return '(no output)';\n const cleaned = raw\n .replace(/^[{[]\\s*/, '') // strip leading JSON opening\n .replace(/\\s*[}\\]]$/, '') // strip trailing JSON closing\n .replace(/\"([^\"]+)\":/g, '$1: ') // unquote JSON keys, add space for readability\n .replace(/\\\\n/g, '\\n') // expand escaped newlines\n .replace(/\\\\\"/g, '\"') // expand escaped quotes\n .trim()\n || raw;\n\n // Try to split into short lines; show the first 3 meaningful ones.\n const lines = cleaned.split('\\n').filter((l) => l.trim().length > 0);\n let preview = lines.slice(0, 3).join('\\n');\n if (lines.length > 3) preview += `\\n… +${lines.length - 3} more lines`;\n if (preview.length > 300) preview = `${preview.slice(0, 297)}…`;\n return preview;\n}\n\n// ---------------------------------------------------------------------------\n// Event → message formatters\n// ---------------------------------------------------------------------------\n\n/**\n * Render a finished delegation as a readable Telegram message.\n *\n * Example:\n * ✅ Delegate → bug-hunter · success\n * Found 3 null-deref risks in auth.ts and patched the worst one…\n * ⏱ 3m · 4 iter · 37 tools · 💲0.0820\n */\nexport function formatDelegateCompleted(e: DelegateCompletedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const status = e.status ?? (e.ok ? 'success' : 'failed');\n const task = e.task.length > 160 ? `${e.task.slice(0, 159)}…` : e.task;\n\n // Prefer the host's one-line summary; fall back to echoing the task when a\n // failure produced no summary.\n const body = e.summary?.trim() || `(no summary) — ${task}`;\n\n const stats = [\n `⏱ ${fmtDuration(e.durationMs)}`,\n `${e.iterations} iter`,\n `${e.toolCalls} tools`,\n ];\n if (typeof e.costUsd === 'number' && e.costUsd > 0) {\n stats.push(`💲${e.costUsd.toFixed(4)}`);\n }\n\n return [`${icon} Delegate → ${e.target} · ${status}`, body, stats.join(' · ')].join('\\n');\n}\n\n/**\n * Render a long-running tool execution notification.\n *\n * Example:\n * ✅ bash completed in 45.2s\n * pnpm test — 12 suites, 47 tests passed\n * …\n */\nexport function formatToolExecuted(e: ToolExecutedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const sec = (e.durationMs / 1000).toFixed(1);\n const headline = `${icon} ${e.name} completed in ${sec}s`;\n\n const output = fmtToolOutput(e.output);\n // Only include output if it's short enough to be readable on mobile\n if (output === '(no output)') return headline;\n return `${headline}\\n${output}`;\n}\n\n/**\n * Render a session-end notification.\n *\n * Example:\n * 🏁 Session sess_abcd ended\n * ⬇ 8,234 in · ⬆ 3,456 out · 11,690 total\n * Cache: 1,200 read · 800 written\n */\nexport function formatSessionEnded(e: SessionEndedLike): string {\n const id = e.id.length > 8 ? e.id.slice(0, 8) : e.id;\n const total = e.inputTokens + e.outputTokens;\n\n const lines = [\n `🏁 Session ${id} ended`,\n `⬇ ${fmtTokens(e.inputTokens)} in · ⬆ ${fmtTokens(e.outputTokens)} out · ${fmtTokens(total)} total`,\n ];\n\n // Show cache stats when available\n if (e.cacheRead || e.cacheWrite) {\n const parts: string[] = [];\n if (e.cacheRead && e.cacheRead > 0) parts.push(`${fmtTokens(e.cacheRead)} cache read`);\n if (e.cacheWrite && e.cacheWrite > 0) parts.push(`${fmtTokens(e.cacheWrite)} cache written`);\n if (parts.length > 0) lines.push(`📦 ${parts.join(' · ')}`);\n }\n\n return lines.join('\\n');\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { Logger } from '@wrongstack/core';\nimport { wstackGlobalRoot } from '@wrongstack/core/utils';\n\n/**\n * Cross-process single-poller lock for a Telegram bot token.\n *\n * Telegram allows exactly one `getUpdates` consumer per token; two wstack\n * instances (TUI + WebUI, or two projects) polling the same token fight each\n * other and every cycle returns HTTP 409. This lock elects one poller: the\n * holder writes a heartbeat to a lock file under `~/.wrongstack/telegram/`,\n * other instances stand by and take over when the heartbeat goes stale or\n * the file disappears.\n */\n\ninterface LockFilePayload {\n /** Unique per PollLock instance — `pid` alone can't distinguish two locks in one process. */\n id: string;\n pid: number;\n acquiredAt: number;\n heartbeatAt: number;\n}\n\nexport interface PollLockOptions {\n log?: Logger | undefined;\n /** How often the holder refreshes its heartbeat. Default: 15s. */\n heartbeatMs?: number | undefined;\n /** A lock whose heartbeat is older than this is considered stale. Default: 45s. */\n staleMs?: number | undefined;\n}\n\n/** Lock file path for a bot token. The token itself never appears in the path. */\nexport function lockPathForToken(token: string, globalRoot = wstackGlobalRoot()): string {\n const hash = createHash('sha256').update(token).digest('hex').slice(0, 12);\n return join(globalRoot, 'telegram', `poll-${hash}.lock`);\n}\n\nexport class PollLock {\n private readonly id = `${process.pid}:${randomUUID()}`;\n private readonly heartbeatMs: number;\n private readonly staleMs: number;\n private readonly log?: Logger | undefined;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private _held = false;\n\n /** Invoked when the lock is stolen by another instance while held. */\n onLost?: (() => void) | undefined;\n\n constructor(\n readonly lockPath: string,\n opts?: PollLockOptions,\n ) {\n this.heartbeatMs = opts?.heartbeatMs ?? 15_000;\n this.staleMs = opts?.staleMs ?? 45_000;\n this.log = opts?.log;\n }\n\n get held(): boolean {\n return this._held;\n }\n\n /**\n * Try to acquire the lock. Returns true when this instance is now (or was\n * already) the holder. Safe to call repeatedly from a standby retry loop.\n */\n tryAcquire(): boolean {\n if (this._held) return true;\n\n const existing = this.readLock();\n if (existing && !this.isStale(existing)) return false;\n\n try {\n mkdirSync(dirname(this.lockPath), { recursive: true });\n // Remove any stale or corrupt file first, then create exclusively: when\n // two standby instances race for a stale lock, `wx` makes exactly one win.\n try {\n unlinkSync(this.lockPath);\n } catch {\n // Nothing to remove, or a competing instance already removed it.\n }\n const now = Date.now();\n const payload: LockFilePayload = {\n id: this.id,\n pid: process.pid,\n acquiredAt: now,\n heartbeatAt: now,\n };\n writeFileSync(this.lockPath, JSON.stringify(payload), { flag: 'wx' });\n } catch {\n return false; // Lost the race or the directory is unwritable.\n }\n\n this._held = true;\n this.startHeartbeat();\n return true;\n }\n\n /** Release the lock and stop the heartbeat. Idempotent. */\n release(): void {\n this.stopHeartbeat();\n if (!this._held) return;\n this._held = false;\n try {\n if (this.readLock()?.id === this.id) unlinkSync(this.lockPath);\n } catch {\n // Best effort — a stale file is reclaimed via the staleness check anyway.\n }\n }\n\n // ------------------------------------------------------------------\n // Internals\n // ------------------------------------------------------------------\n\n private startHeartbeat(): void {\n this.stopHeartbeat();\n this.heartbeatTimer = setInterval(() => this.heartbeatTick(), this.heartbeatMs);\n this.heartbeatTimer.unref?.();\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private heartbeatTick(): void {\n const current = this.readLock();\n if (!current || current.id !== this.id) {\n // Another instance stole the lock (e.g. this process was suspended past\n // the staleness window). Stop claiming it and notify the owner.\n this._held = false;\n this.stopHeartbeat();\n this.log?.warn('Telegram: poll lock was taken over by another instance.');\n this.onLost?.();\n return;\n }\n try {\n const payload: LockFilePayload = { ...current, heartbeatAt: Date.now() };\n // Write via temp + rename so a reader never sees a half-written file.\n const tmp = `${this.lockPath}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(payload));\n renameSync(tmp, this.lockPath);\n } catch (err) {\n this.log?.debug(`Telegram: poll lock heartbeat write failed: ${err}`);\n }\n }\n\n private readLock(): LockFilePayload | null {\n try {\n const raw = readFileSync(this.lockPath, 'utf8');\n const parsed = JSON.parse(raw) as LockFilePayload;\n if (typeof parsed.id !== 'string' || typeof parsed.pid !== 'number') return null;\n return parsed;\n } catch {\n return null; // Missing or corrupt — treated as stale/absent.\n }\n }\n\n private isStale(payload: LockFilePayload): boolean {\n if (Date.now() - payload.heartbeatAt > this.staleMs) return true;\n return !this.isPidAlive(payload.pid);\n }\n\n private isPidAlive(pid: number): boolean {\n if (pid === process.pid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means the process exists but belongs to another user.\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n }\n}\n","import { expectDefined } from '@wrongstack/core';\nimport type { PluginAPI, SlashCommand } from '@wrongstack/core';\r\nimport type { TelegramBot } from '../bot.js';\r\nimport type { TelegramPluginConfig } from '../config.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:status\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgStatusCommand(bot: TelegramBot, cfg: TelegramPluginConfig): SlashCommand {\r\n return {\r\n name: 'status',\r\n aliases: ['tgstat', 'tgs'],\r\n description: 'Show Telegram bot connection status and config',\r\n help: `Usage: /telegram:status\r\n\r\nShows whether the bot is connected, its username, polling interval,\r\nallowlist status, and notification settings.`,\r\n async run(_args, _ctx) {\r\n const health = await bot.health();\r\n const lines = [\r\n '═══ Telegram Plugin Status ═══',\r\n '',\r\n `Bot: ${health.ok ? `✅ @${health.username ?? 'connected'}` : `❌ ${health.error ?? 'offline'}`}`,\r\n `Running: ${bot.running ? 'yes' : 'no'}`,\r\n `Started: ${bot.startedAt ? new Date(bot.startedAt).toLocaleTimeString() : 'N/A'}`,\r\n `Poll: every ${cfg.pollIntervalSec ?? 2}s`,\r\n `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers?.length} users` : 'everyone (users)'} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats?.length} chats` : 'everyone (chats)'}`,\r\n `Notify: sessionEnd=${cfg.notifyOnSessionEnd ?? false}, longTool=${cfg.longToolThresholdMs ? `${cfg.longToolThresholdMs}ms` : 'off'}`,\r\n ];\r\n\r\n return { message: lines.join('\\n') };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:send\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgSendCommand(\r\n bot: TelegramBot,\r\n defaultChatId: string | number | undefined,\r\n): SlashCommand {\r\n return {\r\n name: 'send',\r\n description: 'Send a message to a Telegram chat',\r\n help: `Usage: /telegram:send [chat_id] <message>\r\n\r\nSend a message to a Telegram chat.\r\n- First argument (optional): chat or user ID. Uses notifyChatId from config when omitted.\r\n- Everything else: the message text.\r\n\r\nExamples:\r\n /telegram:send 123456789 Build completed successfully ✓\r\n /telegram:send Deploy finished — check staging`,\r\n async run(args, _ctx) {\r\n if (!args.trim()) {\r\n return { message: 'Usage: /telegram:send [chat_id] <message>' };\r\n }\r\n\r\n let chatId: string | number;\r\n let text: string;\r\n\r\n // First token might be a numeric chat_id\r\n const parts = args.trim().split(/\\s+/);\r\n const maybeId = parts[0];\r\n if (/^\\d+$/.test(expectDefined(maybeId)) && parts.length > 1) {\r\n chatId = expectDefined(maybeId);\r\n text = parts.slice(1).join(' ');\r\n } else if (defaultChatId) {\r\n chatId = defaultChatId;\r\n text = args.trim();\r\n } else {\r\n return {\r\n message:\r\n 'No chat_id provided and no default notifyChatId configured.\\nUsage: /telegram:send <chat_id> <message>',\r\n };\r\n }\r\n\r\n try {\r\n const res = await bot.sendMessage(chatId, text);\r\n return {\r\n message: `✅ Message sent to ${chatId} (msg_id=${res.result?.message_id ?? '?'})`,\r\n };\r\n } catch (err) {\r\n return { message: `❌ Failed to send: ${(err as Error).message}` };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:chatid\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgChatIdCommand(defaultChatId?: string | number): SlashCommand {\r\n const chatIdStr = defaultChatId ? String(defaultChatId) : null;\r\n return {\r\n name: 'chatid',\r\n description: 'Show the configured default chat ID',\r\n help: `Usage: /telegram:chatid\r\n\r\nShows the current default notifyChatId used for notifications\r\nand the \\`telegram_send\\` tool when no chat_id is specified.`,\r\n async run(_args, _ctx) {\r\n if (chatIdStr) {\r\n return { message: `Configured notifyChatId: ${chatIdStr}` };\r\n }\r\n return { message: 'No notifyChatId configured. Set it in the plugin config or pass chat_id explicitly to telegram_send.' };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Register all\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function registerSlashCommands(\r\n api: PluginAPI,\r\n bot: TelegramBot,\r\n cfg: TelegramPluginConfig,\r\n): string[] {\r\n const cmds = [\r\n tgStatusCommand(bot, cfg),\r\n tgSendCommand(bot, cfg.notifyChatId),\r\n tgChatIdCommand(cfg.notifyChatId),\r\n ];\r\n for (const cmd of cmds) api.slashCommands.register(cmd);\r\n return cmds.map((c) => c.name);\r\n}\r\n","import type { Tool } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\n\ninterface TelegramReadInput {\n /** Filter to messages from a specific chat/user ID. Omit to see all chats. */\n chat_id?: string | number | undefined;\n /** Max messages to return (default: 10, max: 50). */\n limit?: number | undefined;\n /**\n * If a message_id is provided, acknowledge all messages up to and\n * including this ID (mark them as processed / remove from buffer).\n */\n ack_last?: number | undefined;\n}\n\nexport function makeTelegramReadTool(opts: {\n bot: TelegramBot;\n}): Tool<TelegramReadInput> {\n return {\n name: 'telegram_read',\n description:\n 'Read recent incoming Telegram messages the bot has received, newest first. Returns messages with sender, text, and timestamp. After reading, acknowledge them with ack_last so they are cleared. When responding to a user via telegram_send, format your reply as natural prose — summarize findings, report outcomes clearly, do not paste raw data.',\n usageHint: 'telegram_read(chat_id: \"123456789\", limit: 5, ack_last: 42) — read messages, then ack the highest message_id to clear them.',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Read messages only from this chat/user.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n description: 'Max messages to return (default: 10).',\n },\n ack_last: {\n type: 'integer',\n description:\n 'After processing messages, pass the highest message_id to clear them from the buffer.',\n },\n },\n },\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n async execute(input) {\n const msgs = opts.bot.getMessages({\n chatId: input.chat_id,\n limit: input.limit ?? 10,\n });\n\n let acked = 0;\n if (input.ack_last !== undefined && input.ack_last > 0) {\n acked = opts.bot.acknowledge(input.ack_last);\n }\n\n return {\n buffer_total: opts.bot.bufferCount,\n messages: msgs.map((m) => ({\n message_id: m.messageId,\n chat_id: m.chatId,\n chat_type: m.chatType,\n from: m.userName ?? `user_${m.userId ?? 'unknown'}`,\n text: m.text,\n ts: new Date(m.timestamp).toISOString(),\n })),\n acked,\n hint: acked > 0\n ? undefined\n : 'Use ack_last with the highest message_id to clear processed messages.',\n };\n },\n };\n}\n","import type { Tool } from '@wrongstack/core';\nimport type { Logger } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\nimport { truncateForTelegram } from '../bot.js';\n\ninterface TelegramSendInput {\n /** Chat or user ID to send the message to. Falls back to config.notifyChatId when omitted. */\n chat_id?: string | number | undefined;\n /** Message text. */\n message: string;\n}\n\nexport function makeTelegramSendTool(opts: {\n bot: TelegramBot;\n /** Resolved at every execute() call so config changes take effect without restart. */\n getDefaultChatId(): string | number | undefined;\n maxMessageLength: number;\n log: Logger;\n}): Tool<TelegramSendInput> {\n return {\n name: 'telegram_send',\n description:\n 'Send a message to a Telegram chat. Write the message in natural prose — a human reads it. Summarize results, state what happened, and include only the key details. Never paste raw JSON, object dumps, or truncated tool output directly into the message field.',\n usageHint: 'telegram_send(chat_id: \"123456789\", message: \"Build completed — 12 tests passed, 0 failed. Deploying to staging now.\")',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Target chat or user ID. Uses the plugin default when omitted.',\n },\n message: {\n type: 'string',\n description:\n 'Message text in natural, human-readable prose. Summarize results, include only key details. Do NOT paste raw JSON, object dumps, or unformatted tool output. Target 1–4 lines for readability on mobile.',\n },\n },\n required: ['message'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 15_000,\n async execute(input, _ctx, _opts) {\n const chatId = input.chat_id ?? opts.getDefaultChatId();\n if (!chatId) {\n throw new Error(\n 'No chat_id provided and no default notifyChatId configured. Set notifyChatId in plugin config or pass chat_id.',\n );\n }\n\n // Truncate message to fit Telegram's 4096 char limit\n const truncated = truncateForTelegram(input.message, opts.maxMessageLength);\n\n opts.log.info(`telegram_send → chat_id=${chatId} (${truncated.length} chars)`);\n\n const res = await opts.bot.sendMessage(chatId, truncated);\n\n return {\n ok: res.ok,\n message_id: res.result?.message_id,\n chat: res.result?.chat\n ? {\n id: res.result.chat.id,\n type: res.result.chat.type,\n title: res.result.chat.title,\n }\n : undefined,\n };\n },\n };\n}\n","import { expectDefined } from '@wrongstack/core';\nimport type { Config, Plugin } from '@wrongstack/core';\nimport { TelegramBot } from './bot.js';\nimport type { TelegramIncomingMessage } from './bot.js';\nimport { truncateForTelegram } from './bot.js';\nimport { PLUGIN_NAME, readTelegramConfig, telegramConfigSchema } from './config.js';\nimport { formatDelegateCompleted, formatSessionEnded, formatToolExecuted } from './format.js';\nimport type { SessionEndedLike, ToolExecutedLike } from './format.js';\nimport { PollLock, lockPathForToken } from './poll-lock.js';\nimport { registerSlashCommands } from './slash-commands/index.js';\nimport { makeTelegramReadTool } from './tools/telegram-read.js';\nimport { makeTelegramSendTool } from './tools/telegram-send.js';\n// ---------------------------------------------------------------------------\n// Teardown state\n// ---------------------------------------------------------------------------\n\n/** Mutable runtime config — updated via api.onConfigChange so changes take\n * effect without restarting the plugin. */\ninterface RuntimeConfig {\n notifyChatId: string | number | undefined;\n notifyOnSessionEnd: boolean;\n notifyOnDelegate: boolean;\n longToolThresholdMs: number;\n maxMessageLength: number;\n}\n\nlet teardownState: {\n offs: Array<() => void>;\n toolNames: string[];\n commandNames: string[];\n bot: TelegramBot;\n runtimeCfg: RuntimeConfig;\n} | null = null;\n\n/** Read the Telegram section from a full Config object. */\nfunction telegramFromConfig(cfg: Config): {\n notifyChatId: string | number | undefined;\n notifyOnSessionEnd: boolean;\n notifyOnDelegate: boolean;\n longToolThresholdMs: number;\n maxMessageLength: number;\n} {\n const ext = (cfg.extensions as Record<string, Record<string, unknown>> | undefined)?.[PLUGIN_NAME] ?? {};\n return {\n notifyChatId:\n ext.notifyChatId !== undefined ? String(ext.notifyChatId) : undefined,\n notifyOnSessionEnd: ext.notifyOnSessionEnd === true,\n notifyOnDelegate: ext.notifyOnDelegate !== false, // default true\n longToolThresholdMs:\n typeof ext.longToolThresholdMs === 'number' ? ext.longToolThresholdMs : 30_000,\n maxMessageLength:\n typeof ext.maxMessageLength === 'number' ? ext.maxMessageLength : 4000,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Plugin\n// ---------------------------------------------------------------------------\n\nconst plugin: Plugin = {\n name: PLUGIN_NAME,\n version: '0.3.4',\n description: 'Telegram bridge — send/receive messages, get agent notifications.',\n apiVersion: '^0.1.10',\n capabilities: {\n tools: true,\n slashCommands: true,\n pipelines: [],\n },\n configSchema: telegramConfigSchema,\n defaultConfig: {\n pollIntervalSec: 2,\n notifyOnSessionEnd: false,\n longToolThresholdMs: 30_000,\n maxMessageLength: 4000,\n },\n\n async setup(api) {\n const cfg = readTelegramConfig(api);\n const log = api.log;\n\n log.info('Starting Telegram plugin...');\n\n // ---- Mutable runtime config (updated via onConfigChange) ----\n const runtimeCfg: RuntimeConfig = {\n notifyChatId: cfg.notifyChatId,\n notifyOnSessionEnd: cfg.notifyOnSessionEnd ?? false,\n notifyOnDelegate: cfg.notifyOnDelegate ?? true,\n longToolThresholdMs: cfg.longToolThresholdMs ?? 30_000,\n maxMessageLength: cfg.maxMessageLength ?? 4000,\n };\n\n // ---- Bot ----\n // Telegram allows one getUpdates consumer per token: elect a single\n // poller across wstack instances so concurrent TUI/WebUI/projects don't\n // fight over the token (HTTP 409 on every poll).\n const lock =\n cfg.singleInstanceLock === false\n ? undefined\n : new PollLock(lockPathForToken(cfg.botToken), { log });\n const bot = new TelegramBot({\n token: cfg.botToken,\n pollIntervalSec: cfg.pollIntervalSec ?? 2,\n allowedUsers: new Set((cfg.allowedUsers ?? []).map(String)),\n allowedChats: new Set((cfg.allowedChats ?? []).map(String)),\n bufferSize: 50,\n log,\n offsetStoragePath: cfg.offsetStoragePath,\n lock,\n onMessage(msg: TelegramIncomingMessage) {\n // Emit custom event so other plugins or the host can react.\n // The TUI can subscribe and surface it (future hook).\n api.emitCustom('telegram:message_received', msg);\n\n // Log it for the user in the TUI\n const who = msg.userName ?? msg.userId ?? 'unknown';\n log.info(`📨 Telegram: ${who} (chat=${msg.chatId}): ${msg.text.slice(0, 200)}`);\n },\n });\n\n // ---- Register tools ----\n const sendTool = makeTelegramSendTool({\n bot,\n getDefaultChatId: () => runtimeCfg.notifyChatId,\n maxMessageLength: runtimeCfg.maxMessageLength,\n log,\n });\n const readTool = makeTelegramReadTool({ bot });\n api.tools.register(sendTool);\n api.tools.register(readTool);\n\n // ---- Event subscriptions ----\n const offs: Array<() => void> = [];\n\n // System prompt contributor — inject unread Telegram messages\n const unregisterPrompt = api.registerSystemPromptContributor(async () => {\n const msgs = bot.getMessages({ limit: 5 });\n if (msgs.length === 0) return [];\n\n const blocks: Array<{ type: 'text'; text: string }> = [\n {\n type: 'text',\n text: [\n '## Telegram Inbox',\n `You have ${bot.bufferCount} unread Telegram message(s).`,\n 'Read them with `telegram_read` and reply with `telegram_send`.',\n '',\n 'Recent messages:',\n ...msgs.map((m) => {\n const who = m.userName ?? `user_${m.userId ?? 'unknown'}`;\n const ts = new Date(m.timestamp).toLocaleTimeString();\n return `- [${ts}] **${who}** (chat=${m.chatId}): ${m.text.slice(0, 200)}`;\n }),\n '',\n ].join('\\n'),\n },\n ];\n return blocks;\n });\n offs.push(unregisterPrompt);\n\n // Register slash commands\n const commandNames = registerSlashCommands(api, bot, cfg);\n\n // ---- Notification event handlers ----\n // Always subscribed; guard at event time against runtime flags so changes\n // take effect immediately without needing to restart the plugin.\n\n offs.push(\n api.events.on('session.ended', (event) => {\n if (!runtimeCfg.notifyOnSessionEnd || !runtimeCfg.notifyChatId) return;\n const payload: SessionEndedLike = {\n id: event.id,\n inputTokens: event.usage.input,\n outputTokens: event.usage.output,\n cacheRead: event.usage.cacheRead,\n cacheWrite: event.usage.cacheWrite,\n };\n const msg = truncateForTelegram(\n formatSessionEnded(payload),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send session end notification: ${(err as Error).message}`);\n });\n }),\n );\n\n offs.push(\n api.events.on('tool.executed', (event) => {\n if (\n !runtimeCfg.notifyChatId ||\n runtimeCfg.longToolThresholdMs <= 0 ||\n event.durationMs < runtimeCfg.longToolThresholdMs\n ) return;\n const payload: ToolExecutedLike = {\n name: event.name,\n ok: event.ok,\n durationMs: event.durationMs,\n output: event.output,\n };\n const msg = truncateForTelegram(\n formatToolExecuted(payload),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send tool notification: ${(err as Error).message}`);\n });\n }),\n );\n\n offs.push(\n api.events.on('delegate.completed', (event) => {\n if (!runtimeCfg.notifyOnDelegate || !runtimeCfg.notifyChatId) return;\n const msg = truncateForTelegram(\n formatDelegateCompleted(event),\n runtimeCfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(runtimeCfg.notifyChatId), msg).catch((err) => {\n log.debug(`Failed to send delegate notification: ${(err as Error).message}`);\n });\n }),\n );\n\n // ---- Live config updates ----\n // api.config is frozen at setup, but onConfigChange fires whenever the\n // ConfigStore is updated (from CLI /settings, WebUI prefSync, /telegram-settings).\n // Update the mutable runtime refs so all handlers pick up the new values\n // on the next event — no restart needed.\n const unlistenConfig = api.onConfigChange((next, _prev) => {\n const fresh = telegramFromConfig(next);\n runtimeCfg.notifyChatId = fresh.notifyChatId;\n runtimeCfg.notifyOnSessionEnd = fresh.notifyOnSessionEnd;\n runtimeCfg.notifyOnDelegate = fresh.notifyOnDelegate;\n runtimeCfg.longToolThresholdMs = fresh.longToolThresholdMs;\n runtimeCfg.maxMessageLength = fresh.maxMessageLength;\n log.debug('Telegram notification settings updated from config', {\n notifyOnSessionEnd: runtimeCfg.notifyOnSessionEnd,\n notifyOnDelegate: runtimeCfg.notifyOnDelegate,\n longToolThresholdMs: runtimeCfg.longToolThresholdMs,\n notifyChatId: runtimeCfg.notifyChatId ?? 'not set',\n });\n });\n offs.push(unlistenConfig);\n\n // ---- Start polling ----\n bot.start();\n\n teardownState = {\n offs,\n toolNames: [sendTool.name, readTool.name],\n commandNames,\n bot,\n runtimeCfg,\n };\n\n log.info('Telegram plugin ready');\n },\n\n async teardown(api) {\n const state = teardownState;\n if (!state) return;\n teardownState = null;\n\n state.bot.stop();\n for (const off of state.offs) off();\n for (const name of state.toolNames) api.tools.unregister(name);\n for (const name of state.commandNames) {\n api.slashCommands.unregister(`${PLUGIN_NAME}:${name}`);\n }\n\n api.log.info('Telegram plugin torn down');\n },\n\n async health() {\n const state = teardownState;\n if (!state?.bot) return { ok: false, message: 'Plugin not initialized' };\n const h = await state.bot.health();\n return h;\n },\n};\n\nexport default plugin;\n\n// Re-export the types consumers may want\nexport type { TelegramIncomingMessage } from './bot.js';\nexport type { TelegramPluginConfig } from './config.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/telegram",
3
- "version": "0.268.0",
3
+ "version": "0.270.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack plugin — Telegram bridge: send messages, receive prompts, get notified.",
6
6
  "repository": {
@@ -25,13 +25,13 @@
25
25
  "dist"
26
26
  ],
27
27
  "peerDependencies": {
28
- "@wrongstack/core": "0.268.0"
28
+ "@wrongstack/core": "0.270.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^25.9.3",
32
32
  "tsup": "^8.5.1",
33
33
  "typescript": "^6.0.3",
34
- "@wrongstack/core": "0.268.0"
34
+ "@wrongstack/core": "0.270.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"