Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4ae3f53
feat: Implement chat auto-reply feature
google-labs-jules[bot] May 26, 2025
83015fc
Merge pull request #1 from rakshabesafe/feat/chat-autoreply
rakshabesafe May 26, 2025
2994d3a
Update main.go
rakshabesafe May 26, 2025
fa0a96c
Update Dockerfile
rakshabesafe May 26, 2025
291892c
feat: Implement WhatsApp autoreply feature
google-labs-jules[bot] May 26, 2025
4935c8a
Merge pull request #2 from rakshabesafe/feature/autoreply
rakshabesafe May 26, 2025
ab23e53
Update main.go
rakshabesafe May 26, 2025
b945ed9
Update wmiau.go
rakshabesafe May 26, 2025
7c6f1de
Create adduser.sh
rakshabesafe May 26, 2025
a396f37
feat: Implement autoreply modes functionality
google-labs-jules[bot] May 26, 2025
f0d20e0
feat: Implement autoreply modes and Google Contacts integration
google-labs-jules[bot] Jun 1, 2025
0fcf450
fix: Add missing strings import to db.go
google-labs-jules[bot] Jun 5, 2025
524269b
feat: Implement live Google Contacts fetching for autoreply modes
google-labs-jules[bot] Jun 5, 2025
226c815
refactor: Standardize autoreply URLs and Swagger tags
google-labs-jules[bot] Jun 5, 2025
b07092d
fix: Add missing io import to handlers.go
google-labs-jules[bot] Jun 5, 2025
b1a9405
fix: Correct database table creation order
google-labs-jules[bot] Jun 5, 2025
9b01d1e
refactor: Remove unused strings import from db.go
google-labs-jules[bot] Jun 5, 2025
8ce1e48
I've addressed an issue in the migrations.go file. It was using `log.…
google-labs-jules[bot] Jun 5, 2025
4b404b3
fix: Remove duplicate AutoReplyEntry definition in spec.yml
google-labs-jules[bot] Jun 5, 2025
a56416a
refactor: Isolate Google Contacts logic into separate files
google-labs-jules[bot] Jun 5, 2025
315b617
fix: Correct syntax errors in handlers
google-labs-jules[bot] Jun 5, 2025
a63eaef
fix: Resolve redeclaration and usage errors
google-labs-jules[bot] Jun 5, 2025
a8cb87a
Jules was unable to complete the task in time. Please review the work…
google-labs-jules[bot] Jun 6, 2025
ea9e125
refactor: Consolidate all handlers into handlers.go for upstream alig…
google-labs-jules[bot] Jun 7, 2025
cf13b53
fix: Replace respondWithJSON with Respond in admin handlers
google-labs-jules[bot] Jun 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,64 @@ curl -s -X POST -H 'Token: 1234ABCD' -H 'Content-Type: application/json' --data

---

### Add/Update Auto-Reply
* **Method:** `POST`
* **Path:** `/chat/autoreply`
* **Description:** Sets up an auto-reply for a specific phone number. If an auto-reply for the given phone number already exists for the user, it will be updated. If not, a new one will be created.
* **Authentication:** Requires user token.
* **Request Body (JSON):**
```json
{
"Phone": "1234567890", // Target phone number (normalized, e.g., digits only or international format used by the system)
"Body": "Hello! I am currently unavailable and will get back to you soon."
}
```
* **Responses:**
* `201 Created`: If a new auto-reply was successfully created.
```json
{
"code": 201,
"data": {
"detail": "Auto-reply added successfully",
"id": "generated-unique-id-for-the-rule"
},
"success": true
}
```
* `400 Bad Request`: If `Phone` or `Body` is missing or invalid.
* `409 Conflict`: If an auto-reply for this phone number already exists for the user.
Comment on lines +711 to +733

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The description for Add/Update Auto-Reply (line 711) states, "If an auto-reply for the given phone number already exists for the user, it will be updated." However, the 409 Conflict response (line 733) is documented for the case "If an auto-reply for this phone number already exists for the user."

This seems contradictory. The current handler implementation (handlers.go:AddAutoReply) also reflects an "add-only" behavior, returning a 409 Conflict if the entry exists, rather than updating it.

Could you clarify the intended behavior?

  • If it's "add-only", the description on line 711 should be updated to reflect this, and the 409 Conflict is appropriate.
  • If it's meant to be an "add-or-update" (upsert) operation, the handler logic in handlers.go would need to be changed (e.g., using ON CONFLICT DO UPDATE for PostgreSQL and INSERT OR REPLACE or equivalent for SQLite), and the success status code might need adjustment (e.g., 200 OK for update, 201 Created for new).

* `500 Internal Server Error`: For other server-side errors.

---

### Delete Auto-Reply
* **Method:** `DELETE`
* **Path:** `/chat/autoreply`
* **Description:** Deletes an existing auto-reply for a specific phone number for the authenticated user.
* **Authentication:** Requires user token.
* **Request Body (JSON):**
```json
{
"Phone": "1234567890" // Target phone number whose auto-reply rule should be deleted.
}
```
* **Responses:**
* `200 OK`: If the auto-reply was successfully deleted.
```json
{
"code": 200,
"data": {
"detail": "Auto-reply deleted successfully"
},
"success": true
}
```
* `400 Bad Request`: If `Phone` is missing or invalid.
* `404 Not Found`: If no auto-reply rule exists for the given phone number for this user.
* `500 Internal Server Error`: For other server-side errors.

---

## Download Document

Downloads a Document from a message and retrieves it Base64 media encoded. Required request parameters are: Url, MediaKey, Mimetype, FileSHA256 and FileLength
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ RUN apk update && apk add --no-cache \
ffmpeg \
tzdata

ENV TZ="America/Sao_Paulo"
ENV TZ="Asia/Kolkata"
WORKDIR /app

COPY --from=builder /app/wuzapi /app/
Expand All @@ -34,4 +34,4 @@ RUN chown -R root:root /app

VOLUME [ "/app/dbdata", "/app/files" ]

ENTRYPOINT ["/app/wuzapi", "--logtype=console", "--color=true"]
ENTRYPOINT ["/app/wuzapi", "--logtype=console", "--color=true"]
22 changes: 22 additions & 0 deletions adduser.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

# Script to create a user via curl

# Validate the number of arguments
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <adminpass> <username> <passwd>"
exit 1
fi

# Assign arguments to variables
ADMIN_PASS="$1"
USERNAME="$2"
USER_PASSWD="$3"

# Execute the curl command
curl -X POST http://localhost:8089/admin/users \
-H "Authorization: ${ADMIN_PASS}" \
-H "Content-Type: application/json" \
-d "{\"name\": \"${USERNAME}\", \"token\": \"${USER_PASSWD}\"}"

echo # Add a newline for cleaner output
98 changes: 94 additions & 4 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,101 @@ func InitializeDatabase(exPath string) (*sqlx.DB, error) {
config := getDatabaseConfig(exPath)

if config.Type == "postgres" {
return initializePostgres(config)
db, err := initializePostgres(config)
if err != nil {
return nil, err
}
// Create tables for postgres
if err := createTables(db, "postgres"); err != nil {
return nil, fmt.Errorf("failed to create tables for postgres: %w", err)
}
return db, nil
}

// Default to SQLite
db, err := initializeSQLite(config)
if err != nil {
return nil, err
}
return initializeSQLite(config)
// Create tables for sqlite
if err := createTables(db, "sqlite"); err != nil {
return nil, fmt.Errorf("failed to create tables for sqlite: %w", err)
}
return db, nil
}

func createTables(db *sqlx.DB, dbType string) error {
// SQL for creating autoreply_modes table
autoreplyModesTableSQL := `
CREATE TABLE IF NOT EXISTS autoreply_modes (
user_id TEXT NOT NULL,
mode_name TEXT NOT NULL,
phone_number TEXT NOT NULL,
message TEXT NOT NULL,
UNIQUE (user_id, mode_name, phone_number)
);`

// SQL for creating active_mode table
activeModeTableSQL := `
CREATE TABLE IF NOT EXISTS active_mode (
user_id TEXT PRIMARY KEY NOT NULL,
current_mode_name TEXT NULLABLE
);`

// Execute table creation statements
if _, err := db.Exec(autoreplyModesTableSQL); err != nil {
return fmt.Errorf("failed to create autoreply_modes table: %w", err)
}
if _, err := db.Exec(activeModeTableSQL); err != nil {
return fmt.Errorf("failed to create active_mode table: %w", err)
}

// No initial data for active_mode as it's user-specific and populated on demand.

// Alter users table to add google_contacts_auth_token column
alterUsersTableSQL := ""
if dbType == "postgres" {
alterUsersTableSQL = `ALTER TABLE users ADD COLUMN IF NOT EXISTS google_contacts_auth_token TEXT;`
} else { // sqlite
// Check if column exists first for older SQLite versions.
// For newer SQLite (3.16.0+), ADD COLUMN is idempotent if the column exists.
// However, to be safe and support potentially older versions, we can check.
// A simpler approach for tests or if newer SQLite is guaranteed is just:
// alterUsersTableSQL = `ALTER TABLE users ADD COLUMN google_contacts_auth_token TEXT;`
// For robustness in a production-like environment, checking PRAGMA is better.

// Let's use the simpler ALTER TABLE for now, assuming modern SQLite.
// If this causes issues, a PRAGMA check can be added.
alterUsersTableSQL = `ALTER TABLE users ADD COLUMN google_contacts_auth_token TEXT;`

// Check if column exists to avoid error on re-run with older SQLite
var columnName string
query := "SELECT name FROM pragma_table_info('users') WHERE name = 'google_contacts_auth_token';"
err := db.Get(&columnName, query)
if err == nil && columnName == "google_contacts_auth_token" {
// Column already exists, no need to alter
alterUsersTableSQL = ""
} else if err != nil && err.Error() != "sql: no rows in result set" {
// An actual error occurred querying pragma_table_info
return fmt.Errorf("failed to check users table schema: %w", err)
}
// If err is "sql: no rows in result set", column doesn't exist, proceed with ALTER.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current logic for adding the google_contacts_auth_token column to the users table in SQLite (lines 92-106) executes the ALTER TABLE users ADD COLUMN google_contacts_auth_token TEXT; statement (line 92) before checking if the column already exists using the PRAGMA table_info query (lines 96-105).

If the column already exists and you're using an older SQLite version that doesn't make ADD COLUMN idempotent, the initial ALTER TABLE statement might fail. The subsequent PRAGMA check then attempts to set alterUsersTableSQL = "", but this occurs after the potential failure. While there's error handling later (lines 112-114) to catch "duplicate column name", it might be more robust to prevent this error.

Would you consider restructuring this to check for the column's existence with PRAGMA first, and only then conditionally execute the ALTER TABLE statement if the column is found to be missing? This could make the migration logic cleaner and less reliant on specific error message strings.

For example, the logic could be:

	alterUsersTableSQL := ""
	if dbType == "postgres" {
		alterUsersTableSQL = `ALTER TABLE users ADD COLUMN IF NOT EXISTS google_contacts_auth_token TEXT;`
	} else { // sqlite
		var columnName string
		query := "SELECT name FROM pragma_table_info('users') WHERE name = 'google_contacts_auth_token';"
		err := db.Get(&columnName, query)
		// If err is sql.ErrNoRows, column doesn't exist. If err is nil and columnName matches, it exists.
		if err == sql.ErrNoRows || (err == nil && columnName != "google_contacts_auth_token") {
			// Column does not exist, or a different error occurred during the check (which is handled if err != sql.ErrNoRows below)
			alterUsersTableSQL = `ALTER TABLE users ADD COLUMN google_contacts_auth_token TEXT;`
		} else if err != nil && err.Error() != "sql: no rows in result set" {
			// An actual error occurred querying pragma_table_info, not just 'no rows'
			return fmt.Errorf("failed to check users table schema: %w", err)
		}
		// If err is nil and columnName matched, column exists, alterUsersTableSQL remains ""
	}

	if alterUsersTableSQL != "" {
		if _, err := db.Exec(alterUsersTableSQL); err != nil {
			// Handle potential errors, e.g., if SQLite version is very old and ADD COLUMN still fails
			// The specific "duplicate column name" check might still be a fallback if needed,
			// but the pre-check should reduce its necessity.
			if !(dbType == "sqlite" && strings.Contains(err.Error(), "duplicate column name")) {
				return fmt.Errorf("failed to alter users table to add google_contacts_auth_token: %w", err)
			}
		}
	}


if alterUsersTableSQL != "" {
if _, err := db.Exec(alterUsersTableSQL); err != nil {
// For SQLite, if the column already exists, this might return an error "duplicate column name"
// We'll log it and continue if it's that specific error for SQLite.
if dbType == "sqlite" && strings.Contains(err.Error(), "duplicate column name") {
// log.Warn().Msg("Column google_contacts_auth_token already exists in users table (SQLite).")
} else {
return fmt.Errorf("failed to alter users table to add google_contacts_auth_token: %w", err)
}
}
}


return nil
}

func getDatabaseConfig(exPath string) DatabaseConfig {
Expand Down Expand Up @@ -70,7 +162,6 @@ func initializePostgres(config DatabaseConfig) (*sqlx.DB, error) {
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("failed to ping postgres database: %w", err)
}

return db, nil
}

Expand All @@ -89,6 +180,5 @@ func initializeSQLite(config DatabaseConfig) (*sqlx.DB, error) {
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("failed to ping sqlite database: %w", err)
}

return db, nil
}
Loading