botcast-sdk 1.0.6 → 1.0.8

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/README.md CHANGED
@@ -16,7 +16,7 @@ Official Node.js and TypeScript SDK for [Botcast](https://botcast.site) — a hi
16
16
  - **Strict TypeScript Types**: Fully typed request parameters, response models, error classes, and event payloads.
17
17
  - **Dual Authentication**:
18
18
  - **WhatsApp**: Scan QR code string or request an 8-digit PIN code without a camera.
19
- - **Telegram**: Scan QR code via *Telegram > Settings > Devices > Link Desktop Device* or authenticate via phone verification code with optional Cloud 2FA password.
19
+ - **Telegram**: Scan QR code via _Telegram > Settings > Devices > Link Desktop Device_ or authenticate via phone verification code with optional Cloud 2FA password.
20
20
  - **Unified Messaging**: Send text messages, automated OTP codes, images, videos, audio, voice notes (PTT), documents, stickers, locations, contact cards, and interactive polls.
21
21
  - **Message Operations**: Edit sent messages, delete for everyone (revoke), mark as read, reactions, pin messages, and forward.
22
22
  - **Chat & History Management**: Query dialogs, contact lists, and message history for both platforms.
@@ -46,10 +46,10 @@ bun add botcast-sdk
46
46
  Use `BotcastAdminClient` with your Master Partner API Key (`bcast_live_...`) to programmatically create and manage customer instances:
47
47
 
48
48
  ```typescript
49
- import { BotcastAdminClient } from 'botcast-sdk';
49
+ import { BotcastAdminClient } from "botcast-sdk";
50
50
 
51
51
  const admin = new BotcastAdminClient({
52
- baseUrl: 'https://botcast.site',
52
+ baseUrl: "https://botcast.site",
53
53
  apiKey: process.env.BOTCAST_API_KEY!, // Master API Key
54
54
  });
55
55
 
@@ -58,14 +58,16 @@ const { instances } = await admin.listInstances();
58
58
 
59
59
  // Create a new WhatsApp or Telegram instance for a customer
60
60
  const { instance } = await admin.createInstance({
61
- name: 'Customer Store 1',
62
- platform: 'whatsapp',
63
- type: 'paid',
61
+ name: "Customer Store 1",
62
+ platform: "whatsapp",
63
+ type: "paid",
64
64
  plan_months: 12,
65
+ expires_at: "2027-12-31T23:59:59.000Z", // Optional exact expiration date
65
66
  });
66
67
 
67
- // Renew subscription
68
+ // Renew subscription (by months or exact date)
68
69
  await admin.renewInstance(instance.id, { months: 12 });
70
+ // or: await admin.setExpiration(instance.id, "2028-06-30T23:59:59.000Z");
69
71
  ```
70
72
 
71
73
  ---
@@ -73,30 +75,35 @@ await admin.renewInstance(instance.id, { months: 12 });
73
75
  ### 2. WhatsApp Client
74
76
 
75
77
  ```typescript
76
- import { BotcastClient } from 'botcast-sdk';
78
+ import { BotcastClient } from "botcast-sdk";
77
79
 
78
80
  const whatsappBot = new BotcastClient({
79
- baseUrl: 'https://botcast.site', // or your self-hosted server URL
80
- platform: 'whatsapp', // 'whatsapp' (default) or 'telegram'
81
- instanceId: 'your_instance_id',
82
- instanceToken: 'your_instance_token',
81
+ baseUrl: "https://botcast.site", // or your self-hosted server URL
82
+ platform: "whatsapp", // 'whatsapp' (default) or 'telegram'
83
+ instanceId: "your_instance_id",
84
+ instanceToken: "your_instance_token",
83
85
  });
84
86
 
85
87
  async function main() {
86
88
  // Check connectivity
87
89
  const status = await whatsappBot.getStatus();
88
- console.log(`WhatsApp state: ${status.status} (${status.phoneNumber || 'Unpaired'})`);
90
+ console.log(
91
+ `WhatsApp state: ${status.status} (${status.phoneNumber || "Unpaired"})`,
92
+ );
89
93
 
90
94
  // Send a text message
91
- const res = await whatsappBot.messages.sendText('201012345678', 'Hello from WhatsApp via Botcast SDK!');
92
- console.log('Message sent! ID:', res.messageId);
95
+ const res = await whatsappBot.messages.sendText(
96
+ "201012345678",
97
+ "Hello from WhatsApp via Botcast SDK!",
98
+ );
99
+ console.log("Message sent! ID:", res.messageId);
93
100
 
94
101
  // Send a secure 6-digit OTP code
95
- const otp = await whatsappBot.messages.sendOTP('201012345678', {
96
- appName: 'My App',
102
+ const otp = await whatsappBot.messages.sendOTP("201012345678", {
103
+ appName: "My App",
97
104
  expiryMinutes: 5,
98
105
  });
99
- console.log('Dispatched OTP:', otp.codeSent);
106
+ console.log("Dispatched OTP:", otp.codeSent);
100
107
  }
101
108
 
102
109
  main().catch(console.error);
@@ -107,13 +114,13 @@ main().catch(console.error);
107
114
  ### 2. Telegram MTProto Client
108
115
 
109
116
  ```typescript
110
- import { BotcastClient } from 'botcast-sdk';
117
+ import { BotcastClient } from "botcast-sdk";
111
118
 
112
119
  const telegramBot = new BotcastClient({
113
- baseUrl: 'https://botcast.site',
114
- platform: 'telegram',
115
- instanceId: 'your_telegram_instance_id',
116
- instanceToken: 'your_telegram_instance_token',
120
+ baseUrl: "https://botcast.site",
121
+ platform: "telegram",
122
+ instanceId: "your_telegram_instance_id",
123
+ instanceToken: "your_telegram_instance_token",
117
124
  });
118
125
 
119
126
  async function main() {
@@ -122,20 +129,23 @@ async function main() {
122
129
  console.log(`Telegram state: ${status.status}`);
123
130
 
124
131
  // Send a text message to a phone number or @username
125
- const res = await telegramBot.messages.sendText('+1234567890', 'Hello from Telegram MTProto!');
126
- console.log('Message ID:', res.messageId);
132
+ const res = await telegramBot.messages.sendText(
133
+ "+1234567890",
134
+ "Hello from Telegram MTProto!",
135
+ );
136
+ console.log("Message ID:", res.messageId);
127
137
 
128
138
  // Send a photo
129
- await telegramBot.messages.sendImage('+1234567890', {
130
- url: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800',
131
- caption: 'Welcome flyer',
139
+ await telegramBot.messages.sendImage("+1234567890", {
140
+ url: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800",
141
+ caption: "Welcome flyer",
132
142
  });
133
143
 
134
144
  // Send OTP
135
- const otp = await telegramBot.messages.sendOTP('+1234567890', {
136
- appName: 'Security Portal',
145
+ const otp = await telegramBot.messages.sendOTP("+1234567890", {
146
+ appName: "Security Portal",
137
147
  });
138
- console.log('Telegram OTP sent:', otp.codeSent);
148
+ console.log("Telegram OTP sent:", otp.codeSent);
139
149
  }
140
150
 
141
151
  main().catch(console.error);
@@ -146,12 +156,12 @@ main().catch(console.error);
146
156
  ### CommonJS (require)
147
157
 
148
158
  ```javascript
149
- const { BotcastClient } = require('botcast-sdk');
159
+ const { BotcastClient } = require("botcast-sdk");
150
160
 
151
161
  const client = new BotcastClient({
152
- platform: 'telegram', // or 'whatsapp'
153
- instanceId: 'your_instance_id',
154
- instanceToken: 'your_instance_token',
162
+ platform: "telegram", // or 'whatsapp'
163
+ instanceId: "your_instance_id",
164
+ instanceToken: "your_instance_token",
155
165
  });
156
166
  ```
157
167
 
@@ -164,24 +174,24 @@ const client = new BotcastClient({
164
174
  ```typescript
165
175
  // 1. Get live status
166
176
  const status = await client.instances.getStatus();
167
- console.log('State:', status.status); // 'connected' | 'qr_ready' | 'stopped' | 'connecting'
177
+ console.log("State:", status.status); // 'connected' | 'qr_ready' | 'stopped' | 'connecting'
168
178
 
169
179
  // 2. Option A: Get QR code string for camera scanning
170
180
  // (WhatsApp: Linked Devices | Telegram: Settings > Devices > Link Desktop Device)
171
181
  const qr = await client.getQR(); // or client.instances.getQR()
172
- console.log('QR DataURL:', qr.qr);
182
+ console.log("QR DataURL:", qr.qr);
173
183
 
174
184
  // 3. Option B: Request Phone Pairing / Login Code
175
185
  // (WhatsApp sends 8-digit PIN | Telegram sends verification code into app)
176
- const pair = await client.pairWithCode('+1234567890');
177
- console.log('Code request status:', pair);
186
+ const pair = await client.pairWithCode("+1234567890");
187
+ console.log("Code request status:", pair);
178
188
 
179
189
  // 4. Complete Telegram Phone Login (with optional 2FA cloud password)
180
- await client.instances.verifyCode('+1234567890', '12345', 'myCloudPassword2fa');
190
+ await client.instances.verifyCode("+1234567890", "12345", "myCloudPassword2fa");
181
191
 
182
192
  // 5. Socket engine controls
183
- await client.instances.start(); // Boots socket / MTProto connection
184
- await client.instances.stop(); // Suspends socket session
193
+ await client.instances.start(); // Boots socket / MTProto connection
194
+ await client.instances.stop(); // Suspends socket session
185
195
  await client.instances.logout(); // Unlinks device and clears session credentials
186
196
  ```
187
197
 
@@ -193,51 +203,51 @@ All messaging methods work across both **WhatsApp** and **Telegram**:
193
203
 
194
204
  ```typescript
195
205
  // 1. Text message
196
- await client.messages.sendText('+1234567890', 'Hello World', {
197
- mentions: ['1234567890'], // WhatsApp mentions
206
+ await client.messages.sendText("+1234567890", "Hello World", {
207
+ mentions: ["1234567890"], // WhatsApp mentions
198
208
  });
199
209
 
200
210
  // 2. Image / Photo
201
- await client.messages.sendImage('+1234567890', {
202
- url: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800',
203
- caption: 'Photo preview',
211
+ await client.messages.sendImage("+1234567890", {
212
+ url: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800",
213
+ caption: "Photo preview",
204
214
  });
205
215
 
206
216
  // 3. Video
207
- await client.messages.sendVideo('+1234567890', {
208
- url: 'https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4',
209
- caption: 'Product tutorial video',
217
+ await client.messages.sendVideo("+1234567890", {
218
+ url: "https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4",
219
+ caption: "Product tutorial video",
210
220
  });
211
221
 
212
222
  // 4. Voice note and audio
213
- await client.messages.sendVoice('+1234567890', {
214
- url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
223
+ await client.messages.sendVoice("+1234567890", {
224
+ url: "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
215
225
  });
216
226
 
217
227
  // 5. Document / PDF / File
218
- await client.messages.sendDocument('+1234567890', {
219
- url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
220
- fileName: 'Invoice_2026.pdf',
221
- caption: 'Monthly invoice statement',
228
+ await client.messages.sendDocument("+1234567890", {
229
+ url: "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
230
+ fileName: "Invoice_2026.pdf",
231
+ caption: "Monthly invoice statement",
222
232
  });
223
233
 
224
234
  // 6. Location coordinates
225
- await client.messages.sendLocation('+1234567890', {
235
+ await client.messages.sendLocation("+1234567890", {
226
236
  latitude: 24.7136,
227
237
  longitude: 46.6753,
228
- name: 'Kingdom Tower',
229
- address: 'King Fahd Rd, Riyadh, Saudi Arabia',
238
+ name: "Kingdom Tower",
239
+ address: "King Fahd Rd, Riyadh, Saudi Arabia",
230
240
  });
231
241
 
232
242
  // 7. Contact Card
233
- await client.messages.sendContact('+1234567890', {
234
- firstName: 'Support Agent',
235
- phone: '+1234567890',
243
+ await client.messages.sendContact("+1234567890", {
244
+ firstName: "Support Agent",
245
+ phone: "+1234567890",
236
246
  });
237
247
 
238
248
  // 8. Automated OTP verification code
239
- const otp = await client.messages.sendOTP('+1234567890', {
240
- appName: 'Botcast Portal',
249
+ const otp = await client.messages.sendOTP("+1234567890", {
250
+ appName: "Botcast Portal",
241
251
  expiryMinutes: 5,
242
252
  });
243
253
  ```
@@ -248,28 +258,30 @@ const otp = await client.messages.sendOTP('+1234567890', {
248
258
 
249
259
  ```typescript
250
260
  const msgKey = {
251
- remoteJid: '201012345678@s.whatsapp.net',
261
+ remoteJid: "201012345678@s.whatsapp.net",
252
262
  fromMe: true,
253
- id: '3EB0ABCDEF1234567890',
263
+ id: "3EB0ABCDEF1234567890",
254
264
  };
255
265
 
256
266
  // 1. Edit sent message
257
- await client.messages.edit('201012345678', msgKey, 'Corrected text content');
267
+ await client.messages.edit("201012345678", msgKey, "Corrected text content");
258
268
 
259
269
  // 2. Delete message for everyone (revoke)
260
- await client.messages.delete('201012345678', msgKey);
270
+ await client.messages.delete("201012345678", msgKey);
261
271
 
262
272
  // 3. Mark message as read
263
273
  await client.messages.markAsRead(msgKey);
264
274
 
265
275
  // 4. Emoji reaction
266
- await client.messages.react('201012345678', msgKey, '👍');
276
+ await client.messages.react("201012345678", msgKey, "👍");
267
277
 
268
278
  // 5. Pin message
269
- await client.messages.pin('201012345678', msgKey, 1, 86400);
279
+ await client.messages.pin("201012345678", msgKey, 1, 86400);
270
280
 
271
281
  // 6. Forward message
272
- await client.messages.forward('201012345678', { conversation: 'Forwarded message text' });
282
+ await client.messages.forward("201012345678", {
283
+ conversation: "Forwarded message text",
284
+ });
273
285
  ```
274
286
 
275
287
  ---
@@ -284,12 +296,15 @@ const { chats } = await client.chats.list({ limit: 50 });
284
296
  const { contacts } = await client.chats.listContacts({ limit: 100 });
285
297
 
286
298
  // 3. List messages for a chat
287
- const { messages } = await client.chats.listMessages({ chatJid: '201012345678@s.whatsapp.net', limit: 50 });
299
+ const { messages } = await client.chats.listMessages({
300
+ chatJid: "201012345678@s.whatsapp.net",
301
+ limit: 50,
302
+ });
288
303
 
289
304
  // 4. Modify WhatsApp chat state (archive, mute, pin, delete)
290
- await client.chats.archive('201012345678@s.whatsapp.net');
291
- await client.chats.mute('201012345678@s.whatsapp.net', 8 * 60 * 60 * 1000); // 8 hours
292
- await client.chats.pin('201012345678@s.whatsapp.net');
305
+ await client.chats.archive("201012345678@s.whatsapp.net");
306
+ await client.chats.mute("201012345678@s.whatsapp.net", 8 * 60 * 60 * 1000); // 8 hours
307
+ await client.chats.pin("201012345678@s.whatsapp.net");
293
308
  ```
294
309
 
295
310
  ---
@@ -301,24 +316,26 @@ The SDK includes a built-in event dispatcher and middleware for handling inbound
301
316
  #### Express Example
302
317
 
303
318
  ```typescript
304
- import express from 'express';
305
- import { BotcastClient } from 'botcast-sdk';
319
+ import express from "express";
320
+ import { BotcastClient } from "botcast-sdk";
306
321
 
307
322
  const app = express();
308
323
  app.use(express.json());
309
324
 
310
325
  const client = new BotcastClient({
311
- platform: 'telegram', // or 'whatsapp'
312
- instanceId: 'inst_123',
313
- instanceToken: 'tok_abc',
326
+ platform: "telegram", // or 'whatsapp'
327
+ instanceId: "inst_123",
328
+ instanceToken: "tok_abc",
314
329
  });
315
330
 
316
331
  // Listen for incoming messages
317
332
  client.webhook.onMessage(async (event) => {
318
- console.log(`Message from ${event.sender}: ${event.message} [Platform: ${event.platform || 'whatsapp'}]`);
333
+ console.log(
334
+ `Message from ${event.sender}: ${event.message} [Platform: ${event.platform || "whatsapp"}]`,
335
+ );
319
336
 
320
- if (event.message.trim().toLowerCase() === 'ping') {
321
- await client.messages.sendText(event.sender, 'pong');
337
+ if (event.message.trim().toLowerCase() === "ping") {
338
+ await client.messages.sendText(event.sender, "pong");
322
339
  }
323
340
  });
324
341
 
@@ -328,9 +345,9 @@ client.webhook.onConnection(async (event) => {
328
345
  });
329
346
 
330
347
  // Mount middleware on your webhook route
331
- app.post('/webhook/botcast', client.webhook.middleware());
348
+ app.post("/webhook/botcast", client.webhook.middleware());
332
349
 
333
- app.listen(3000, () => console.log('Webhook server running on port 3000'));
350
+ app.listen(3000, () => console.log("Webhook server running on port 3000"));
334
351
  ```
335
352
 
336
353
  ---
@@ -348,25 +365,25 @@ import {
348
365
  BotcastRateLimitError,
349
366
  BotcastValidationError,
350
367
  BotcastNotFoundError,
351
- } from 'botcast-sdk';
368
+ } from "botcast-sdk";
352
369
 
353
370
  try {
354
- await client.messages.sendText('+1234567890', 'Hello World');
371
+ await client.messages.sendText("+1234567890", "Hello World");
355
372
  } catch (error) {
356
373
  if (error instanceof BotcastAuthError) {
357
- console.error('Invalid instance credentials or token.');
374
+ console.error("Invalid instance credentials or token.");
358
375
  } else if (error instanceof BotcastPaymentRequiredError) {
359
- console.error('Instance requires payment confirmation.');
376
+ console.error("Instance requires payment confirmation.");
360
377
  } else if (error instanceof BotcastSubscriptionExpiredError) {
361
- console.error('Subscription expired on:', error.expiresAt);
378
+ console.error("Subscription expired on:", error.expiresAt);
362
379
  } else if (error instanceof BotcastRateLimitError) {
363
- console.error('Daily message limit reached.');
380
+ console.error("Daily message limit reached.");
364
381
  } else if (error instanceof BotcastValidationError) {
365
- console.error('Validation error:', error.message, error.details);
382
+ console.error("Validation error:", error.message, error.details);
366
383
  } else if (error instanceof BotcastNotFoundError) {
367
- console.error('Resource not found:', error.message);
384
+ console.error("Resource not found:", error.message);
368
385
  } else {
369
- console.error('Unexpected error:', error);
386
+ console.error("Unexpected error:", error);
370
387
  }
371
388
  }
372
389
  ```
@@ -378,7 +395,7 @@ try {
378
395
  - Website: [https://botcast.site](https://botcast.site)
379
396
  - Documentation: [https://botcast.site/docs](https://botcast.site/docs)
380
397
  - NPM Package: [https://www.npmjs.com/package/botcast-sdk](https://www.npmjs.com/package/botcast-sdk)
381
- - GitHub Repository: [https://github.com/ANOOBALSA/Botcast](https://github.com/ANOOBALSA/Botcast)
398
+ - GitHub Repository: [https://github.com/alsasoft-web/Botcast](https://github.com/alsasoft-web/Botcast)
382
399
 
383
400
  ---
384
401
 
package/dist/admin.cjs CHANGED
@@ -65,6 +65,12 @@ class BotcastAdminClient {
65
65
  * Admins are zero-payment, so instances are provisioned as active paid instances.
66
66
  */
67
67
  async createInstance(options) {
68
+ const rawExp = options.expires_at || options.expiresAt;
69
+ const expires_at = rawExp
70
+ ? rawExp instanceof Date
71
+ ? rawExp.toISOString()
72
+ : new Date(rawExp).toISOString()
73
+ : undefined;
68
74
  return this.request('/api/instances', {
69
75
  method: 'POST',
70
76
  body: JSON.stringify({
@@ -75,29 +81,60 @@ class BotcastAdminClient {
75
81
  allowed_numbers: options.allowed_numbers || [],
76
82
  webhook_url: options.webhook_url || '',
77
83
  target_user_id: options.target_user_id,
84
+ expires_at,
78
85
  }),
79
86
  });
80
87
  }
81
88
  /**
82
89
  * Programmatically renews an instance's subscription without payment transactions.
90
+ * Supports either extending by months or setting an exact expiration date.
83
91
  */
84
92
  async renewInstance(instanceId, options) {
93
+ const rawExp = options?.expires_at || options?.expiresAt;
94
+ const expires_at = rawExp
95
+ ? rawExp instanceof Date
96
+ ? rawExp.toISOString()
97
+ : new Date(rawExp).toISOString()
98
+ : undefined;
85
99
  return this.request(`/api/instances/${instanceId}/renew`, {
86
100
  method: 'POST',
87
101
  body: JSON.stringify({
88
- months: options?.months || options?.plan_months || 12,
102
+ months: options?.months || options?.plan_months || (expires_at ? undefined : 12),
103
+ expires_at,
89
104
  }),
90
105
  });
91
106
  }
92
107
  /**
93
- * Updates instance configuration (name, webhook URL, allowed recipient numbers).
108
+ * Updates instance configuration (name, webhook URL, allowed recipient numbers, expiration date).
94
109
  */
95
110
  async updateInstance(instanceId, updates) {
111
+ const rawExp = updates.expires_at !== undefined ? updates.expires_at : updates.expiresAt;
112
+ let expires_at = undefined;
113
+ if (rawExp !== undefined) {
114
+ if (rawExp === null) {
115
+ expires_at = null;
116
+ }
117
+ else {
118
+ expires_at = rawExp instanceof Date ? rawExp.toISOString() : new Date(rawExp).toISOString();
119
+ }
120
+ }
96
121
  return this.request(`/api/instances/${instanceId}`, {
97
122
  method: 'PUT',
98
- body: JSON.stringify(updates),
123
+ body: JSON.stringify({
124
+ ...updates,
125
+ expires_at,
126
+ }),
99
127
  });
100
128
  }
129
+ /**
130
+ * Directly sets or clears the exact expiration date for an instance.
131
+ *
132
+ * @param instanceId Target instance ID (e.g. `ins_paid_...`)
133
+ * @param expiresAt Date instance, ISO string (e.g. `2027-01-01T00:00:00.000Z`), or `null` to remove expiry.
134
+ */
135
+ async setExpiration(instanceId, expiresAt) {
136
+ return this.updateInstance(instanceId, { expires_at: expiresAt });
137
+ }
101
138
  /**
102
139
  * Regenerates secret instance token for an instance.
103
140
  */
package/dist/admin.d.ts CHANGED
@@ -12,6 +12,8 @@ export interface CreateAdminInstanceOptions {
12
12
  allowed_numbers?: string[];
13
13
  webhook_url?: string;
14
14
  target_user_id?: number;
15
+ expires_at?: string | Date;
16
+ expiresAt?: string | Date;
15
17
  }
16
18
  export interface AdminInstanceRecord {
17
19
  id: string;
@@ -59,26 +61,41 @@ export declare class BotcastAdminClient {
59
61
  }>;
60
62
  /**
61
63
  * Programmatically renews an instance's subscription without payment transactions.
64
+ * Supports either extending by months or setting an exact expiration date.
62
65
  */
63
66
  renewInstance(instanceId: string, options?: {
64
67
  months?: number;
65
68
  plan_months?: number;
69
+ expires_at?: string | Date;
70
+ expiresAt?: string | Date;
66
71
  }): Promise<{
67
72
  success: boolean;
68
73
  message: string;
69
74
  instance: AdminInstanceRecord;
70
75
  }>;
71
76
  /**
72
- * Updates instance configuration (name, webhook URL, allowed recipient numbers).
77
+ * Updates instance configuration (name, webhook URL, allowed recipient numbers, expiration date).
73
78
  */
74
79
  updateInstance(instanceId: string, updates: {
75
80
  name?: string;
76
81
  webhook_url?: string;
77
82
  allowed_numbers?: string[];
83
+ expires_at?: string | Date | null;
84
+ expiresAt?: string | Date | null;
78
85
  }): Promise<{
79
86
  message: string;
80
87
  instance: AdminInstanceRecord;
81
88
  }>;
89
+ /**
90
+ * Directly sets or clears the exact expiration date for an instance.
91
+ *
92
+ * @param instanceId Target instance ID (e.g. `ins_paid_...`)
93
+ * @param expiresAt Date instance, ISO string (e.g. `2027-01-01T00:00:00.000Z`), or `null` to remove expiry.
94
+ */
95
+ setExpiration(instanceId: string, expiresAt: string | Date | null): Promise<{
96
+ message: string;
97
+ instance: AdminInstanceRecord;
98
+ }>;
82
99
  /**
83
100
  * Regenerates secret instance token for an instance.
84
101
  */
@@ -1 +1 @@
1
- {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/admin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/admin.ts"],"names":[],"mappings":"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"}
package/dist/admin.js CHANGED
@@ -62,6 +62,12 @@ export class BotcastAdminClient {
62
62
  * Admins are zero-payment, so instances are provisioned as active paid instances.
63
63
  */
64
64
  async createInstance(options) {
65
+ const rawExp = options.expires_at || options.expiresAt;
66
+ const expires_at = rawExp
67
+ ? rawExp instanceof Date
68
+ ? rawExp.toISOString()
69
+ : new Date(rawExp).toISOString()
70
+ : undefined;
65
71
  return this.request('/api/instances', {
66
72
  method: 'POST',
67
73
  body: JSON.stringify({
@@ -72,29 +78,60 @@ export class BotcastAdminClient {
72
78
  allowed_numbers: options.allowed_numbers || [],
73
79
  webhook_url: options.webhook_url || '',
74
80
  target_user_id: options.target_user_id,
81
+ expires_at,
75
82
  }),
76
83
  });
77
84
  }
78
85
  /**
79
86
  * Programmatically renews an instance's subscription without payment transactions.
87
+ * Supports either extending by months or setting an exact expiration date.
80
88
  */
81
89
  async renewInstance(instanceId, options) {
90
+ const rawExp = options?.expires_at || options?.expiresAt;
91
+ const expires_at = rawExp
92
+ ? rawExp instanceof Date
93
+ ? rawExp.toISOString()
94
+ : new Date(rawExp).toISOString()
95
+ : undefined;
82
96
  return this.request(`/api/instances/${instanceId}/renew`, {
83
97
  method: 'POST',
84
98
  body: JSON.stringify({
85
- months: options?.months || options?.plan_months || 12,
99
+ months: options?.months || options?.plan_months || (expires_at ? undefined : 12),
100
+ expires_at,
86
101
  }),
87
102
  });
88
103
  }
89
104
  /**
90
- * Updates instance configuration (name, webhook URL, allowed recipient numbers).
105
+ * Updates instance configuration (name, webhook URL, allowed recipient numbers, expiration date).
91
106
  */
92
107
  async updateInstance(instanceId, updates) {
108
+ const rawExp = updates.expires_at !== undefined ? updates.expires_at : updates.expiresAt;
109
+ let expires_at = undefined;
110
+ if (rawExp !== undefined) {
111
+ if (rawExp === null) {
112
+ expires_at = null;
113
+ }
114
+ else {
115
+ expires_at = rawExp instanceof Date ? rawExp.toISOString() : new Date(rawExp).toISOString();
116
+ }
117
+ }
93
118
  return this.request(`/api/instances/${instanceId}`, {
94
119
  method: 'PUT',
95
- body: JSON.stringify(updates),
120
+ body: JSON.stringify({
121
+ ...updates,
122
+ expires_at,
123
+ }),
96
124
  });
97
125
  }
126
+ /**
127
+ * Directly sets or clears the exact expiration date for an instance.
128
+ *
129
+ * @param instanceId Target instance ID (e.g. `ins_paid_...`)
130
+ * @param expiresAt Date instance, ISO string (e.g. `2027-01-01T00:00:00.000Z`), or `null` to remove expiry.
131
+ */
132
+ async setExpiration(instanceId, expiresAt) {
133
+ return this.updateInstance(instanceId, { expires_at: expiresAt });
134
+ }
98
135
  /**
99
136
  * Regenerates secret instance token for an instance.
100
137
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botcast-sdk",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Official TypeScript/JavaScript SDK for Botcast WhatsApp & Telegram MTProto Multi-Instance API Gateway",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -51,7 +51,7 @@
51
51
  "homepage": "https://botcast.site",
52
52
  "repository": {
53
53
  "type": "git",
54
- "url": "git+https://github.com/ANOOBALSA/botcast-sdk.git",
54
+ "url": "git+https://github.com/alsasoft-web/botcast-sdk.git",
55
55
  "directory": "packages/botcast-sdk"
56
56
  },
57
57
  "bugs": {