@llblab/pi-kit 0.1.7 → 0.1.9
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/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +17 -0
- package/node_modules/@llblab/pi-telegram/README.md +3 -3
- package/node_modules/@llblab/pi-telegram/api/voice.ts +0 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +5 -5
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +2 -3
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +5 -15
- package/node_modules/@llblab/pi-telegram/docs/voice.md +9 -37
- package/node_modules/@llblab/pi-telegram/index.ts +30 -7
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +112 -25
- package/node_modules/@llblab/pi-telegram/lib/config.ts +38 -46
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +107 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +4 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +15 -11
- package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +3 -18
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +66 -24
- package/node_modules/@llblab/pi-telegram/lib/setup.ts +0 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +11 -4
- package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -31
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +1 -1
- package/package.json +2 -2
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
export const TELEGRAM_BUS_FOLLOWER_PROMOTION_GRACE_MS = 2_500;
|
|
48
48
|
export const TELEGRAM_FOLLOWER_SESSION_HANDOFF_TTL_MS = 30_000;
|
|
49
49
|
export const TELEGRAM_BUS_FOLLOWER_CLIENT_TIMEOUT_MS = 30_000;
|
|
50
|
+
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_WAIT_MS = 30_000;
|
|
50
51
|
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_RETRY_ATTEMPTS =
|
|
51
52
|
TELEGRAM_BUS_REGISTRATION_RETRY.attempts;
|
|
52
53
|
export const TELEGRAM_BUS_FOLLOWER_REGISTRATION_RETRY_DELAY_MS =
|
|
@@ -167,6 +168,9 @@ export interface TelegramBusFollowerRegistrationState {
|
|
|
167
168
|
getSlot: () => string | undefined;
|
|
168
169
|
getThreadName: () => string | undefined;
|
|
169
170
|
getGeneration: () => string | undefined;
|
|
171
|
+
beginRecovery: () => number;
|
|
172
|
+
cancelRecovery: () => void;
|
|
173
|
+
waitForGeneration: (timeoutMs?: number) => Promise<string | undefined>;
|
|
170
174
|
getLeaderProtocol: () => TelegramBusProtocolIdentity | undefined;
|
|
171
175
|
getEligibleElectionSlots: () => readonly string[];
|
|
172
176
|
setEligibleElectionSlots: (slots: readonly string[]) => void;
|
|
@@ -214,6 +218,9 @@ export interface TelegramBusFollowerClientRuntimeDeps<TMessage = unknown> {
|
|
|
214
218
|
getApiAuthSecret?: () => string | undefined;
|
|
215
219
|
getForwardingAuthSecret?: () => string | undefined;
|
|
216
220
|
getRegistrationGeneration: () => string | undefined;
|
|
221
|
+
waitForRegistrationGeneration?: (
|
|
222
|
+
timeoutMs?: number,
|
|
223
|
+
) => Promise<string | undefined>;
|
|
217
224
|
getForwardCommentBatchPosition?: (
|
|
218
225
|
message: TMessage,
|
|
219
226
|
) => "comment" | "forward" | undefined;
|
|
@@ -231,6 +238,9 @@ export interface TelegramBusFollowerApiCallerDeps {
|
|
|
231
238
|
createRequestId: () => string;
|
|
232
239
|
getAuthSecret?: () => string | undefined;
|
|
233
240
|
getRegistrationGeneration: () => string | undefined;
|
|
241
|
+
waitForRegistrationGeneration?: (
|
|
242
|
+
timeoutMs?: number,
|
|
243
|
+
) => Promise<string | undefined>;
|
|
234
244
|
getNowMs?: () => number;
|
|
235
245
|
timeoutMs?: number;
|
|
236
246
|
}
|
|
@@ -417,6 +427,7 @@ export interface TelegramBusFollowerHeartbeatRecoveryHandlerDeps<TContext> {
|
|
|
417
427
|
| "getSlot"
|
|
418
428
|
| "getThreadName"
|
|
419
429
|
| "getEligibleElectionSlots"
|
|
430
|
+
| "beginRecovery"
|
|
420
431
|
| "setRegistered"
|
|
421
432
|
>;
|
|
422
433
|
getRegistrationRuntime: () => TelegramBusFollowerRegistrationRuntime<TContext>;
|
|
@@ -638,6 +649,7 @@ export function createTelegramBusFollowerClientRuntime<
|
|
|
638
649
|
socketPath: deps.socketPath,
|
|
639
650
|
createRequestId,
|
|
640
651
|
timeoutMs,
|
|
652
|
+
waitForRegistrationGeneration: deps.waitForRegistrationGeneration,
|
|
641
653
|
};
|
|
642
654
|
return {
|
|
643
655
|
createRequestId,
|
|
@@ -695,14 +707,14 @@ export function createTelegramBusFollowerQueueHandoffClient(
|
|
|
695
707
|
const timeoutMs =
|
|
696
708
|
deps.timeoutMs ?? TELEGRAM_BUS_FOLLOWER_CLIENT_TIMEOUT_MS;
|
|
697
709
|
return async (input) => {
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
710
|
+
const registration = await resolveTelegramBusFollowerRegistration(
|
|
711
|
+
deps,
|
|
712
|
+
timeoutMs,
|
|
713
|
+
);
|
|
702
714
|
const socketPath = resolveTelegramBusSocketPath(deps.socketPath);
|
|
703
715
|
const response = await sendTelegramBusLocalEnvelope({
|
|
704
716
|
socketPath,
|
|
705
|
-
timeoutMs,
|
|
717
|
+
timeoutMs: registration.remainingTimeoutMs,
|
|
706
718
|
retry: getTelegramBusTransportRetryPolicy({
|
|
707
719
|
endpoint: socketPath,
|
|
708
720
|
operation: "operation",
|
|
@@ -712,7 +724,7 @@ export function createTelegramBusFollowerQueueHandoffClient(
|
|
|
712
724
|
requestId: deps.createRequestId(),
|
|
713
725
|
auth: deps.getAuthSecret?.(),
|
|
714
726
|
instanceId: deps.instanceId,
|
|
715
|
-
registrationGeneration,
|
|
727
|
+
registrationGeneration: registration.generation,
|
|
716
728
|
...input,
|
|
717
729
|
sentAtMs: getNowMs(),
|
|
718
730
|
},
|
|
@@ -770,11 +782,12 @@ export function createTelegramBusAgentMessageClient(
|
|
|
770
782
|
envelope:
|
|
771
783
|
| Extract<TelegramBusEnvelope, { kind: "follower.resolveAgentTarget" }>
|
|
772
784
|
| Extract<TelegramBusEnvelope, { kind: "follower.routeAgentMessage" }>,
|
|
785
|
+
requestTimeoutMs = timeoutMs,
|
|
773
786
|
): Promise<unknown> => {
|
|
774
787
|
const socketPath = resolveTelegramBusSocketPath(deps.socketPath);
|
|
775
788
|
const response = await sendTelegramBusLocalEnvelope({
|
|
776
789
|
socketPath,
|
|
777
|
-
timeoutMs,
|
|
790
|
+
timeoutMs: requestTimeoutMs,
|
|
778
791
|
retry: getTelegramBusTransportRetryPolicy({
|
|
779
792
|
endpoint: socketPath,
|
|
780
793
|
operation: "operation",
|
|
@@ -788,26 +801,30 @@ export function createTelegramBusAgentMessageClient(
|
|
|
788
801
|
: "Telegram bus agent message did not return an acknowledgement.",
|
|
789
802
|
);
|
|
790
803
|
};
|
|
791
|
-
const registrationFields = () => {
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
804
|
+
const registrationFields = async () => {
|
|
805
|
+
const registration = await resolveTelegramBusFollowerRegistration(
|
|
806
|
+
deps,
|
|
807
|
+
timeoutMs,
|
|
808
|
+
);
|
|
796
809
|
return {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
810
|
+
fields: {
|
|
811
|
+
auth: deps.getAuthSecret?.(),
|
|
812
|
+
instanceId: deps.instanceId,
|
|
813
|
+
registrationGeneration: registration.generation,
|
|
814
|
+
},
|
|
815
|
+
remainingTimeoutMs: registration.remainingTimeoutMs,
|
|
800
816
|
};
|
|
801
817
|
};
|
|
802
818
|
return {
|
|
803
819
|
async resolveTarget(selector) {
|
|
820
|
+
const registration = await registrationFields();
|
|
804
821
|
const result = await request({
|
|
805
822
|
kind: "follower.resolveAgentTarget",
|
|
806
823
|
requestId: deps.createRequestId(),
|
|
807
|
-
...
|
|
824
|
+
...registration.fields,
|
|
808
825
|
selector,
|
|
809
826
|
sentAtMs: getNowMs(),
|
|
810
|
-
});
|
|
827
|
+
}, registration.remainingTimeoutMs);
|
|
811
828
|
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
812
829
|
throw new Error("Telegram bus returned an invalid agent target.");
|
|
813
830
|
}
|
|
@@ -821,13 +838,14 @@ export function createTelegramBusAgentMessageClient(
|
|
|
821
838
|
return { chatId: target.chatId, threadId: target.threadId };
|
|
822
839
|
},
|
|
823
840
|
async routeMessage(message) {
|
|
841
|
+
const registration = await registrationFields();
|
|
824
842
|
await request({
|
|
825
843
|
kind: "follower.routeAgentMessage",
|
|
826
844
|
requestId: deps.createRequestId(),
|
|
827
|
-
...
|
|
845
|
+
...registration.fields,
|
|
828
846
|
message,
|
|
829
847
|
sentAtMs: getNowMs(),
|
|
830
|
-
});
|
|
848
|
+
}, registration.remainingTimeoutMs);
|
|
831
849
|
},
|
|
832
850
|
};
|
|
833
851
|
}
|
|
@@ -839,16 +857,16 @@ export function createTelegramBusFollowerApiCaller(
|
|
|
839
857
|
const timeoutMs =
|
|
840
858
|
deps.timeoutMs ?? TELEGRAM_BUS_FOLLOWER_CLIENT_TIMEOUT_MS;
|
|
841
859
|
return async (method, args) => {
|
|
860
|
+
const registration = await resolveTelegramBusFollowerRegistration(
|
|
861
|
+
deps,
|
|
862
|
+
timeoutMs,
|
|
863
|
+
);
|
|
842
864
|
const socketPath = resolveTelegramBusSocketPath(deps.socketPath);
|
|
843
|
-
const registrationGeneration = deps.getRegistrationGeneration();
|
|
844
|
-
if (!registrationGeneration) {
|
|
845
|
-
throw new Error("Telegram bus follower is not registered.");
|
|
846
|
-
}
|
|
847
865
|
let response: TelegramBusEnvelope | undefined;
|
|
848
866
|
try {
|
|
849
867
|
response = await sendTelegramBusLocalEnvelope({
|
|
850
868
|
socketPath,
|
|
851
|
-
timeoutMs,
|
|
869
|
+
timeoutMs: registration.remainingTimeoutMs,
|
|
852
870
|
retry: getTelegramBusTransportRetryPolicy({
|
|
853
871
|
endpoint: socketPath,
|
|
854
872
|
operation: "operation",
|
|
@@ -858,7 +876,7 @@ export function createTelegramBusFollowerApiCaller(
|
|
|
858
876
|
requestId: deps.createRequestId(),
|
|
859
877
|
auth: deps.getAuthSecret?.(),
|
|
860
878
|
instanceId: deps.instanceId,
|
|
861
|
-
registrationGeneration,
|
|
879
|
+
registrationGeneration: registration.generation,
|
|
862
880
|
method,
|
|
863
881
|
args,
|
|
864
882
|
sentAtMs: getNowMs(),
|
|
@@ -893,6 +911,29 @@ export function createTelegramBusFollowerApiCaller(
|
|
|
893
911
|
};
|
|
894
912
|
}
|
|
895
913
|
|
|
914
|
+
async function resolveTelegramBusFollowerRegistration(
|
|
915
|
+
deps: Pick<
|
|
916
|
+
TelegramBusFollowerApiCallerDeps,
|
|
917
|
+
| "getRegistrationGeneration"
|
|
918
|
+
| "waitForRegistrationGeneration"
|
|
919
|
+
| "getNowMs"
|
|
920
|
+
>,
|
|
921
|
+
timeoutMs: number,
|
|
922
|
+
): Promise<{ generation: string; remainingTimeoutMs: number }> {
|
|
923
|
+
const current = deps.getRegistrationGeneration();
|
|
924
|
+
if (current) return { generation: current, remainingTimeoutMs: timeoutMs };
|
|
925
|
+
const getNowMs = deps.getNowMs ?? Date.now;
|
|
926
|
+
const startedAtMs = getNowMs();
|
|
927
|
+
const restored = await deps.waitForRegistrationGeneration?.(
|
|
928
|
+
timeoutMs,
|
|
929
|
+
);
|
|
930
|
+
const remainingTimeoutMs = Math.max(0, timeoutMs - (getNowMs() - startedAtMs));
|
|
931
|
+
if (restored && remainingTimeoutMs > 0) {
|
|
932
|
+
return { generation: restored, remainingTimeoutMs };
|
|
933
|
+
}
|
|
934
|
+
throw new Error("Telegram bus follower is not registered.");
|
|
935
|
+
}
|
|
936
|
+
|
|
896
937
|
function isTelegramStaleContextError(error: unknown): boolean {
|
|
897
938
|
return (
|
|
898
939
|
error instanceof Error &&
|
|
@@ -1046,12 +1087,52 @@ export function createTelegramBusFollowerRegistrationState(
|
|
|
1046
1087
|
let generation: string | undefined;
|
|
1047
1088
|
let leaderProtocol: TelegramBusProtocolIdentity | undefined;
|
|
1048
1089
|
let eligibleElectionSlots: string[] = [];
|
|
1090
|
+
let recoveryEpoch = 0;
|
|
1091
|
+
let activeRecoveryEpoch: number | undefined;
|
|
1092
|
+
const generationWaiters = new Set<{
|
|
1093
|
+
epoch: number;
|
|
1094
|
+
settle: (value: string | undefined) => void;
|
|
1095
|
+
}>();
|
|
1096
|
+
const settleGenerationWaiters = (
|
|
1097
|
+
value: string | undefined,
|
|
1098
|
+
epoch?: number,
|
|
1099
|
+
) => {
|
|
1100
|
+
for (const waiter of [...generationWaiters]) {
|
|
1101
|
+
if (epoch === undefined || waiter.epoch === epoch) waiter.settle(value);
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1049
1104
|
return {
|
|
1050
1105
|
isRegistered: () => registered,
|
|
1051
1106
|
getTarget: () => (target ? { ...target } : undefined),
|
|
1052
1107
|
getSlot: () => slot,
|
|
1053
1108
|
getThreadName: () => threadName,
|
|
1054
1109
|
getGeneration: () => generation,
|
|
1110
|
+
beginRecovery: () => {
|
|
1111
|
+
if (activeRecoveryEpoch !== undefined) return activeRecoveryEpoch;
|
|
1112
|
+
activeRecoveryEpoch = ++recoveryEpoch;
|
|
1113
|
+
return activeRecoveryEpoch;
|
|
1114
|
+
},
|
|
1115
|
+
cancelRecovery: () => {
|
|
1116
|
+
const epoch = activeRecoveryEpoch;
|
|
1117
|
+
activeRecoveryEpoch = undefined;
|
|
1118
|
+
if (epoch !== undefined) settleGenerationWaiters(undefined, epoch);
|
|
1119
|
+
},
|
|
1120
|
+
waitForGeneration: (timeoutMs = TELEGRAM_BUS_FOLLOWER_REGISTRATION_WAIT_MS) => {
|
|
1121
|
+
if (generation) return Promise.resolve(generation);
|
|
1122
|
+
const epoch = activeRecoveryEpoch;
|
|
1123
|
+
if (epoch === undefined) return Promise.resolve(undefined);
|
|
1124
|
+
return new Promise((resolve) => {
|
|
1125
|
+
let timer: NodeJS.Timeout | undefined;
|
|
1126
|
+
const settle = (value: string | undefined) => {
|
|
1127
|
+
generationWaiters.delete(waiter);
|
|
1128
|
+
if (timer) clearTimeout(timer);
|
|
1129
|
+
resolve(value);
|
|
1130
|
+
};
|
|
1131
|
+
const waiter = { epoch, settle };
|
|
1132
|
+
generationWaiters.add(waiter);
|
|
1133
|
+
timer = setTimeout(() => settle(undefined), Math.max(0, timeoutMs));
|
|
1134
|
+
});
|
|
1135
|
+
},
|
|
1055
1136
|
getLeaderProtocol: () =>
|
|
1056
1137
|
leaderProtocol
|
|
1057
1138
|
? { ...leaderProtocol, capabilities: [...leaderProtocol.capabilities] }
|
|
@@ -1077,6 +1158,10 @@ export function createTelegramBusFollowerRegistrationState(
|
|
|
1077
1158
|
}
|
|
1078
1159
|
: undefined;
|
|
1079
1160
|
if (availabilityChanged) options.onAvailabilityChanged?.();
|
|
1161
|
+
if (generation) {
|
|
1162
|
+
activeRecoveryEpoch = undefined;
|
|
1163
|
+
settleGenerationWaiters(generation);
|
|
1164
|
+
}
|
|
1080
1165
|
},
|
|
1081
1166
|
};
|
|
1082
1167
|
}
|
|
@@ -1253,6 +1338,7 @@ export function createTelegramBusFollowerHeartbeatRecoveryHandler<TContext>(
|
|
|
1253
1338
|
): Promise<void> => {
|
|
1254
1339
|
if (promotionPending) return;
|
|
1255
1340
|
promotionPending = true;
|
|
1341
|
+
deps.registrationState.beginRecovery();
|
|
1256
1342
|
try {
|
|
1257
1343
|
const initialBinding = carriedBinding ?? snapshotBinding();
|
|
1258
1344
|
const state = deps.getLeaderState();
|
|
@@ -1366,6 +1452,7 @@ export function createTelegramBusFollowerRegistrationRuntime<
|
|
|
1366
1452
|
heartbeatPromise = undefined;
|
|
1367
1453
|
heartbeatPromiseGeneration = undefined;
|
|
1368
1454
|
deps.setActiveAuthSecret?.(undefined);
|
|
1455
|
+
deps.registrationState?.cancelRecovery();
|
|
1369
1456
|
deps.registrationState?.setRegistered(false);
|
|
1370
1457
|
lastKnownTarget = undefined;
|
|
1371
1458
|
lastKnownSlot = undefined;
|
|
@@ -99,8 +99,6 @@ export interface TelegramConfig {
|
|
|
99
99
|
botId?: number;
|
|
100
100
|
/** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
|
|
101
101
|
allowedUserId?: number;
|
|
102
|
-
/** @deprecated persisted identity belongs in profiles.default; retained for effective/legacy views */
|
|
103
|
-
lastUpdateId?: number;
|
|
104
102
|
inboundHandlers?: TelegramInboundHandlerConfig[];
|
|
105
103
|
attachmentHandlers?: TelegramInboundHandlerConfig[];
|
|
106
104
|
outboundHandlers?: TelegramOutboundHandlerConfig[];
|
|
@@ -120,9 +118,8 @@ export interface TelegramConfig {
|
|
|
120
118
|
/** @deprecated use assistant.rendering */
|
|
121
119
|
assistantRendering?: TelegramAssistantRenderingMode;
|
|
122
120
|
voice?: {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
sendTranscript?: boolean;
|
|
121
|
+
/** `hidden` is a read-only compatibility alias for the former manual mode. */
|
|
122
|
+
replyMode?: "manual" | "hidden" | "mirror" | "always";
|
|
126
123
|
};
|
|
127
124
|
time?: TelegramTimeConfig;
|
|
128
125
|
threads?: {
|
|
@@ -144,6 +141,9 @@ export interface TelegramBotProfile {
|
|
|
144
141
|
botUsername?: string;
|
|
145
142
|
botId?: number;
|
|
146
143
|
allowedUserId?: number;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface TelegramLegacyCursorCarrier {
|
|
147
147
|
lastUpdateId?: number;
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -177,6 +177,8 @@ export interface TelegramConfigStore {
|
|
|
177
177
|
getBotToken: () => string | undefined;
|
|
178
178
|
hasBotToken: () => boolean;
|
|
179
179
|
getAllowedUserId: () => number | undefined;
|
|
180
|
+
getLegacyPollingCursor: () => number | undefined;
|
|
181
|
+
removeLegacyPollingCursor: () => void;
|
|
180
182
|
getInboundHandlers: () => TelegramInboundHandlerConfig[] | undefined;
|
|
181
183
|
getAttachmentHandlers: () => TelegramInboundHandlerConfig[] | undefined;
|
|
182
184
|
getOutboundHandlers: () => TelegramOutboundHandlerConfig[] | undefined;
|
|
@@ -368,15 +370,7 @@ function mergeTelegramConfigDelta(
|
|
|
368
370
|
continue;
|
|
369
371
|
}
|
|
370
372
|
if (!desiredHas) {
|
|
371
|
-
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
if (
|
|
375
|
-
key === "lastUpdateId" &&
|
|
376
|
-
typeof desiredValue === "number" &&
|
|
377
|
-
typeof merged[key] === "number"
|
|
378
|
-
) {
|
|
379
|
-
merged[key] = Math.max(desiredValue, merged[key] as number);
|
|
373
|
+
delete merged[key];
|
|
380
374
|
continue;
|
|
381
375
|
}
|
|
382
376
|
if (
|
|
@@ -433,6 +427,8 @@ export function getTelegramProfileFields(
|
|
|
433
427
|
): TelegramBotProfile | undefined {
|
|
434
428
|
const token = config.botToken?.trim();
|
|
435
429
|
if (!token) return undefined;
|
|
430
|
+
const legacyCursor = (config as TelegramConfig & TelegramLegacyCursorCarrier)
|
|
431
|
+
.lastUpdateId;
|
|
436
432
|
return {
|
|
437
433
|
botToken: token,
|
|
438
434
|
...(config.botUsername !== undefined
|
|
@@ -442,9 +438,7 @@ export function getTelegramProfileFields(
|
|
|
442
438
|
...(config.allowedUserId !== undefined
|
|
443
439
|
? { allowedUserId: config.allowedUserId }
|
|
444
440
|
: {}),
|
|
445
|
-
...(
|
|
446
|
-
? { lastUpdateId: config.lastUpdateId }
|
|
447
|
-
: {}),
|
|
441
|
+
...(legacyCursor !== undefined ? { lastUpdateId: legacyCursor } : {}),
|
|
448
442
|
};
|
|
449
443
|
}
|
|
450
444
|
|
|
@@ -456,7 +450,7 @@ function omitTelegramRootProfileFields(config: TelegramConfig): TelegramConfig {
|
|
|
456
450
|
allowedUserId: _allowedUserId,
|
|
457
451
|
lastUpdateId: _lastUpdateId,
|
|
458
452
|
...sharedConfig
|
|
459
|
-
} = config;
|
|
453
|
+
} = config as TelegramConfig & TelegramLegacyCursorCarrier;
|
|
460
454
|
return sharedConfig;
|
|
461
455
|
}
|
|
462
456
|
|
|
@@ -477,7 +471,8 @@ export function normalizeTelegramDefaultProfileConfig(config: TelegramConfig): {
|
|
|
477
471
|
if (Object.hasOwn(config, "botToken") && !legacyToken) {
|
|
478
472
|
throw new Error("Legacy Telegram default profile has no bot token");
|
|
479
473
|
}
|
|
480
|
-
const legacyProfile: Partial<TelegramBotProfile>
|
|
474
|
+
const legacyProfile: Partial<TelegramBotProfile> &
|
|
475
|
+
TelegramLegacyCursorCarrier = {
|
|
481
476
|
...(legacyToken ? { botToken: legacyToken } : {}),
|
|
482
477
|
...(config.botUsername !== undefined
|
|
483
478
|
? { botUsername: config.botUsername }
|
|
@@ -486,8 +481,12 @@ export function normalizeTelegramDefaultProfileConfig(config: TelegramConfig): {
|
|
|
486
481
|
...(config.allowedUserId !== undefined
|
|
487
482
|
? { allowedUserId: config.allowedUserId }
|
|
488
483
|
: {}),
|
|
489
|
-
...(config
|
|
490
|
-
|
|
484
|
+
...((config as TelegramConfig & TelegramLegacyCursorCarrier)
|
|
485
|
+
.lastUpdateId !== undefined
|
|
486
|
+
? {
|
|
487
|
+
lastUpdateId: (config as TelegramConfig & TelegramLegacyCursorCarrier)
|
|
488
|
+
.lastUpdateId,
|
|
489
|
+
}
|
|
491
490
|
: {}),
|
|
492
491
|
};
|
|
493
492
|
if (!canonicalProfile && !legacyToken) {
|
|
@@ -609,6 +608,16 @@ export function createTelegramConfigStore(
|
|
|
609
608
|
getBotToken: () => getEffectiveConfig().botToken,
|
|
610
609
|
hasBotToken: () => !!getEffectiveConfig().botToken,
|
|
611
610
|
getAllowedUserId: () => getEffectiveConfig().allowedUserId,
|
|
611
|
+
getLegacyPollingCursor: () =>
|
|
612
|
+
(getEffectiveConfig() as TelegramConfig & TelegramLegacyCursorCarrier)
|
|
613
|
+
.lastUpdateId,
|
|
614
|
+
removeLegacyPollingCursor: () => {
|
|
615
|
+
const next = {
|
|
616
|
+
...(getEffectiveConfig() as TelegramConfig & TelegramLegacyCursorCarrier),
|
|
617
|
+
};
|
|
618
|
+
delete next.lastUpdateId;
|
|
619
|
+
setEffectiveConfig(next);
|
|
620
|
+
},
|
|
612
621
|
getInboundHandlers: () => [
|
|
613
622
|
...(config.inboundHandlers ?? []),
|
|
614
623
|
...(config.attachmentHandlers ?? []),
|
|
@@ -703,27 +712,6 @@ export function createTelegramConfigStore(
|
|
|
703
712
|
};
|
|
704
713
|
}
|
|
705
714
|
|
|
706
|
-
export function createTelegramPollingOffsetPersister(
|
|
707
|
-
configStore: Pick<TelegramConfigStore, "get" | "set" | "persist">,
|
|
708
|
-
persist: () => Promise<void> = () => configStore.persist(),
|
|
709
|
-
): (pollingConfig: { lastUpdateId?: number }) => Promise<void> {
|
|
710
|
-
return async (pollingConfig) => {
|
|
711
|
-
const nextOffset = pollingConfig.lastUpdateId;
|
|
712
|
-
if (typeof nextOffset === "number") {
|
|
713
|
-
const current = configStore.get();
|
|
714
|
-
const currentOffset = current.lastUpdateId;
|
|
715
|
-
configStore.set({
|
|
716
|
-
...current,
|
|
717
|
-
lastUpdateId:
|
|
718
|
-
typeof currentOffset === "number"
|
|
719
|
-
? Math.max(currentOffset, nextOffset)
|
|
720
|
-
: nextOffset,
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
await persist();
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
715
|
export function createTelegramProactivePushChecker(
|
|
728
716
|
configStore: Pick<TelegramConfigStore, "get">,
|
|
729
717
|
): () => boolean {
|
|
@@ -856,10 +844,10 @@ export function createTelegramActivityVerbositySetter(
|
|
|
856
844
|
|
|
857
845
|
export function createTelegramVoiceReplyModeGetter(
|
|
858
846
|
configStore: Pick<TelegramConfigStore, "get">,
|
|
859
|
-
): () => "
|
|
847
|
+
): () => "manual" | "mirror" | "always" {
|
|
860
848
|
return () => {
|
|
861
849
|
const mode = configStore.get().voice?.replyMode;
|
|
862
|
-
return mode === "mirror" || mode === "always" ? mode : "
|
|
850
|
+
return mode === "mirror" || mode === "always" ? mode : "manual";
|
|
863
851
|
};
|
|
864
852
|
}
|
|
865
853
|
|
|
@@ -874,11 +862,15 @@ export function createTelegramVoiceReplyModeConfiguredChecker(
|
|
|
874
862
|
|
|
875
863
|
export function createTelegramVoiceReplyModeSetter(
|
|
876
864
|
configStore: TelegramMutableConfigStore,
|
|
877
|
-
): (replyMode: "hidden" | "mirror" | "always" | undefined) => Promise<void> {
|
|
865
|
+
): (replyMode: "manual" | "hidden" | "mirror" | "always" | undefined) => Promise<void> {
|
|
878
866
|
return async (replyMode) => {
|
|
879
867
|
await loadLatestTelegramConfig(configStore);
|
|
880
868
|
const current = configStore.get();
|
|
881
|
-
if (
|
|
869
|
+
if (
|
|
870
|
+
replyMode === undefined ||
|
|
871
|
+
replyMode === "manual" ||
|
|
872
|
+
replyMode === "hidden"
|
|
873
|
+
) {
|
|
882
874
|
const { replyMode: _replyMode, ...remainingVoice } = current.voice ?? {};
|
|
883
875
|
const next = { ...current };
|
|
884
876
|
if (Object.keys(remainingVoice).length > 0) next.voice = remainingVoice;
|