@bytesbrains/pi-telegram-bridge 1.0.0 → 1.0.2

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.
@@ -0,0 +1,251 @@
1
+ /**
2
+ * pi-telegram-bridge — Tool Schema Tests
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { Value } from "typebox/value";
6
+ import {
7
+ listenSchema,
8
+ sendSchema,
9
+ askSchema,
10
+ statusSchema,
11
+ overrideSchema,
12
+ } from "../tools/telegram";
13
+
14
+ // ── listenSchema ──────────────────────────────────────────────────
15
+
16
+ describe("listenSchema", () => {
17
+ it("accepts empty object", () => {
18
+ expect(Value.Check(listenSchema, {})).toBe(true);
19
+ });
20
+
21
+ it("still checks ok with extra properties (by default)", () => {
22
+ // TypeBox Object doesn't strip extra by default unless additionalProperties: false
23
+ expect(Value.Check(listenSchema, { extra: 1 })).toBe(true);
24
+ });
25
+ });
26
+
27
+ // ── sendSchema ────────────────────────────────────────────────────
28
+
29
+ describe("sendSchema", () => {
30
+ it("accepts valid message", () => {
31
+ expect(Value.Check(sendSchema, { message: "Hello" })).toBe(true);
32
+ });
33
+
34
+ it("accepts empty message", () => {
35
+ expect(Value.Check(sendSchema, { message: "" })).toBe(true);
36
+ });
37
+
38
+ it("rejects missing message", () => {
39
+ expect(Value.Check(sendSchema, {})).toBe(false);
40
+ expect(Value.Check(sendSchema, { message: 123 })).toBe(false);
41
+ expect(Value.Check(sendSchema, { message: null })).toBe(false);
42
+ });
43
+
44
+ it("accepts long messages", () => {
45
+ expect(Value.Check(sendSchema, { message: "x".repeat(4096) })).toBe(true);
46
+ });
47
+ });
48
+
49
+ // ── askSchema ─────────────────────────────────────────────────────
50
+
51
+ describe("askSchema", () => {
52
+ it("accepts minimal valid input (question only)", () => {
53
+ expect(Value.Check(askSchema, { question: "Deploy?" })).toBe(true);
54
+ });
55
+
56
+ it("accepts question with options", () => {
57
+ expect(
58
+ Value.Check(askSchema, {
59
+ question: "Which?",
60
+ options: ["A", "B", "C"],
61
+ }),
62
+ ).toBe(true);
63
+ });
64
+
65
+ it("accepts question with timeoutMinutes", () => {
66
+ expect(
67
+ Value.Check(askSchema, {
68
+ question: "Go?",
69
+ timeoutMinutes: 5,
70
+ }),
71
+ ).toBe(true);
72
+ });
73
+
74
+ it("accepts all fields", () => {
75
+ expect(
76
+ Value.Check(askSchema, {
77
+ question: "Full test",
78
+ options: ["Yes", "No"],
79
+ timeoutMinutes: 10,
80
+ }),
81
+ ).toBe(true);
82
+ });
83
+
84
+ it("rejects missing question", () => {
85
+ expect(Value.Check(askSchema, {})).toBe(false);
86
+ expect(Value.Check(askSchema, { question: 123 })).toBe(false);
87
+ expect(Value.Check(askSchema, { options: ["A"] })).toBe(false);
88
+ });
89
+
90
+ it("accepts empty options array", () => {
91
+ expect(
92
+ Value.Check(askSchema, {
93
+ question: "test",
94
+ options: [],
95
+ }),
96
+ ).toBe(true);
97
+ });
98
+
99
+ it("rejects non-array options", () => {
100
+ expect(
101
+ Value.Check(askSchema, {
102
+ question: "test",
103
+ options: "not-array",
104
+ }),
105
+ ).toBe(false);
106
+ });
107
+
108
+ it("rejects non-number timeoutMinutes", () => {
109
+ expect(
110
+ Value.Check(askSchema, {
111
+ question: "test",
112
+ timeoutMinutes: "30",
113
+ }),
114
+ ).toBe(false);
115
+ });
116
+
117
+ it("accepts timeoutMinutes as 0", () => {
118
+ expect(
119
+ Value.Check(askSchema, {
120
+ question: "test",
121
+ timeoutMinutes: 0,
122
+ }),
123
+ ).toBe(true);
124
+ });
125
+ });
126
+
127
+ // ── statusSchema ──────────────────────────────────────────────────
128
+
129
+ describe("statusSchema", () => {
130
+ it("accepts empty object", () => {
131
+ expect(Value.Check(statusSchema, {})).toBe(true);
132
+ });
133
+ });
134
+
135
+ // ── overrideSchema ────────────────────────────────────────────────
136
+
137
+ describe("overrideSchema", () => {
138
+ it("accepts required fields only", () => {
139
+ expect(
140
+ Value.Check(overrideSchema, {
141
+ command: "git push --force",
142
+ reason: "Force push detected",
143
+ }),
144
+ ).toBe(true);
145
+ });
146
+
147
+ it("rejects missing command", () => {
148
+ expect(
149
+ Value.Check(overrideSchema, {
150
+ reason: "test",
151
+ }),
152
+ ).toBe(false);
153
+ });
154
+
155
+ it("rejects missing reason", () => {
156
+ expect(
157
+ Value.Check(overrideSchema, {
158
+ command: "rm -rf /",
159
+ }),
160
+ ).toBe(false);
161
+ });
162
+
163
+ it("accepts all optional fields", () => {
164
+ expect(
165
+ Value.Check(overrideSchema, {
166
+ command: "rm -rf node_modules",
167
+ reason: "Outside project boundary",
168
+ context: "User requested clean reinstall",
169
+ options: ["Yes", "No", "Maybe", "Explain"],
170
+ timeoutMinutes: 15,
171
+ }),
172
+ ).toBe(true);
173
+ });
174
+
175
+ it("accepts empty options array", () => {
176
+ expect(
177
+ Value.Check(overrideSchema, {
178
+ command: "test",
179
+ reason: "test",
180
+ options: [],
181
+ }),
182
+ ).toBe(true);
183
+ });
184
+
185
+ it("rejects non-string command", () => {
186
+ expect(
187
+ Value.Check(overrideSchema, {
188
+ command: 123,
189
+ reason: "test",
190
+ }),
191
+ ).toBe(false);
192
+ });
193
+
194
+ it("rejects non-string reason", () => {
195
+ expect(
196
+ Value.Check(overrideSchema, {
197
+ command: "test",
198
+ reason: null,
199
+ }),
200
+ ).toBe(false);
201
+ });
202
+
203
+ it("rejects non-string context", () => {
204
+ expect(
205
+ Value.Check(overrideSchema, {
206
+ command: "test",
207
+ reason: "test",
208
+ context: 123,
209
+ }),
210
+ ).toBe(false);
211
+ });
212
+
213
+ it("rejects non-array options", () => {
214
+ expect(
215
+ Value.Check(overrideSchema, {
216
+ command: "test",
217
+ reason: "test",
218
+ options: "yes,no",
219
+ }),
220
+ ).toBe(false);
221
+ });
222
+
223
+ it("rejects non-number timeoutMinutes", () => {
224
+ expect(
225
+ Value.Check(overrideSchema, {
226
+ command: "test",
227
+ reason: "test",
228
+ timeoutMinutes: "10",
229
+ }),
230
+ ).toBe(false);
231
+ });
232
+
233
+ it("accepts timeoutMinutes as 0", () => {
234
+ expect(
235
+ Value.Check(overrideSchema, {
236
+ command: "test",
237
+ reason: "test",
238
+ timeoutMinutes: 0,
239
+ }),
240
+ ).toBe(true);
241
+ });
242
+
243
+ it("accepts long command and reason strings", () => {
244
+ expect(
245
+ Value.Check(overrideSchema, {
246
+ command: "x".repeat(1000),
247
+ reason: "y".repeat(1000),
248
+ }),
249
+ ).toBe(true);
250
+ });
251
+ });
package/src/helpers.ts CHANGED
@@ -67,7 +67,11 @@ export async function pollReply(
67
67
  // Drain pending updates to start fresh
68
68
  let lastId = 0;
69
69
  try {
70
- const drain = await fetch(`${BASE_URL}${token}/getUpdates?timeout=0`);
70
+ const drain = await fetch(`${BASE_URL}${token}/getUpdates`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({ timeout: 0 }),
74
+ });
71
75
  if (drain.ok) {
72
76
  const d = (await drain.json()) as {
73
77
  ok: boolean;
@@ -83,7 +87,12 @@ export async function pollReply(
83
87
 
84
88
  while (Date.now() < deadline) {
85
89
  const res = await fetch(
86
- `${BASE_URL}${token}/getUpdates?offset=${lastId + 1}&timeout=5`,
90
+ `${BASE_URL}${token}/getUpdates`,
91
+ {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({ offset: lastId + 1, timeout: 5 }),
95
+ },
87
96
  );
88
97
  if (!res.ok) {
89
98
  await new Promise((r) => setTimeout(r, 3000));
@@ -154,7 +163,10 @@ export async function seedLastUpdateId(
154
163
  if (lastUpdateId !== 0) return lastUpdateId;
155
164
  const token = getToken();
156
165
  try {
157
- const res = await fetch(`${BASE_URL}${token}/getUpdates?timeout=0`, {
166
+ const res = await fetch(`${BASE_URL}${token}/getUpdates`, {
167
+ method: "POST",
168
+ headers: { "Content-Type": "application/json" },
169
+ body: JSON.stringify({ timeout: 0 }),
158
170
  signal,
159
171
  });
160
172
  if (res.ok) {
@@ -184,8 +196,13 @@ export async function pollUpdates(
184
196
  while (!signal.aborted) {
185
197
  try {
186
198
  const res = await fetch(
187
- `${BASE_URL}${token}/getUpdates?offset=${lastUpdateId + 1}&timeout=10`,
188
- { signal },
199
+ `${BASE_URL}${token}/getUpdates`,
200
+ {
201
+ method: "POST",
202
+ headers: { "Content-Type": "application/json" },
203
+ body: JSON.stringify({ offset: lastUpdateId + 1, timeout: 10 }),
204
+ signal,
205
+ },
189
206
  );
190
207
  if (!res.ok) {
191
208
  await new Promise((r) => setTimeout(r, 3000));
package/src/index.ts CHANGED
@@ -102,7 +102,12 @@ export default function telegramBridge(pi: ExtensionAPI) {
102
102
  if (!botId) botId = await getBotId();
103
103
 
104
104
  const res = await fetch(
105
- `https://api.telegram.org/bot${token}/getUpdates?offset=${lastUpdateId + 1}&timeout=5`,
105
+ `https://api.telegram.org/bot${token}/getUpdates`,
106
+ {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify({ offset: lastUpdateId + 1, timeout: 5 }),
110
+ },
106
111
  );
107
112
  if (!res.ok)
108
113
  return {