Skip to content

Commit fbac1b5

Browse files
committed
[commands] add batch command
1 parent 42dccc3 commit fbac1b5

14 files changed

Lines changed: 1039 additions & 151 deletions

File tree

‎Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Start from the official Go image
2-
FROM golang:1.23-alpine AS builder
2+
FROM golang:1.25-alpine AS builder
33

44
# Set the working directory inside the container
55
WORKDIR /app

‎README.md‎

Lines changed: 174 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
- [Exit codes](#exit-codes)
4242
- [Examples](#examples)
4343
- [Sending messages](#sending-messages)
44+
- [Batch message sending](#batch-message-sending)
4445
- [Getting message status](#getting-message-status)
4546
- [Getting logs](#getting-logs)
4647
- [Output formats](#output-formats-1)
@@ -55,7 +56,7 @@
5556
There are two CLI tools in this repository: `smsgate` and `smsgate-ca`. The first one is for SMS Gateway for Android itself, and the second one is for the Certificate Authority.
5657

5758
This CLI provides a robust interface for:
58-
- Sending and managing SMS messages
59+
- Sending and managing SMS messages (including batch operations from CSV and Excel files)
5960
- Configuring webhook integrations
6061
- Issuing certificates for private deployments
6162

@@ -71,7 +72,7 @@ This CLI provides a robust interface for:
7172

7273
### Prerequisites
7374

74-
- Go 1.23+ (for building from source)
75+
- Go 1.25+ (for building from source)
7576
- Docker (optional, for containerized execution)
7677

7778
### Installation
@@ -135,7 +136,7 @@ smsgate [global options] command [command options] [arguments...]
135136

136137
The CLI offers three main groups of commands:
137138

138-
- **Messages**: Commands for sending messages and checking their status.
139+
- **Messages**: Commands for sending messages and checking their status, including batch operations from CSV and Excel files.
139140
- **Webhooks**: Commands for managing webhooks, including creating, updating, and deleting them.
140141
- **Logs**: Commands for retrieving logs for a specific time range.
141142

@@ -164,16 +165,184 @@ smsgate -u <username> -p <password> send --phones '+12025550123' 'Hello, Dr. Tur
164165

165166
#### Sending messages
166167

168+
The `send` command supports various options to customize message delivery:
169+
167170
```bash
168-
# Send a message
171+
# Send a simple text message
169172
smsgate send --phones '+12025550123' 'Hello, Dr. Turk!'
170173

171-
# Send a message to multiple numbers
174+
# Send to multiple numbers
172175
smsgate send --phones '+12025550123' --phones '+12025550124' 'Hello, doctors!'
173176
# or
174177
smsgate send --phones '+12025550123,+12025550124' 'Hello, doctors!'
178+
179+
# Send with explicit device selection
180+
smsgate send --phones '+12025550123' --device-id device123 'Message'
181+
182+
# Send with SIM number selection (1-based)
183+
smsgate send --phones '+12025550123' --sim-number 2 'Message'
184+
185+
# Send with priority (>=100 bypasses limits)
186+
smsgate send --phones '+12025550123' --priority 100 'Urgent message'
187+
188+
# Send with time-to-live (TTL)
189+
smsgate send --phones '+12025550123' --ttl 1h30m 'Expiring message'
190+
191+
# Send with expiration date (RFC3339 format)
192+
smsgate send --phones '+12025550123' --valid-until '2024-12-31T23:59:59Z' 'Message'
193+
194+
# Disable delivery report
195+
smsgate send --phones '+12025550123' --delivery-report=false 'Message'
196+
197+
# Skip phone number validation
198+
smsgate send --phones '+12025550123' --skip-phone-validation 'Message'
199+
200+
# Filter by device activity (devices active within last 12 hours)
201+
smsgate send --phones '+12025550123' --device-active-within 12 'Message'
202+
203+
# Send data message (base64 encoded)
204+
echo -n 'hello world' | base64
205+
smsgate send --phones '+12025550123' --data --data-port 12345 'aGVsbG8gd29ybGQ='
206+
```
207+
208+
**Send command options:**
209+
210+
| Option | Description | Default Value | Example |
211+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ----------------------- |
212+
| `--id` | A unique message ID. If not provided, one will be automatically generated. | empty | `zXDYfTmTVf3iMd16zzdBj` |
213+
| `--device-id`, `--device` | Optional device ID for explicit selection. If not provided, a random device will be selected. | empty | `oi2i20J8xVP1ct5neqGZt` |
214+
| `--phones`, `--phone`, `-p` | Specifies the recipient's phone number(s). This option can be used multiple times or accepts comma-separated values. Numbers must be in E.164 format. | **required** | `+12025550123` |
215+
| `--sim-number`, `--sim` | The one-based SIM card slot number. If not specified, the device's SIM rotation feature will be used. | empty | `2` |
216+
| `--delivery-report` | Enables delivery report for the message. | `true` | `true` / `false` |
217+
| `--priority` | Sets the priority of the message. Messages with priority >= 100 bypass all limits and delays. Range: -128 to 127. | `0` | `100` |
218+
| **Data Message** | | | |
219+
| `--data` | Send data message instead of text (content must be base64 encoded). | `false` | `true` |
220+
| `--data-port` | Destination port for data message (1 to 65535). | `53739` | `12345` |
221+
| **Options** | | | |
222+
| `--ttl` | Time-to-live (TTL) for the message. Duration format (e.g., `1h30m`). If not provided, the message will not expire.<br>**Conflicts with `--valid-until`.** | empty | `1h30m` |
223+
| `--valid-until` | The expiration date and time for the message. RFC3339 format (e.g., `2006-01-02T15:04:05Z07:00`).<br>**Conflicts with `--ttl`.** | empty | `2024-12-31T23:59:59Z` |
224+
| `--skip-phone-validation` | Skip phone number validation. | `false` | `true` |
225+
| `--device-active-within` | Time window in hours for device activity filtering. `0` means no filtering. | `0` | `12` |
226+
227+
#### Batch message sending
228+
229+
The CLI supports sending messages in bulk from CSV and Excel files. The `batch send` command also supports the shared delivery/device options from `send` (such as `--device-id`, `--sim-number`, `--priority`, `--ttl`, `--valid-until`, `--delivery-report`, `--skip-phone-validation`, `--device-active-within`), allowing fine-grained control over each message in the batch.
230+
231+
**Supported file formats:**
232+
- **CSV** (Comma-Separated Values)
233+
- **XLSX** (Excel files)
234+
235+
**Basic usage:**
236+
237+
```bash
238+
# Send messages from a CSV file
239+
smsgate batch send contacts.csv --map phone=Phone,text=Message
240+
241+
# Send messages from an Excel file with specific sheet
242+
smsgate batch send campaign.xlsx --sheet Sheet1 --map phone=Phone,text=Message
243+
244+
# Send with custom delimiter and no header
245+
smsgate batch send data.csv --delimiter ';' --header=false --map phone=col_1,text=col_2
246+
```
247+
248+
**Batch-specific options:**
249+
250+
| Option | Description | Default Value | Example |
251+
| --------------------- | -------------------------------------------------- | ------------- | -------------------------- |
252+
| `--sheet` | Sheet name (defaults to first sheet) | empty | `Sheet1` |
253+
| `--delimiter` | CSV delimiter character | `,` | `;` |
254+
| `--header` | Treat first row as header | `true` | `false` |
255+
| `--map` | Column mapping (required) | **required** | `phone=Phone,text=Message` |
256+
| `--dry-run` | Validate and print normalized rows without sending | `false` | `true` |
257+
| `--validate-only` | Validate input only (no preview, no sending) | `false` | `true` |
258+
| `--concurrency` | Number of concurrent send workers | CPU cores | `5` |
259+
| `--continue-on-error` | Continue sending after per-row failures | `false` | `true` |
260+
261+
**Inherited message options:**
262+
The shared delivery/device options from the `send` command are also available (e.g., `--device-id`, `--sim-number`, `--priority`, `--ttl`, `--valid-until`, `--delivery-report`, `--skip-phone-validation`, `--device-active-within`). These apply to every message sent in the batch.
263+
264+
**Column mapping:**
265+
266+
The `--map` option defines how columns in your file map to message fields:
267+
268+
| Field | Required | Description |
269+
| ------------ | -------- | --------------------------------------------- |
270+
| `phone` | ✅ | Phone number column |
271+
| `text` | ✅ | Message text column |
272+
| `id` | ❌ | Message ID column (UUID generated when empty) |
273+
| `device_id` | ❌ | Device identifier column |
274+
| `sim_number` | ❌ | SIM number column (1-255) |
275+
| `priority` | ❌ | Message priority column (-128 to 127) |
276+
277+
**Mapping examples:**
278+
279+
```bash
280+
# Basic mapping with headers
281+
smsgate batch send --map phone=Phone,text=Message contacts.csv
282+
283+
# Excel file with specific sheet
284+
smsgate batch send --sheet Sheet1 --map phone=Phone,text=Message campaign.xlsx
285+
286+
# No headers, column positions
287+
smsgate batch send --header=false --map phone=col_1,text=col_2 data.csv
288+
289+
# Full mapping with optional fields
290+
smsgate batch send --map phone=Phone,text=Message,device_id=Device,sim_number=SIM,priority=Priority contacts.csv
291+
```
292+
293+
**File format examples:**
294+
295+
**CSV with Headers:**
296+
```csv
297+
Phone,Message,Device,Priority
298+
+12025550123,"Hello Dr. Turk!",device1,1
299+
+12025550124,"Hello Dr. Smith!",device1,2
300+
+12025550125,"Hello Dr. Jones!",device2,1
301+
```
302+
303+
**CSV without Headers:**
304+
```csv
305+
+12025550123,"Hello Dr. Turk!",device1,1
306+
+12025550124,"Hello Dr. Smith!",device1,2
307+
+12025550125,"Hello Dr. Jones!",device2,1
175308
```
176309

310+
**Excel Files:**
311+
- Supports multiple sheets (use `--sheet` to specify)
312+
- First row treated as headers by default
313+
- Column mapping works the same as CSV
314+
315+
**Workflow modes:**
316+
317+
1. **Validation Only** - Validates file format and column mapping, checks required fields, exits without sending:
318+
```bash
319+
smsgate batch send --map phone=Phone,text=Message --validate-only contacts.csv
320+
```
321+
322+
2. **Dry Run** - Validates and processes all rows, shows what would be sent without actually sending:
323+
```bash
324+
smsgate batch send --map phone=Phone,text=Message --dry-run contacts.csv
325+
```
326+
327+
3. **Full Send** - Sends all messages with real-time progress:
328+
```bash
329+
smsgate batch send --map phone=Phone,text=Message --concurrency=5 contacts.csv
330+
```
331+
332+
**Output and error handling:**
333+
334+
- **Summary**: `Batch send summary: total=100 enqueued=95 failed=3 skipped=2`
335+
- **Real-time progress**: Shows each message's UUID and state during sending
336+
- **Error handling**: By default stops on first error; use `--continue-on-error` to send all rows even if some fail
337+
338+
**Best practices:**
339+
340+
1. Always use `--dry-run` or `--validate-only` first to test your configuration
341+
2. Ensure phone numbers are in E.164 format
342+
3. Start with lower concurrency values and increase as needed
343+
4. Use `--continue-on-error` for non-critical bulk sends
344+
5. Combine with inherited message options (e.g., `--device-id`, `--priority`) for advanced scenarios
345+
177346
#### Getting message status
178347

179348
```bash

‎internal/commands/flags/errors.go‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package flags
2+
3+
import "errors"
4+
5+
var (
6+
ErrValidationFailed = errors.New("validation failed")
7+
)

0 commit comments

Comments
 (0)