RMC chatbot version 1 implemented
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/target/
|
||||||
|
/.idea/
|
||||||
|
*.iml
|
||||||
|
.DS_Store
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.compile.nullAnalysis.mode": "automatic"
|
||||||
|
}
|
||||||
310
README.md
Normal file
310
README.md
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
# WhatsApp Automation
|
||||||
|
|
||||||
|
WhatsApp Automation is a Spring Boot backend for WhatsApp Business messaging workflows. It receives webhook events from Meta, runs bot-driven conversation logic, and persists booking/state data in PostgreSQL.
|
||||||
|
|
||||||
|
## About The Project
|
||||||
|
|
||||||
|
This project provides automation features around WhatsApp messaging:
|
||||||
|
|
||||||
|
- Webhook verification and message handling (`/webhook`)
|
||||||
|
- Chatbot conversation flow
|
||||||
|
- PostgreSQL persistence for appointments, service bookings, test bookings, and user state
|
||||||
|
|
||||||
|
The application is a **Java 21** Maven service built with Spring Boot.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- Spring Boot 3.3.2
|
||||||
|
- Java 21
|
||||||
|
- Spring Web
|
||||||
|
- Spring Data JPA
|
||||||
|
- PostgreSQL
|
||||||
|
- ngrok (for local webhook tunneling to Meta)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| **Java 21** | Runtime for the Spring Boot app |
|
||||||
|
| **Maven 3.9+** | Build and run the project |
|
||||||
|
| **PostgreSQL** | Application database |
|
||||||
|
| **ngrok** | Expose local backend to the public internet so Meta can call your webhook |
|
||||||
|
| **Meta / WhatsApp Business API access** | Access token, webhook configuration, and test messaging |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Run the Backend (Full Setup)
|
||||||
|
|
||||||
|
Follow these steps in order.
|
||||||
|
|
||||||
|
### 1. Clone the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd "WhatsApp Automation"
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `<repository-url>` with the actual Git remote URL for this project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Install ngrok
|
||||||
|
|
||||||
|
ngrok creates a public HTTPS URL that tunnels to your local Spring Boot server so Meta can deliver WhatsApp webhooks.
|
||||||
|
|
||||||
|
1. Go to [https://ngrok.com](https://ngrok.com)
|
||||||
|
2. Sign up or sign in
|
||||||
|
3. Install ngrok for your OS (macOS example with Homebrew):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Or download the binary from the ngrok dashboard and follow their install instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Authenticate ngrok with your access token
|
||||||
|
|
||||||
|
After signing in to ngrok, copy your **authtoken** from the ngrok dashboard, then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <YOUR_NGROK_AUTHTOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
Without this step, ngrok will not start a usable tunnel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Log in to the WhatsApp Business / Meta developer dashboard
|
||||||
|
|
||||||
|
1. Open the [WhatsApp Business API / Meta for Developers](https://developers.facebook.com/) dashboard
|
||||||
|
2. Log in with the account that has access to this app
|
||||||
|
3. Use the project’s WhatsApp / Meta credentials (app ID, business ID, phone number ID, etc.) as configured for your team
|
||||||
|
|
||||||
|
Relevant app links used by this project:
|
||||||
|
|
||||||
|
- **API testing / access token:**
|
||||||
|
[Generate WhatsApp access token](https://developers.facebook.com/apps/1054675710401577/use_cases/customize/api-testing-v2/?product_route=whatsapp-business&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=api-testing-v2)
|
||||||
|
|
||||||
|
- **Webhooks configuration:**
|
||||||
|
[Configure webhooks](https://developers.facebook.com/apps/1054675710401577/use_cases/customize/webhooks/?product_route=webhooks&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=webhooks)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Generate a WhatsApp access token and configure the app
|
||||||
|
|
||||||
|
1. Open the API testing page:
|
||||||
|
[https://developers.facebook.com/apps/1054675710401577/use_cases/customize/api-testing-v2/?product_route=whatsapp-business&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=api-testing-v2](https://developers.facebook.com/apps/1054675710401577/use_cases/customize/api-testing-v2/?product_route=whatsapp-business&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=api-testing-v2)
|
||||||
|
2. Generate a **new Access Token**
|
||||||
|
3. Open the project config file:
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/main/resources/application.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Paste the token into the access-token property:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
whatsapp.access-token=<YOUR_WHATSAPP_ACCESS_TOKEN>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** Temporary tokens from Meta expire. If outbound WhatsApp API calls start failing with auth errors, generate a new token and update this property.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Create the PostgreSQL database and set credentials
|
||||||
|
|
||||||
|
1. Ensure PostgreSQL is installed and running locally
|
||||||
|
2. Create the database used by this project (default name: `rmc_bot`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
createdb rmc_bot
|
||||||
|
```
|
||||||
|
|
||||||
|
Or via `psql`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE rmc_bot;
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Update database settings in `src/main/resources/application.properties`:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/rmc_bot
|
||||||
|
spring.datasource.username=<YOUR_POSTGRES_USERNAME>
|
||||||
|
spring.datasource.password=<YOUR_POSTGRES_PASSWORD>
|
||||||
|
```
|
||||||
|
|
||||||
|
Default values currently in the file (change these for your machine):
|
||||||
|
|
||||||
|
| Property | Default / example |
|
||||||
|
|----------|-------------------|
|
||||||
|
| `spring.datasource.url` | `jdbc:postgresql://localhost:5432/rmc_bot` |
|
||||||
|
| `spring.datasource.username` | `postgres` |
|
||||||
|
| `spring.datasource.password` | *(your local password)* |
|
||||||
|
|
||||||
|
The app uses:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
```
|
||||||
|
|
||||||
|
so tables are created/updated automatically on startup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Start the Spring Boot backend
|
||||||
|
|
||||||
|
The server listens on **port 8085** (`server.port=8085`).
|
||||||
|
|
||||||
|
From the project root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm the app started successfully (no datasource or port-binding errors). Leave this process running.
|
||||||
|
|
||||||
|
You can also run tests with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Start ngrok and point it at the backend
|
||||||
|
|
||||||
|
**Important:** Start the Spring Boot backend *before* configuring Meta webhooks, and keep both the backend and ngrok running while testing.
|
||||||
|
|
||||||
|
In a **new terminal**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok http 8085
|
||||||
|
```
|
||||||
|
|
||||||
|
ngrok will print a public HTTPS URL, for example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://abcd-1234.ngrok-free.app
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy that base URL (the `https://...` forwarding address).
|
||||||
|
|
||||||
|
> Free ngrok URLs change every time you restart ngrok. When the URL changes, you must update the Callback URL in Meta again.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Configure the Meta webhook
|
||||||
|
|
||||||
|
1. Open the webhooks page:
|
||||||
|
[https://developers.facebook.com/apps/1054675710401577/use_cases/customize/webhooks/?product_route=webhooks&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=webhooks](https://developers.facebook.com/apps/1054675710401577/use_cases/customize/webhooks/?product_route=webhooks&business_id=1051413880774512&use_case_enum=WHATSAPP_BUSINESS_MESSAGING&selected_tab=webhooks)
|
||||||
|
|
||||||
|
2. Set **Callback URL** to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://<YOUR_NGROK_HOST>/webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://abcd-1234.ngrok-free.app/webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Set **Verify Token** to exactly:
|
||||||
|
|
||||||
|
```text
|
||||||
|
my_whatsapp_verify_token_123
|
||||||
|
```
|
||||||
|
|
||||||
|
This must match `whatsapp.verify-token` in `application.properties`:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
whatsapp.verify-token=my_whatsapp_verify_token_123
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Click **Verify and Save** (or equivalent). Meta will send a `GET` request to `/webhook` with `hub.mode`, `hub.verify_token`, and `hub.challenge`. The backend validates the token and returns the challenge.
|
||||||
|
|
||||||
|
5. Subscribe to the webhook fields you need (typically **messages** for inbound WhatsApp traffic).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration reference
|
||||||
|
|
||||||
|
All runtime secrets and connection settings live in:
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/main/resources/application.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `server.port` | Backend HTTP port (default `8085`) |
|
||||||
|
| `whatsapp.access-token` | Meta WhatsApp Cloud API access token |
|
||||||
|
| `whatsapp.verify-token` | Token Meta uses to verify your webhook (`my_whatsapp_verify_token_123`) |
|
||||||
|
| `spring.datasource.url` | JDBC URL for PostgreSQL |
|
||||||
|
| `spring.datasource.username` | PostgreSQL username |
|
||||||
|
| `spring.datasource.password` | PostgreSQL password |
|
||||||
|
| `spring.jpa.hibernate.ddl-auto` | Schema mode (`update`) |
|
||||||
|
| `spring.jpa.properties.hibernate.dialect` | PostgreSQL dialect |
|
||||||
|
| `spring.jpa.show-sql` | Log SQL statements |
|
||||||
|
|
||||||
|
Webhook endpoint exposed by the app:
|
||||||
|
|
||||||
|
| Method | Path | Purpose |
|
||||||
|
|--------|------|---------|
|
||||||
|
| `GET` | `/webhook` | Meta webhook verification |
|
||||||
|
| `POST` | `/webhook` | Inbound WhatsApp events / messages |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick checklist
|
||||||
|
|
||||||
|
Use this when bringing the stack up on a new machine:
|
||||||
|
|
||||||
|
1. [ ] Clone the repo
|
||||||
|
2. [ ] Install and authenticate ngrok
|
||||||
|
3. [ ] Log in to Meta / WhatsApp Business dashboard
|
||||||
|
4. [ ] Generate access token → set `whatsapp.access-token`
|
||||||
|
5. [ ] Create PostgreSQL DB → set datasource username/password
|
||||||
|
6. [ ] `mvn spring-boot:run` (port **8085**)
|
||||||
|
7. [ ] `ngrok http 8085`
|
||||||
|
8. [ ] Meta Callback URL = `https://<ngrok-host>/webhook`
|
||||||
|
9. [ ] Verify token = `my_whatsapp_verify_token_123`
|
||||||
|
10. [ ] Verify webhook + subscribe to message events
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Problem | What to check |
|
||||||
|
|---------|----------------|
|
||||||
|
| Webhook verification fails | Verify token must be exactly `my_whatsapp_verify_token_123`. Backend must be running. Callback URL must end with `/webhook`. |
|
||||||
|
| Meta cannot reach the server | ngrok must be running (`ngrok http 8085`). Use the current ngrok HTTPS URL, not an old one. |
|
||||||
|
| Database connection errors | PostgreSQL is running; database `rmc_bot` exists; username/password in `application.properties` are correct. |
|
||||||
|
| Cannot send WhatsApp messages | Access token may have expired — generate a new one and update `whatsapp.access-token`. |
|
||||||
|
| Port already in use | Something else is bound to `8085`, or change `server.port` and point ngrok at the new port. |
|
||||||
|
| Not receiving messages on webhook | Your WhatsApp Business Account (WABA) might not be subscribed to your developer App. Link them via API: `curl -X POST "https://graph.facebook.com/v20.0/<WABA_ID>/subscribed_apps" -H "Authorization: Bearer <your_access_token>"` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project structure (high level)
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/main/java/com/example/whatsappautomation/
|
||||||
|
WhatsAppAutomationApplication.java
|
||||||
|
controller/WhatsAppWebhookController.java
|
||||||
|
service/ChatbotService.java
|
||||||
|
entity/ # Appointment, ServiceBooking, TestBooking, UserState
|
||||||
|
repository/ # Spring Data JPA repositories
|
||||||
|
src/main/resources/
|
||||||
|
application.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Internal / project use unless otherwise specified.
|
||||||
121
app_test.log
Normal file
121
app_test.log
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
[INFO] Scanning for projects...
|
||||||
|
[INFO]
|
||||||
|
[INFO] ------------------< com.example:whatsapp-automation >-------------------
|
||||||
|
[INFO] Building WhatsApp Automation 0.0.1-SNAPSHOT
|
||||||
|
[INFO] from pom.xml
|
||||||
|
[INFO] --------------------------------[ jar ]---------------------------------
|
||||||
|
[INFO]
|
||||||
|
[INFO] >>> spring-boot:3.3.2:run (default-cli) > test-compile @ whatsapp-automation >>>
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- resources:3.3.1:resources (default-resources) @ whatsapp-automation ---
|
||||||
|
[INFO] Copying 1 resource from src/main/resources to target/classes
|
||||||
|
[INFO] Copying 3 resources from src/main/resources to target/classes
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- compiler:3.13.0:compile (default-compile) @ whatsapp-automation ---
|
||||||
|
[INFO] Nothing to compile - all classes are up to date.
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- resources:3.3.1:testResources (default-testResources) @ whatsapp-automation ---
|
||||||
|
[INFO] skip non existing resourceDirectory /Users/devprasad/Downloads/RMC-WhatsApp-Bot-main/src/test/resources
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ whatsapp-automation ---
|
||||||
|
[INFO] Nothing to compile - all classes are up to date.
|
||||||
|
[INFO]
|
||||||
|
[INFO] <<< spring-boot:3.3.2:run (default-cli) < test-compile @ whatsapp-automation <<<
|
||||||
|
[INFO]
|
||||||
|
[INFO]
|
||||||
|
[INFO] --- spring-boot:3.3.2:run (default-cli) @ whatsapp-automation ---
|
||||||
|
[INFO] Attaching agents: []
|
||||||
|
|
||||||
|
. ____ _ __ _ _
|
||||||
|
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||||
|
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||||
|
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||||
|
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||||
|
=========|_|==============|___/=/_/_/_/
|
||||||
|
|
||||||
|
:: Spring Boot :: (v3.3.2)
|
||||||
|
|
||||||
|
2026-08-17T11:25:46.813+05:30 INFO 98153 --- [whatsapp-automation] [ main] c.e.w.WhatsAppAutomationApplication : Starting WhatsAppAutomationApplication using Java 26.0.1 with PID 98153 (/Users/devprasad/Downloads/RMC-WhatsApp-Bot-main/target/classes started by devprasad in /Users/devprasad/Downloads/RMC-WhatsApp-Bot-main)
|
||||||
|
2026-08-17T11:25:46.814+05:30 INFO 98153 --- [whatsapp-automation] [ main] c.e.w.WhatsAppAutomationApplication : No active profile set, falling back to 1 default profile: "default"
|
||||||
|
2026-08-17T11:25:46.992+05:30 INFO 98153 --- [whatsapp-automation] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
|
||||||
|
2026-08-17T11:25:47.009+05:30 INFO 98153 --- [whatsapp-automation] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 4 JPA repository interfaces.
|
||||||
|
WARNING: A restricted method in java.lang.System has been called
|
||||||
|
WARNING: java.lang.System::load has been called by org.apache.tomcat.jni.Library in an unnamed module (file:/Users/devprasad/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/10.1.26/tomcat-embed-core-10.1.26.jar)
|
||||||
|
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
|
||||||
|
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
|
||||||
|
|
||||||
|
2026-08-17T11:25:47.150+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8086 (http)
|
||||||
|
2026-08-17T11:25:47.153+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
|
||||||
|
2026-08-17T11:25:47.154+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.26]
|
||||||
|
2026-08-17T11:25:47.167+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
|
||||||
|
2026-08-17T11:25:47.168+05:30 INFO 98153 --- [whatsapp-automation] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 339 ms
|
||||||
|
2026-08-17T11:25:47.210+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
|
||||||
|
2026-08-17T11:25:47.226+05:30 INFO 98153 --- [whatsapp-automation] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final
|
||||||
|
2026-08-17T11:25:47.234+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
|
||||||
|
2026-08-17T11:25:47.316+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
|
||||||
|
2026-08-17T11:25:47.323+05:30 INFO 98153 --- [whatsapp-automation] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
|
||||||
|
2026-08-17T11:25:47.373+05:30 INFO 98153 --- [whatsapp-automation] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@10e9a5fe
|
||||||
|
2026-08-17T11:25:47.373+05:30 INFO 98153 --- [whatsapp-automation] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
|
||||||
|
2026-08-17T11:25:47.387+05:30 WARN 98153 --- [whatsapp-automation] [ main] org.hibernate.orm.deprecation : HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
|
||||||
|
2026-08-17T11:25:47.639+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
|
||||||
|
Hibernate: alter table if exists user_state add column menu_page integer not null
|
||||||
|
2026-08-17T11:25:47.666+05:30 WARN 98153 --- [whatsapp-automation] [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "alter table if exists user_state add column menu_page integer not null" via JDBC [ERROR: column "menu_page" of relation "user_state" contains null values]
|
||||||
|
|
||||||
|
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "alter table if exists user_state add column menu_page integer not null" via JDBC [ERROR: column "menu_page" of relation "user_state" contains null values]
|
||||||
|
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:94) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:575) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:515) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.migrateTable(AbstractSchemaMigrator.java:333) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:84) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:232) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:117) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at java.base/java.util.HashMap.forEach(HashMap.java:1430) ~[na:na]
|
||||||
|
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:322) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) ~[spring-orm-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:954) ~[spring-context-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.11.jar:6.1.11]
|
||||||
|
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.2.jar:3.3.2]
|
||||||
|
at com.example.whatsappautomation.WhatsAppAutomationApplication.main(WhatsAppAutomationApplication.java:10) ~[classes/:na]
|
||||||
|
Caused by: org.postgresql.util.PSQLException: ERROR: column "menu_page" of relation "user_state" contains null values
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2725) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2412) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:371) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:502) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:419) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:341) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:326) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:302) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:297) ~[postgresql-42.7.3.jar:42.7.3]
|
||||||
|
at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) ~[HikariCP-5.1.0.jar:na]
|
||||||
|
at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-5.1.0.jar:na]
|
||||||
|
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:80) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
|
||||||
|
... 37 common frames omitted
|
||||||
|
|
||||||
|
2026-08-17T11:25:47.668+05:30 INFO 98153 --- [whatsapp-automation] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
|
||||||
|
2026-08-17T11:25:47.778+05:30 WARN 98153 --- [whatsapp-automation] [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
|
||||||
|
2026-08-17T11:25:47.866+05:30 INFO 98153 --- [whatsapp-automation] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8086 (http) with context path '/'
|
||||||
|
2026-08-17T11:25:47.869+05:30 INFO 98153 --- [whatsapp-automation] [ main] c.e.w.WhatsAppAutomationApplication : Started WhatsAppAutomationApplication in 1.159 seconds (process running for 1.272)
|
||||||
63
fix_args.py
Normal file
63
fix_args.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
with open('src/main/java/com/example/whatsappautomation/service/ChatbotService.java', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# We want to match: sendPaginatedButtons(state, arg2, arg3, new String[]{...}, new String[]{...})
|
||||||
|
# and replace it with: sendPaginatedButtons(state, arg2, new String[]{...}, new String[]{...})
|
||||||
|
# Note: arg2 and arg3 might be getMessage(...) or "string".
|
||||||
|
# It's safest to match: sendPaginatedButtons(state, (whatever), (whatever), new String[]{
|
||||||
|
|
||||||
|
def replacer(match):
|
||||||
|
# match.group(1) is the part from `state, ` up to `, new String[]{`
|
||||||
|
# this part contains `arg2, arg3`. We need to split by comma from the right, but arg2 or arg3 might have commas!
|
||||||
|
# Wait, getting the last argument before `new String[]{` is the best way.
|
||||||
|
# Actually, the button text (arg3) is almost always `getMessage("something.btn", state)` or `"View Options"` or similar.
|
||||||
|
# Let's just use regex to match the third argument which is always followed by `, new String\[\]\{`
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Let's write a simple python parser for the arguments of sendPaginatedButtons
|
||||||
|
new_content = ""
|
||||||
|
idx = 0
|
||||||
|
while True:
|
||||||
|
start_idx = content.find('sendPaginatedButtons(state, ', idx)
|
||||||
|
if start_idx == -1:
|
||||||
|
new_content += content[idx:]
|
||||||
|
break
|
||||||
|
|
||||||
|
new_content += content[idx:start_idx]
|
||||||
|
|
||||||
|
# find the start of `new String[]{`
|
||||||
|
arrays_start = content.find('new String[]{', start_idx)
|
||||||
|
|
||||||
|
# The text between `start_idx + len('sendPaginatedButtons(state, ')` and `arrays_start` is `arg2, arg3, `
|
||||||
|
# We want to remove `arg3, `.
|
||||||
|
# `arg3` is either a string literal `"..."` or a `getMessage(...)` call.
|
||||||
|
args_str = content[start_idx + len('sendPaginatedButtons(state, '):arrays_start].strip()
|
||||||
|
# args_str ends with `,`. Let's remove the trailing comma.
|
||||||
|
args_str = args_str[:-1].strip()
|
||||||
|
|
||||||
|
# Now we need to split args_str into arg2 and arg3.
|
||||||
|
# If it ends with `state)`, arg3 is `getMessage(..., state)`.
|
||||||
|
# If it ends with `"`, arg3 is a string literal.
|
||||||
|
|
||||||
|
# Actually, we can just find the LAST comma that is NOT inside parentheses!
|
||||||
|
# A simple comma split works if we respect parens.
|
||||||
|
def rsplit_args(s):
|
||||||
|
depth = 0
|
||||||
|
for i in range(len(s)-1, -1, -1):
|
||||||
|
if s[i] == ')': depth += 1
|
||||||
|
elif s[i] == '(': depth -= 1
|
||||||
|
elif s[i] == ',' and depth == 0:
|
||||||
|
return s[:i].strip(), s[i+1:].strip()
|
||||||
|
return s, ""
|
||||||
|
|
||||||
|
arg2, arg3 = rsplit_args(args_str)
|
||||||
|
|
||||||
|
new_content += f"sendPaginatedButtons(state, {arg2}, "
|
||||||
|
idx = arrays_start
|
||||||
|
|
||||||
|
with open('src/main/java/com/example/whatsappautomation/service/ChatbotService.java', 'w') as f:
|
||||||
|
f.write(new_content)
|
||||||
|
|
||||||
|
print("Fixed arguments")
|
||||||
101
fix_menu.py
Normal file
101
fix_menu.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
with open("src/main/java/com/example/whatsappautomation/service/ChatbotService.java", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
old_paginated = """ private void sendPaginatedButtons(UserState state, String bodyText, String[] ids, String[] titles) {
|
||||||
|
int totalOptions = ids.length;
|
||||||
|
|
||||||
|
// Loop and send chunks of up to 3 buttons
|
||||||
|
for (int i = 0; i < totalOptions; i += 3) {
|
||||||
|
int end = Math.min(i + 3, totalOptions);
|
||||||
|
int count = end - i;
|
||||||
|
String[] chunkIds = new String[count];
|
||||||
|
String[] chunkTitles = new String[count];
|
||||||
|
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
chunkIds[j] = ids[i + j];
|
||||||
|
chunkTitles[j] = titles[i + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
String chunkBodyText = (i == 0) ? bodyText : ( "en".equals(state.getLanguagePreference()) ? "More options:" : "மேலும் விருப்பங்கள்:" );
|
||||||
|
|
||||||
|
sendInteractiveButtons(chunkBodyText, chunkIds, chunkTitles);
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
new_methods = """ private void sendInteractiveList(String bodyText, String buttonText, String sectionTitle, String[] ids, String[] titles) {
|
||||||
|
String phoneNumberId = currentPhoneId.get();
|
||||||
|
String recipientPhoneNumber = currentFrom.get();
|
||||||
|
if (phoneNumberId == null || recipientPhoneNumber == null) return;
|
||||||
|
String url = "https://graph.facebook.com/v20.0/" + phoneNumberId + "/messages";
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectNode requestBody = objectMapper.createObjectNode();
|
||||||
|
requestBody.put("messaging_product", "whatsapp");
|
||||||
|
requestBody.put("recipient_type", "individual");
|
||||||
|
requestBody.put("to", recipientPhoneNumber);
|
||||||
|
requestBody.put("type", "interactive");
|
||||||
|
|
||||||
|
ObjectNode interactiveNode = objectMapper.createObjectNode();
|
||||||
|
interactiveNode.put("type", "list");
|
||||||
|
|
||||||
|
ObjectNode bodyNode = objectMapper.createObjectNode();
|
||||||
|
|
||||||
|
UserState uState = userStateRepository.findById(recipientPhoneNumber).orElse(null);
|
||||||
|
if (uState != null && !"MAIN_MENU".equals(uState.getState()) && !"LANGUAGE_SELECTION".equals(uState.getState())) {
|
||||||
|
String restartText = "en".equals(uState.getLanguagePreference())
|
||||||
|
? "\\n\\n(Press 0 to restart the conversation)"
|
||||||
|
: "\\n\\n(உரையாடலை மீண்டும் தொடங்க 0 ஐ அழுத்தவும்)";
|
||||||
|
bodyText += restartText;
|
||||||
|
}
|
||||||
|
bodyNode.put("text", bodyText);
|
||||||
|
interactiveNode.set("body", bodyNode);
|
||||||
|
|
||||||
|
ObjectNode actionNode = objectMapper.createObjectNode();
|
||||||
|
actionNode.put("button", buttonText);
|
||||||
|
|
||||||
|
com.fasterxml.jackson.databind.node.ArrayNode sectionsArray = actionNode.putArray("sections");
|
||||||
|
ObjectNode section = objectMapper.createObjectNode();
|
||||||
|
section.put("title", sectionTitle);
|
||||||
|
com.fasterxml.jackson.databind.node.ArrayNode rowsArray = section.putArray("rows");
|
||||||
|
|
||||||
|
int limit = Math.min(ids.length, 10);
|
||||||
|
for (int i = 0; i < limit; i++) {
|
||||||
|
ObjectNode row = objectMapper.createObjectNode();
|
||||||
|
row.put("id", ids[i]);
|
||||||
|
row.put("title", titles[i]);
|
||||||
|
rowsArray.add(row);
|
||||||
|
}
|
||||||
|
sectionsArray.add(section);
|
||||||
|
|
||||||
|
interactiveNode.set("action", actionNode);
|
||||||
|
requestBody.set("interactive", interactiveNode);
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
|
||||||
|
headers.setBearerAuth(accessToken);
|
||||||
|
|
||||||
|
org.springframework.http.HttpEntity<String> entity = new org.springframework.http.HttpEntity<>(requestBody.toString(), headers);
|
||||||
|
restTemplate.postForEntity(url, entity, String.class);
|
||||||
|
} catch (org.springframework.web.client.HttpStatusCodeException e) {
|
||||||
|
logger.error("Error sending interactive list. Status: {}, Response: {}", e.getStatusCode(), e.getResponseBodyAsString(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error sending interactive list", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPaginatedButtons(UserState state, String bodyText, String[] ids, String[] titles) {
|
||||||
|
if (ids.length <= 3) {
|
||||||
|
sendInteractiveButtons(bodyText, ids, titles);
|
||||||
|
} else {
|
||||||
|
String buttonText = "en".equals(state.getLanguagePreference()) ? "Select Option" : "விருப்பத்தை தேர்ந்தெடுக்கவும்";
|
||||||
|
String sectionTitle = "en".equals(state.getLanguagePreference()) ? "Options" : "விருப்பங்கள்";
|
||||||
|
sendInteractiveList(bodyText, buttonText, sectionTitle, ids, titles);
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = content.replace(old_paginated, new_methods)
|
||||||
|
|
||||||
|
with open("src/main/java/com/example/whatsappautomation/service/ChatbotService.java", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print("Replacement complete.")
|
||||||
91
fix_restart.py
Normal file
91
fix_restart.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
with open("src/main/java/com/example/whatsappautomation/service/ChatbotService.java", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# 1. Modify sendInteractiveButtons to fetch state and append restart text
|
||||||
|
old_method_start = """ private void sendInteractiveButtons(String bodyText, String[] ids, String[] titles) {
|
||||||
|
String phoneNumberId = currentPhoneId.get();
|
||||||
|
String recipientPhoneNumber = currentFrom.get();"""
|
||||||
|
|
||||||
|
new_method_start = """ private void sendInteractiveButtons(String bodyText, String[] ids, String[] titles) {
|
||||||
|
String phoneNumberId = currentPhoneId.get();
|
||||||
|
String recipientPhoneNumber = currentFrom.get();
|
||||||
|
|
||||||
|
UserState uState = userStateRepository.findById(recipientPhoneNumber).orElse(null);
|
||||||
|
if (uState != null && !"MAIN_MENU".equals(uState.getState()) && !"LANGUAGE_SELECTION".equals(uState.getState())) {
|
||||||
|
String restartText = "en".equals(uState.getLanguagePreference())
|
||||||
|
? "\\n\\n(Press 0 to restart the conversation)"
|
||||||
|
: "\\n\\n(உரையாடலை மீண்டும் தொடங்க 0 ஐ அழுத்தவும்)";
|
||||||
|
bodyText += restartText;
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = content.replace(old_method_start, new_method_start)
|
||||||
|
|
||||||
|
# 2. Modify sendPaginatedButtons to not append restart text itself
|
||||||
|
old_paginated = """ private void sendPaginatedButtons(UserState state, String bodyText, String[] ids, String[] titles) {
|
||||||
|
int totalOptions = ids.length;
|
||||||
|
String restartText = "en".equals(state.getLanguagePreference())
|
||||||
|
? "\\n\\n(Send 0 to go back to the main menu / Restart the conversation)"
|
||||||
|
: "\\n\\n(உரையாடலை மீண்டும் தொடங்க 0 ஐ அழுத்தவும்)";
|
||||||
|
|
||||||
|
// Loop and send chunks of up to 3 buttons
|
||||||
|
for (int i = 0; i < totalOptions; i += 3) {
|
||||||
|
int end = Math.min(i + 3, totalOptions);
|
||||||
|
int count = end - i;
|
||||||
|
String[] chunkIds = new String[count];
|
||||||
|
String[] chunkTitles = new String[count];
|
||||||
|
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
chunkIds[j] = ids[i + j];
|
||||||
|
chunkTitles[j] = titles[i + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
String chunkBodyText = (i == 0) ? bodyText : ( "en".equals(state.getLanguagePreference()) ? "More options:" : "மேலும் விருப்பங்கள்:" );
|
||||||
|
if (end == totalOptions) {
|
||||||
|
chunkBodyText += restartText;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInteractiveButtons(chunkBodyText, chunkIds, chunkTitles);
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
new_paginated = """ private void sendPaginatedButtons(UserState state, String bodyText, String[] ids, String[] titles) {
|
||||||
|
int totalOptions = ids.length;
|
||||||
|
|
||||||
|
// Loop and send chunks of up to 3 buttons
|
||||||
|
for (int i = 0; i < totalOptions; i += 3) {
|
||||||
|
int end = Math.min(i + 3, totalOptions);
|
||||||
|
int count = end - i;
|
||||||
|
String[] chunkIds = new String[count];
|
||||||
|
String[] chunkTitles = new String[count];
|
||||||
|
|
||||||
|
for (int j = 0; j < count; j++) {
|
||||||
|
chunkIds[j] = ids[i + j];
|
||||||
|
chunkTitles[j] = titles[i + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
String chunkBodyText = (i == 0) ? bodyText : ( "en".equals(state.getLanguagePreference()) ? "More options:" : "மேலும் விருப்பங்கள்:" );
|
||||||
|
|
||||||
|
sendInteractiveButtons(chunkBodyText, chunkIds, chunkTitles);
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = content.replace(old_paginated, new_paginated)
|
||||||
|
|
||||||
|
# 3. Change processMessage to say "Press 0 to restart the conversation" instead of "(Send 0 to go back to the main menu / Restart the conversation)" for text responses
|
||||||
|
old_process_text = """ if (response != null && !response.isEmpty() && !response.startsWith("INTERACTIVE:") && !userState.getState().equals("MAIN_MENU") && !userState.getState().equals("LANGUAGE_SELECTION")) {
|
||||||
|
response += "\\n\\n(Send 0 to go back to the main menu / Restart the conversation)";
|
||||||
|
}"""
|
||||||
|
|
||||||
|
new_process_text = """ if (response != null && !response.isEmpty() && !response.startsWith("INTERACTIVE:") && !userState.getState().equals("MAIN_MENU") && !userState.getState().equals("LANGUAGE_SELECTION")) {
|
||||||
|
String rt = "en".equals(userState.getLanguagePreference())
|
||||||
|
? "\\n\\n(Press 0 to restart the conversation)"
|
||||||
|
: "\\n\\n(உரையாடலை மீண்டும் தொடங்க 0 ஐ அழுத்தவும்)";
|
||||||
|
response += rt;
|
||||||
|
}"""
|
||||||
|
|
||||||
|
content = content.replace(old_process_text, new_process_text)
|
||||||
|
|
||||||
|
with open("src/main/java/com/example/whatsappautomation/service/ChatbotService.java", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print("Replacement complete.")
|
||||||
38
fix_tamil.py
Normal file
38
fix_tamil.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
replacements = {
|
||||||
|
"காய்ச்சல் பரிசோதனைகள்": "காய்ச்சல் பரிசோதனை",
|
||||||
|
"மூட்டு மற்றும் எலும்பு": "மூட்டு & எலும்பு",
|
||||||
|
"முழுமையான உடல் பரிசோதனை": "முழு உடல் பரிசோதனை",
|
||||||
|
"காய்ச்சல் அல்ட்ரா பேனல்": "காய்ச்சல் அல்ட்ரா",
|
||||||
|
"மலம் பரிசோதனை (Stool Occult Blood)": "மலம் பரிசோதனை",
|
||||||
|
"அமயா இன்ட் கார்டியாக்": "அமயா கார்டியாக்",
|
||||||
|
"கார்டியாக் பேக்கேஜ் - அமயா": "அமயா பேக்கேஜ்",
|
||||||
|
"உயர் இரத்த அழுத்தம் தொகுப்பு": "உயர் ரத்த அழுத்தம்",
|
||||||
|
"மாஸ்டர் ஹெல்த் பேசிக்": "மாஸ்டர் பேசிக்",
|
||||||
|
"மாஸ்டர் ஹெல்த் எக்ஸிகியூட்டிவ்": "மாஸ்டர் எக்ஸிகியூட்",
|
||||||
|
"மாஸ்டர் ஹெல்த் பிரீமியம்": "மாஸ்டர் பிரீமியம்",
|
||||||
|
"கர்ப்ப காலம் அட்வான்ஸ்டு": "கர்ப்ப அட்வான்ஸ்டு",
|
||||||
|
"சர்க்கரை கட்டுப்பாடு பேசிக்": "சர்க்கரை பேசிக்",
|
||||||
|
"சர்க்கரை கட்டுப்பாடு பிளஸ்": "சர்க்கரை பிளஸ்",
|
||||||
|
"பெண்கள் நல்வாழ்வு பேசிக்": "பெண்கள் பேசிக்",
|
||||||
|
"பெண்கள் நல்வாழ்வு அட்வான்ஸ்டு": "பெண்கள் அட்வான்ஸ்டு",
|
||||||
|
"புதிய சந்திப்பை முன்பதிவு செய்ய": "புதிய முன்பதிவு",
|
||||||
|
"சந்திப்புகளைப் பார்க்க": "சந்திப்புகளை பார்",
|
||||||
|
"சந்திப்பை மாற்றியமைக்க": "மாற்றியமைக்க",
|
||||||
|
"சந்திப்பை ரத்து செய்ய": "ரத்து செய்ய",
|
||||||
|
"முன்பதிவை உறுதிப்படுத்தவும்": "உறுதிப்படுத்துக",
|
||||||
|
"சந்திப்பை ரத்து செய்யவும்": "ரத்து செய்க",
|
||||||
|
"கட்டு போடுதல் (Dressing)": "கட்டு போடுதல்",
|
||||||
|
"வடிகுழாய் (Catheterization)": "வடிகுழாய்",
|
||||||
|
"வீட்டு பராமரிப்பு தொகுப்புகள்": "வீட்டு பராமரிப்பு"
|
||||||
|
}
|
||||||
|
|
||||||
|
with open('src/main/resources/messages_ta.properties', 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
for old, new in replacements.items():
|
||||||
|
content = content.replace(f"={old}", f"={new}")
|
||||||
|
|
||||||
|
with open('src/main/resources/messages_ta.properties', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print("Replaced lengths.")
|
||||||
15
length_check.py
Normal file
15
length_check.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
# Keys that are used for buttons (options)
|
||||||
|
button_keys = ['opt', 'btn', 'yes', 'no', 'confirm', 'change', 'cancel']
|
||||||
|
|
||||||
|
with open('src/main/resources/messages_ta.properties', 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith('#'):
|
||||||
|
continue
|
||||||
|
if '=' in line:
|
||||||
|
key, val = line.split('=', 1)
|
||||||
|
is_button = any(bk in key for bk in button_keys)
|
||||||
|
if is_button and len(val) > 20:
|
||||||
|
print(f"{key}={val} ({len(val)} chars)")
|
||||||
56
pom.xml
Normal file
56
pom.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.3.2</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>whatsapp-automation</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>WhatsApp Automation</name>
|
||||||
|
<description>Spring Boot application for WhatsApp automation</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
54
replace.py
Normal file
54
replace.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
with open('src/main/java/com/example/whatsappautomation/service/ChatbotService.java', 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# Pattern to match sendInteractiveList(...) up to the null);
|
||||||
|
# We need to capture bodyText (arg 1), ids (arg 4), titles (arg 5).
|
||||||
|
# Using a python script to parse the balanced parentheses might be better.
|
||||||
|
|
||||||
|
def replace_calls(text):
|
||||||
|
start = 0
|
||||||
|
while True:
|
||||||
|
idx = text.find('sendInteractiveList(', start)
|
||||||
|
if idx == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
# find the end of this statement
|
||||||
|
end_idx = text.find(');', idx)
|
||||||
|
if end_idx == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
call_str = text[idx:end_idx+2]
|
||||||
|
|
||||||
|
# We need to extract the parts. It's safer to just split by "new String[]{"
|
||||||
|
# because we know args 4 and 5 are array allocations.
|
||||||
|
|
||||||
|
parts = call_str.split('new String[]{')
|
||||||
|
if len(parts) == 3:
|
||||||
|
# parts[0] has 'sendInteractiveList(bodyText, buttonText, sectionTitle, \n'
|
||||||
|
# parts[1] has 'ids...}, \n'
|
||||||
|
# parts[2] has 'titles...}, null);'
|
||||||
|
|
||||||
|
# Extract bodyText from parts[0]
|
||||||
|
# sendInteractiveList(arg1, arg2, arg3,
|
||||||
|
# We want just arg1!
|
||||||
|
p0 = parts[0][len('sendInteractiveList('):].strip()
|
||||||
|
# It's tricky to parse commas.
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("Doing regex...")
|
||||||
|
|
||||||
|
# Actually, let's just use a simple regex if they are formatted consistently.
|
||||||
|
# Almost all of them are:
|
||||||
|
# sendInteractiveList( BODY, BTN, TITLE,
|
||||||
|
# new String[]{IDS},
|
||||||
|
# new String[]{TITLES}, null);
|
||||||
|
|
||||||
|
pattern = r'sendInteractiveList\((.*?),\s*[^,]+,\s*[^,]+,\s*(new String\[\]\{.*?\})\s*,\s*(new String\[\]\{.*?\})\s*,\s*null\);'
|
||||||
|
replaced = re.sub(pattern, r'sendPaginatedButtons(state, \1, \2, \3);', content, flags=re.DOTALL)
|
||||||
|
|
||||||
|
with open('src/main/java/com/example/whatsappautomation/service/ChatbotService.java', 'w') as f:
|
||||||
|
f.write(replaced)
|
||||||
|
|
||||||
|
print("Done")
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.example.whatsappautomation;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class WhatsAppAutomationApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(WhatsAppAutomationApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.example.whatsappautomation.controller;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.service.ChatbotService;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/webhook")
|
||||||
|
public class WhatsAppWebhookController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(WhatsAppWebhookController.class);
|
||||||
|
|
||||||
|
@Value("${whatsapp.verify-token}")
|
||||||
|
private String verifyToken;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private final ChatbotService chatbotService;
|
||||||
|
|
||||||
|
public WhatsAppWebhookController(ChatbotService chatbotService) {
|
||||||
|
this.chatbotService = chatbotService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webhook verification endpoint for Meta/WhatsApp.
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<String> verifyWebhook(
|
||||||
|
@RequestParam("hub.mode") String mode,
|
||||||
|
@RequestParam("hub.challenge") String challenge,
|
||||||
|
@RequestParam("hub.verify_token") String token) {
|
||||||
|
|
||||||
|
logger.info("Received verification request: mode={}, token={}", mode, token);
|
||||||
|
|
||||||
|
if ("subscribe".equals(mode) && verifyToken.equals(token)) {
|
||||||
|
logger.info("Webhook verified successfully!");
|
||||||
|
return ResponseEntity.ok(challenge);
|
||||||
|
} else {
|
||||||
|
logger.warn("Webhook verification failed. Provided token: {}", token);
|
||||||
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Verification failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webhook event handler endpoint.
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<String> receive(@RequestBody String body) {
|
||||||
|
System.out.println("Received:");
|
||||||
|
System.out.println(body);
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonNode payload = objectMapper.readTree(body);
|
||||||
|
if (payload.has("entry")) {
|
||||||
|
JsonNode entryArray = payload.get("entry");
|
||||||
|
if (entryArray.isArray() && entryArray.size() > 0) {
|
||||||
|
JsonNode changesArray = entryArray.get(0).get("changes");
|
||||||
|
if (changesArray != null && changesArray.isArray() && changesArray.size() > 0) {
|
||||||
|
JsonNode value = changesArray.get(0).get("value");
|
||||||
|
if (value != null && value.has("messages")) {
|
||||||
|
JsonNode messagesArray = value.get("messages");
|
||||||
|
if (messagesArray.isArray() && messagesArray.size() > 0) {
|
||||||
|
JsonNode message = messagesArray.get(0);
|
||||||
|
String from = message.get("from").asText();
|
||||||
|
|
||||||
|
// Avoid processing status changes/read receipts as incoming messages
|
||||||
|
String textBody = null;
|
||||||
|
if (message.has("text")) {
|
||||||
|
textBody = message.get("text").get("body").asText();
|
||||||
|
} else if (message.has("interactive")) {
|
||||||
|
JsonNode interactive = message.get("interactive");
|
||||||
|
if (interactive.has("button_reply")) {
|
||||||
|
textBody = interactive.get("button_reply").get("id").asText();
|
||||||
|
} else if (interactive.has("list_reply")) {
|
||||||
|
textBody = interactive.get("list_reply").get("id").asText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (textBody != null) {
|
||||||
|
String phoneNumberId = value.get("metadata").get("phone_number_id").asText();
|
||||||
|
// Delegate to Chatbot Service
|
||||||
|
chatbotService.processMessage(from, textBody, phoneNumberId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error processing webhook payload", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.ok("EVENT_RECEIVED");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.example.whatsappautomation.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "appointment")
|
||||||
|
public class Appointment {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String phoneNumber;
|
||||||
|
private String patientId;
|
||||||
|
private String appointmentId;
|
||||||
|
private String department;
|
||||||
|
private String doctor;
|
||||||
|
private String appointmentDate;
|
||||||
|
private String appointmentTime;
|
||||||
|
private String fee;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
public String getPhoneNumber() { return phoneNumber; }
|
||||||
|
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
|
||||||
|
public String getPatientId() { return patientId; }
|
||||||
|
public void setPatientId(String patientId) { this.patientId = patientId; }
|
||||||
|
public String getAppointmentId() { return appointmentId; }
|
||||||
|
public void setAppointmentId(String appointmentId) { this.appointmentId = appointmentId; }
|
||||||
|
public String getDepartment() { return department; }
|
||||||
|
public void setDepartment(String department) { this.department = department; }
|
||||||
|
public String getDoctor() { return doctor; }
|
||||||
|
public void setDoctor(String doctor) { this.doctor = doctor; }
|
||||||
|
public String getAppointmentDate() { return appointmentDate; }
|
||||||
|
public void setAppointmentDate(String appointmentDate) { this.appointmentDate = appointmentDate; }
|
||||||
|
public String getAppointmentTime() { return appointmentTime; }
|
||||||
|
public void setAppointmentTime(String appointmentTime) { this.appointmentTime = appointmentTime; }
|
||||||
|
public String getFee() { return fee; }
|
||||||
|
public void setFee(String fee) { this.fee = fee; }
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.example.whatsappautomation.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "service_booking")
|
||||||
|
public class ServiceBooking {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String phoneNumber;
|
||||||
|
private String patientId;
|
||||||
|
private String bookingId;
|
||||||
|
private String serviceName;
|
||||||
|
private String price;
|
||||||
|
private String address;
|
||||||
|
private String bookingDate;
|
||||||
|
private String bookingTime;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
public String getPhoneNumber() { return phoneNumber; }
|
||||||
|
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
|
||||||
|
public String getPatientId() { return patientId; }
|
||||||
|
public void setPatientId(String patientId) { this.patientId = patientId; }
|
||||||
|
public String getBookingId() { return bookingId; }
|
||||||
|
public void setBookingId(String bookingId) { this.bookingId = bookingId; }
|
||||||
|
public String getServiceName() { return serviceName; }
|
||||||
|
public void setServiceName(String serviceName) { this.serviceName = serviceName; }
|
||||||
|
public String getPrice() { return price; }
|
||||||
|
public void setPrice(String price) { this.price = price; }
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
public String getBookingDate() { return bookingDate; }
|
||||||
|
public void setBookingDate(String bookingDate) { this.bookingDate = bookingDate; }
|
||||||
|
public String getBookingTime() { return bookingTime; }
|
||||||
|
public void setBookingTime(String bookingTime) { this.bookingTime = bookingTime; }
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.example.whatsappautomation.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "test_booking")
|
||||||
|
public class TestBooking {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String phoneNumber;
|
||||||
|
private String bookingId;
|
||||||
|
private String packageName;
|
||||||
|
private String price;
|
||||||
|
private String collectionType;
|
||||||
|
private String address;
|
||||||
|
private String bookingDate;
|
||||||
|
private String bookingTime;
|
||||||
|
private String patientId;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
public String getPhoneNumber() { return phoneNumber; }
|
||||||
|
public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }
|
||||||
|
public String getBookingId() { return bookingId; }
|
||||||
|
public void setBookingId(String bookingId) { this.bookingId = bookingId; }
|
||||||
|
public String getPackageName() { return packageName; }
|
||||||
|
public void setPackageName(String packageName) { this.packageName = packageName; }
|
||||||
|
public String getPrice() { return price; }
|
||||||
|
public void setPrice(String price) { this.price = price; }
|
||||||
|
public String getCollectionType() { return collectionType; }
|
||||||
|
public void setCollectionType(String collectionType) { this.collectionType = collectionType; }
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
public String getBookingDate() { return bookingDate; }
|
||||||
|
public void setBookingDate(String bookingDate) { this.bookingDate = bookingDate; }
|
||||||
|
public String getBookingTime() { return bookingTime; }
|
||||||
|
public void setBookingTime(String bookingTime) { this.bookingTime = bookingTime; }
|
||||||
|
public String getPatientId() { return patientId; }
|
||||||
|
public void setPatientId(String patientId) { this.patientId = patientId; }
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package com.example.whatsappautomation.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "user_state")
|
||||||
|
public class UserState {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
private String patientId;
|
||||||
|
private String state;
|
||||||
|
private String category;
|
||||||
|
private String subCategory;
|
||||||
|
private String selectedItem;
|
||||||
|
private String price;
|
||||||
|
private String address;
|
||||||
|
private String bookingDate;
|
||||||
|
private String bookingTime;
|
||||||
|
private String languagePreference = "en";
|
||||||
|
private Integer menuPage = 0;
|
||||||
|
private String lastPhoneNumberId;
|
||||||
|
private Long lastInteractionTime;
|
||||||
|
|
||||||
|
public UserState() {}
|
||||||
|
|
||||||
|
public UserState(String phoneNumber) {
|
||||||
|
this.phoneNumber = phoneNumber;
|
||||||
|
this.state = "LANGUAGE_SELECTION";
|
||||||
|
this.patientId = "PAT-" + (100000 + new java.util.Random().nextInt(900000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.state = "LANGUAGE_SELECTION";
|
||||||
|
this.category = null;
|
||||||
|
this.subCategory = null;
|
||||||
|
this.selectedItem = null;
|
||||||
|
this.price = null;
|
||||||
|
this.address = null;
|
||||||
|
this.bookingDate = null;
|
||||||
|
this.bookingTime = null;
|
||||||
|
this.menuPage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhoneNumber() {
|
||||||
|
return phoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhoneNumber(String phoneNumber) {
|
||||||
|
this.phoneNumber = phoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
if (!state.equals(this.state)) {
|
||||||
|
this.menuPage = 0;
|
||||||
|
}
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubCategory() {
|
||||||
|
return subCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubCategory(String subCategory) {
|
||||||
|
this.subCategory = subCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelectedItem() {
|
||||||
|
return selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedItem(String selectedItem) {
|
||||||
|
this.selectedItem = selectedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(String price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBookingDate() {
|
||||||
|
return bookingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBookingDate(String bookingDate) {
|
||||||
|
this.bookingDate = bookingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBookingTime() {
|
||||||
|
return bookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBookingTime(String bookingTime) {
|
||||||
|
this.bookingTime = bookingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPatientId() {
|
||||||
|
return patientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPatientId(String patientId) {
|
||||||
|
this.patientId = patientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguagePreference() {
|
||||||
|
return languagePreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguagePreference(String languagePreference) {
|
||||||
|
this.languagePreference = languagePreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMenuPage() {
|
||||||
|
return menuPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMenuPage(Integer menuPage) {
|
||||||
|
this.menuPage = menuPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastPhoneNumberId() {
|
||||||
|
return lastPhoneNumberId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastPhoneNumberId(String lastPhoneNumberId) {
|
||||||
|
this.lastPhoneNumberId = lastPhoneNumberId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getLastInteractionTime() {
|
||||||
|
return lastInteractionTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastInteractionTime(Long lastInteractionTime) {
|
||||||
|
this.lastInteractionTime = lastInteractionTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.example.whatsappautomation.repository;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.Appointment;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
|
||||||
|
List<Appointment> findByPhoneNumber(String phoneNumber);
|
||||||
|
Optional<Appointment> findByAppointmentId(String appointmentId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.example.whatsappautomation.repository;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.ServiceBooking;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ServiceBookingRepository extends JpaRepository<ServiceBooking, Long> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.example.whatsappautomation.repository;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.TestBooking;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TestBookingRepository extends JpaRepository<TestBooking, Long> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.example.whatsappautomation.repository;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.UserState;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserStateRepository extends JpaRepository<UserState, String> {
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
19
src/main/resources/application.properties
Normal file
19
src/main/resources/application.properties
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
spring.application.name=whatsapp-automation
|
||||||
|
server.port=8085
|
||||||
|
|
||||||
|
whatsapp.access-token=EAAOZCONMSXCkBSQhQ1hh9oJ7J9kYvZBvZAQjz3SH0KKbZC709ES9afccFJ95uCh8TuWVgxU6cFjZALkTSfGBy15FU1uwdWDimSMUytSmW7NzSLQ9RCq6RgONcIbKuNRioYwO2uPJu8RqhmoLeyztqzzBpGupN4jn9yOkvCXClL0IvfFdFwyvTb4zl3uR0ZB95bQ6bpFDvH0ZAM8fwYirdM6hgIjhoUitKkGRU6ONbADCD5HvZAUSZB5idETz0C3WR8LYMZBZAbZCrwZA2Iw8L2BcDTIEI
|
||||||
|
whatsapp.verify-token=my_whatsapp_verify_token_123
|
||||||
|
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/rmc_bot
|
||||||
|
spring.datasource.username=postgres
|
||||||
|
spring.datasource.password=arjun@132006
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||||
|
spring.jpa.show-sql=true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
conversation.inactivity.timeout=1200
|
||||||
|
chatbot.new-workflow.enabled=true
|
||||||
|
spring.messages.encoding=UTF-8
|
||||||
92
src/main/resources/messages.properties
Normal file
92
src/main/resources/messages.properties
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
welcome.body=👋 Welcome to Rajalakshmi Medical College! I'm your virtual healthcare assistant. Please choose one of the following options:
|
||||||
|
menu.btn.services=My Services
|
||||||
|
menu.btn.tests=My Tests
|
||||||
|
menu.btn.booking=Book Appointment
|
||||||
|
menu.services=🩺 My Services\n\nPlease choose a category:\n1️⃣ Nursing Services\n2️⃣ Attender Services\n3️⃣ Physiotherapy\n4️⃣ Medical Procedures\n5️⃣ Digital Consultation\n6️⃣ Home Care Packages\n\nReply with the option number.
|
||||||
|
menu.tests=🏥 Rajalakshmi Medical College\nWelcome to Laboratory Services.\n\nPlease choose a Health Package Category.\n1️⃣ Fever Panel Tests\n2️⃣ Heart Health\n3️⃣ Joint & Bone\n4️⃣ Master Health Check Plans\n5️⃣ Pregnancy\n6️⃣ Sugar Control Series\n7️⃣ Women's Wellness Plans\n8️⃣ Search Test\n\nReply with the option number.
|
||||||
|
menu.doctor=👨⚕️ Doctor Appointment\nPlease choose an option\n1️⃣ Book New Appointment\n2️⃣ View My Appointments\n3️⃣ Reschedule Appointment\n4️⃣ Cancel Appointment\n\nReply with the option number.
|
||||||
|
invalid.option=Invalid option. Please try again.
|
||||||
|
|
||||||
|
new.menu.title=How may I help you today?
|
||||||
|
new.menu.opt1=🚨 Emergency Contact
|
||||||
|
new.menu.opt2=👨⚕️ Doctor Appointment
|
||||||
|
new.menu.opt3=🏥 Health Packages
|
||||||
|
new.menu.opt4=🔬 Book Lab Test
|
||||||
|
new.menu.opt5=💼 Job Enquiry
|
||||||
|
new.menu.opt6=📞 Assistance
|
||||||
|
new.menu.btn=Select Option
|
||||||
|
new.menu.list=Main Menu
|
||||||
|
|
||||||
|
new.emergency.text=For emergency assistance, please contact our emergency department immediately.\n📞 Emergency Contact: +91 44 2715 2000
|
||||||
|
new.btn.main_menu=Main Menu
|
||||||
|
new.btn.exit=Exit
|
||||||
|
new.btn.back=⬅️ Back
|
||||||
|
new.exit.message=Thank you for contacting Rajalakshmi Medical College. Have a healthy day! 👋
|
||||||
|
|
||||||
|
new.assistance.text=Certainly. For assistance, you can contact our reception team.\n📞 Reception Contact Number: +91 44 2715 2000
|
||||||
|
|
||||||
|
new.job.ask_phone=Please reply with your 10-digit Contact Number.
|
||||||
|
new.job.invalid_phone=Invalid phone number. Please enter a 10-digit number.
|
||||||
|
new.job.ask_email=Thank you. Now please reply with your Email ID.
|
||||||
|
new.job.invalid_email=Invalid email format. Please enter a valid Email ID.
|
||||||
|
new.job.success=Thank you. Your details have been received successfully.\nOur recruitment team will contact you regarding suitable opportunities.\n📞 HR Contact: +91 44 2715 2000 (hr@rmchospital.com)
|
||||||
|
|
||||||
|
new.doc.dept.title=Please select a speciality.
|
||||||
|
new.doc.dept.btn=View Specialities
|
||||||
|
new.doc.dept.list=Specialities
|
||||||
|
new.doc.sel.title=Please select a Doctor.
|
||||||
|
new.doc.sel.btn=View Doctors
|
||||||
|
new.doc.sel.list=Doctors
|
||||||
|
new.doc.ask_date=Please enter your preferred visit date in DD/MM/YYYY format.
|
||||||
|
new.doc.invalid_date=Invalid date format or date is invalid. Please reply with a valid date in DD/MM/YYYY format.
|
||||||
|
new.doc.time.title=Please choose an available time slot.
|
||||||
|
new.doc.time.btn=View Time Slots
|
||||||
|
new.doc.time.list=Time Slots
|
||||||
|
new.doc.summary=👨⚕️ Doctor: {0}\n🏥 Speciality: {1}\n📅 Date: {2}\n⏰ Time: {3}
|
||||||
|
new.doc.confirm_btn=Confirm Appointment
|
||||||
|
new.doc.cancel_btn=Cancel
|
||||||
|
new.doc.success=✅ Your appointment has been successfully booked.\n\nDoctor: {0}\nSpeciality: {1}\nDate: {2}\nTime: {3}\nAppointment ID: {4}
|
||||||
|
|
||||||
|
new.pkg.title=Please select a Healthcare Package.
|
||||||
|
new.pkg.btn=View Packages
|
||||||
|
new.pkg.list=Packages
|
||||||
|
new.pkg.opt1=Gen Health (₹1500)
|
||||||
|
new.pkg.opt2=Cardiac Pkg (₹3000)
|
||||||
|
new.pkg.opt3=Maternity (₹2500)
|
||||||
|
new.pkg.ask_coupon=Do you have a coupon code? Please type the code below, or click 'Skip Coupon' to proceed.
|
||||||
|
new.pkg.btn_skip=Skip Coupon
|
||||||
|
new.pkg.invalid_coupon=Invalid coupon code. Please try again or click 'Skip Coupon'.
|
||||||
|
new.pkg.ask_date=Please enter your preferred date in DD/MM/YYYY format.
|
||||||
|
new.pkg.ask_time=Please select a preferred time slot.
|
||||||
|
new.pkg.confirm_title=Please confirm your booking details:
|
||||||
|
new.pkg.summary=📦 Package: {0}\n📅 Date: {1}\n⏰ Time: {2}\n💵 Rate: {3} (Coupon: {4})
|
||||||
|
new.pkg.success=✅ Healthcare package scheduled successfully.\n\nPackage: {0}\nDate: {1}\nTime: {2}\nFinal Rate: {3}\nBooking ID: {4}
|
||||||
|
|
||||||
|
new.inv.title=Please choose the investigation type.
|
||||||
|
new.inv.btn=Choose Type
|
||||||
|
new.inv.list=Types
|
||||||
|
new.inv.opt1=🩸 Blood / Lab
|
||||||
|
new.inv.opt2=🩻 Radiology
|
||||||
|
|
||||||
|
new.blood.title=Please select a blood test.
|
||||||
|
new.blood.btn=View Tests
|
||||||
|
new.blood.list=Tests
|
||||||
|
new.blood.opt1=CBC Test (₹300)
|
||||||
|
new.blood.opt2=LFT Test (₹500)
|
||||||
|
new.blood.opt3=Lipid Profile (₹450)
|
||||||
|
new.blood.opt4=HbA1c Test (₹350)
|
||||||
|
new.blood.confirm=You have selected:\n🧪 {0}\nRate: {1}\nWould you like to confirm?
|
||||||
|
new.blood.btn_confirm=Confirm
|
||||||
|
new.blood.btn_cancel=Cancel
|
||||||
|
|
||||||
|
new.radio.title=Please select a Radiology service.
|
||||||
|
new.radio.btn=View Services
|
||||||
|
new.radio.list=Services
|
||||||
|
new.radio.opt1=X-Ray (₹500)
|
||||||
|
new.radio.opt2=CT Scan (₹2500)
|
||||||
|
new.radio.opt3=MRI (₹5000)
|
||||||
|
new.radio.opt4=Ultrasound (₹1200)
|
||||||
|
|
||||||
|
new.btn.confirm=Confirm Booking
|
||||||
|
new.btn.cancel=Cancel
|
||||||
|
new.booking.success=✅ Booking scheduled successfully.\n\nService: {0}\nDate: {1}\nTime: {2}\nRate: {3}\nBooking ID: {4}
|
||||||
293
src/main/resources/messages_en.properties
Normal file
293
src/main/resources/messages_en.properties
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
welcome.body=👋 Welcome to Rajalakshmi Medical College! I'm your virtual healthcare assistant. Please choose one of the following options:
|
||||||
|
menu.btn.services=My Services
|
||||||
|
menu.btn.tests=My Tests
|
||||||
|
menu.btn.booking=Book Appointment
|
||||||
|
menu.services=🩺 My Services\n\nPlease choose a category:\n1️⃣ Nursing Services\n2️⃣ Attender Services\n3️⃣ Physiotherapy\n4️⃣ Medical Procedures\n5️⃣ Digital Consultation\n6️⃣ Home Care Packages\n\nReply with the option number.
|
||||||
|
menu.tests=🏥 Rajalakshmi Medical College\nWelcome to Laboratory Services.\n\nPlease choose a Health Package Category.\n1️⃣ Fever Panel Tests\n2️⃣ Heart Health\n3️⃣ Joint & Bone\n4️⃣ Master Health Check Plans\n5️⃣ Pregnancy\n6️⃣ Sugar Control Series\n7️⃣ Women's Wellness Plans\n8️⃣ Search Test\n\nReply with the option number.
|
||||||
|
menu.doctor=👨⚕️ Doctor Appointment\nPlease choose an option\n1️⃣ Book New Appointment\n2️⃣ View My Appointments\n3️⃣ Reschedule Appointment\n4️⃣ Cancel Appointment\n\nReply with the option number.
|
||||||
|
invalid.option=Invalid option. Please try again.
|
||||||
|
|
||||||
|
# Tests Menu
|
||||||
|
tests.main.title=🩺 My Tests\n\nPlease choose a category:
|
||||||
|
tests.main.btn=View Tests
|
||||||
|
tests.main.list=Test Categories
|
||||||
|
tests.main.opt1=Fever Panel
|
||||||
|
tests.main.opt2=Heart Health
|
||||||
|
tests.main.opt3=Joint & Bone
|
||||||
|
tests.main.opt4=Master Health
|
||||||
|
tests.main.opt5=Pregnancy
|
||||||
|
tests.main.opt6=Sugar Control
|
||||||
|
tests.main.opt7=Women's Wellness
|
||||||
|
|
||||||
|
tests.fever.title=🌡️ Fever Panel Tests
|
||||||
|
tests.fever.btn=View Tests
|
||||||
|
tests.fever.list=Tests
|
||||||
|
tests.fever.opt1=Fever Ultra Panel
|
||||||
|
tests.fever.opt2=CBC
|
||||||
|
tests.fever.opt3=IgE Total Serum
|
||||||
|
tests.fever.opt4=LFT
|
||||||
|
tests.fever.opt5=Stool Occult Blood
|
||||||
|
tests.fever.desc1=₹1,999 (11 Tests)
|
||||||
|
tests.fever.desc2=₹300
|
||||||
|
tests.fever.desc3=₹400
|
||||||
|
tests.fever.desc4=₹500
|
||||||
|
tests.fever.desc5=₹250
|
||||||
|
|
||||||
|
tests.heart.title=❤️ Heart Health Packages
|
||||||
|
tests.heart.btn=View Packages
|
||||||
|
tests.heart.list=Packages
|
||||||
|
tests.heart.opt1=Heart Health Plus
|
||||||
|
tests.heart.opt2=Amaya Int Cardiac
|
||||||
|
tests.heart.opt3=Cardiac Pkg - Amaya
|
||||||
|
tests.heart.opt4=Healthy Heart Basic
|
||||||
|
tests.heart.opt5=Heart Basic
|
||||||
|
tests.heart.opt6=Heart Master
|
||||||
|
tests.heart.opt7=Hypertension Comp
|
||||||
|
|
||||||
|
tests.joint.title=🦴 Joint & Bone Packages
|
||||||
|
tests.joint.opt1=Joint Care Plus
|
||||||
|
tests.joint.opt2=Arthritis Package
|
||||||
|
|
||||||
|
tests.master.title=🩺 Master Health Check Plans
|
||||||
|
tests.master.btn=View Plans
|
||||||
|
tests.master.list=Plans
|
||||||
|
tests.master.opt1=Master Health Basic
|
||||||
|
tests.master.opt2=Master Health Exec
|
||||||
|
tests.master.opt3=Master Health Prem
|
||||||
|
|
||||||
|
tests.preg.title=🤰 Pregnancy Packages
|
||||||
|
tests.preg.opt1=Pregnancy Basic
|
||||||
|
tests.preg.opt2=Pregnancy Advanced
|
||||||
|
|
||||||
|
tests.sugar.title=🩸 Sugar Control Series
|
||||||
|
tests.sugar.btn=View Packages
|
||||||
|
tests.sugar.list=Packages
|
||||||
|
tests.sugar.opt1=Sugar Control Basic
|
||||||
|
tests.sugar.opt2=Sugar Control Plus
|
||||||
|
tests.sugar.opt3=Diabetes Monitoring
|
||||||
|
tests.sugar.opt4=HbA1c Comprehensive
|
||||||
|
|
||||||
|
tests.women.title=👩 Women's Wellness Plans
|
||||||
|
tests.women.opt1=Wellness Basic
|
||||||
|
tests.women.opt2=Wellness Advanced
|
||||||
|
|
||||||
|
# Doctor Menu
|
||||||
|
doc.main.title=🩺 Doctor Appointments
|
||||||
|
doc.main.btn=View Options
|
||||||
|
doc.main.list=Options
|
||||||
|
doc.main.opt1=Book New Appointment
|
||||||
|
doc.main.opt2=View Appointments
|
||||||
|
doc.main.opt3=Reschedule Appointment
|
||||||
|
doc.main.opt4=Cancel Appointment
|
||||||
|
|
||||||
|
doc.dept.title=Please select a department.
|
||||||
|
doc.dept.btn=View Departments
|
||||||
|
doc.dept.list=Departments
|
||||||
|
doc.dept.opt1=General Medicine
|
||||||
|
doc.dept.opt2=Cardiology
|
||||||
|
doc.dept.opt3=Orthopedics
|
||||||
|
doc.dept.opt4=Neurology
|
||||||
|
doc.dept.opt5=Pediatrics
|
||||||
|
doc.dept.opt6=Gynecology
|
||||||
|
doc.dept.opt7=Dermatology
|
||||||
|
doc.dept.opt8=ENT
|
||||||
|
doc.dept.opt9=Ophthalmology
|
||||||
|
|
||||||
|
doc.sel.title=Available Doctors
|
||||||
|
doc.sel.btn=Select Doctor
|
||||||
|
doc.sel.list=Doctors
|
||||||
|
|
||||||
|
doc.date.title=Available Dates
|
||||||
|
doc.date.btn=Select Date
|
||||||
|
doc.date.list=Dates
|
||||||
|
|
||||||
|
doc.time.title=Available Time Slots
|
||||||
|
doc.time.btn=Select Time
|
||||||
|
doc.time.list=Time Slots
|
||||||
|
|
||||||
|
doc.summary.title=Appointment Summary
|
||||||
|
doc.summary.btn=Select Option
|
||||||
|
doc.summary.list=Options
|
||||||
|
doc.summary.confirm=Confirm Booking
|
||||||
|
doc.summary.change=Change Time
|
||||||
|
doc.summary.cancel=Cancel
|
||||||
|
|
||||||
|
doc.reschedule.ask=Please enter your Appointment ID.
|
||||||
|
doc.cancel.ask=Please enter your Appointment ID.
|
||||||
|
doc.no_apps=You have no upcoming appointments.
|
||||||
|
|
||||||
|
# Confirmations
|
||||||
|
booking.service.confirm=✅ Booking Confirmed\nService: {0}\nAmount: {1}\n\nPlease share:\n📍 Service Address\nor\n📍 Live Location
|
||||||
|
booking.ask_address=Please enter your address.
|
||||||
|
booking.ask_date=Thank you.\nPlease reply with your preferred date.\n\n📅 DD/MM/YYYY
|
||||||
|
booking.ask_time.title=Please choose the preferred start time.
|
||||||
|
booking.ask_time.btn=Select Time
|
||||||
|
booking.ask_time.list=Available Slots
|
||||||
|
booking.ask_time.opt1=9 AM - 1 PM
|
||||||
|
booking.ask_time.opt2=1 PM - 5 PM
|
||||||
|
booking.ask_time.opt3=5 PM - 9 PM
|
||||||
|
|
||||||
|
booking.service.success=✅ Service Booked Successfully\nBooking ID:\n{0}\nPatient ID:\n{1}\n\nPlease provide your Patient ID at the hospital billing desk for payment.\n\nOur nursing team will contact you shortly.\nThank you for choosing Rajalakshmi Medical College.
|
||||||
|
|
||||||
|
booking.test.confirm_pkg=Selected Package\n\n{0}\nPrice:\n{1}\nWould you like to continue?
|
||||||
|
booking.test.btn_book=Book Now
|
||||||
|
booking.test.btn_back=Back
|
||||||
|
booking.test.ask_coll=How would you like to provide your sample?
|
||||||
|
booking.test.coll_home=Home Collection
|
||||||
|
booking.test.coll_visit=Visit Laboratory
|
||||||
|
booking.test.ask_date=Please reply with your preferred date.\n📅 DD/MM/YYYY
|
||||||
|
booking.test.ask_time.title=Choose your preferred time slot.
|
||||||
|
booking.test.ask_time.btn=Select Slot
|
||||||
|
booking.test.ask_time.list=Time Slots
|
||||||
|
booking.test.opt1=7 AM - 9 AM
|
||||||
|
booking.test.opt2=9 AM - 11 AM
|
||||||
|
booking.test.opt3=11 AM - 1 PM
|
||||||
|
|
||||||
|
booking.test.success=✅ Booking Successful\nBooking ID:\n{0}\nPatient ID:\n{1}\n\nPackage:\n{2}\nAmount:\n{3}\n\nPlease provide your Patient ID at the hospital billing desk for payment.\n\nCollection:\n{4}\nTechnician Visit\n{5}\n{6}\n\nThank you for choosing Rajalakshmi Medical College.
|
||||||
|
|
||||||
|
# Final Main Menu
|
||||||
|
menu.final.title=Would you like to go back to the Main Menu?
|
||||||
|
menu.final.yes=Main Menu
|
||||||
|
menu.final.no=Exit
|
||||||
|
|
||||||
|
doc.booking.success=✅ Appointment Booked Successfully\n\nAppointment ID:\n{0}\n\nPatient ID:\n{1}\n\nPlease provide your Patient ID at the hospital billing desk for payment.\n\nDoctor:\n{2}\n\nDepartment:\n{3}\n\nDate:\n{4}\n\nTime:\n{5}\n\nPlease arrive 15 minutes before your appointment.\nThank you for choosing Rajalakshmi Medical College
|
||||||
|
doc.view_apps.title=Your Upcoming Appointments
|
||||||
|
doc.view_apps.item=Appointment ID:\n{0}\nDoctor:\n{1}\nDepartment:\n{2}\nDate:\n{3}\nTime:\n{4}\nStatus:\n✅ {5}
|
||||||
|
doc.view_apps.btn=Select Option
|
||||||
|
doc.view_apps.list=Options
|
||||||
|
doc.view_apps.opt1=Cancel Appointment
|
||||||
|
doc.view_apps.opt2=Reschedule
|
||||||
|
doc.view_apps.opt3=Main Menu
|
||||||
|
doc.invalid_id=Invalid Appointment ID. Please try again.
|
||||||
|
doc.reschedule.time.title=Available New Time Slots
|
||||||
|
doc.reschedule.time.btn=Select Time
|
||||||
|
doc.reschedule.time.list=Time Slots
|
||||||
|
doc.reschedule.success=✅ Appointment Rescheduled\nDoctor:\n{0}\nNew Time:\n{1}\nDate:\n{2}
|
||||||
|
error.generic=Error. Please try again.
|
||||||
|
doc.cancel.confirm_msg=Are you sure you want to cancel?
|
||||||
|
doc.cancel.yes=Yes
|
||||||
|
doc.cancel.no=No
|
||||||
|
doc.cancel.success=✅ Appointment Cancelled Successfully.
|
||||||
|
|
||||||
|
pagination.next=Next ➡️
|
||||||
|
# Services Menu
|
||||||
|
services.nursing.title=💉 Nursing Services\n\nChoose:
|
||||||
|
services.nursing.opt1=Male Nurse
|
||||||
|
services.nursing.opt2=Female Nurse
|
||||||
|
services.attender.title=🤝 Attender Services
|
||||||
|
services.attender.opt1=Male Attender
|
||||||
|
services.attender.opt2=Female Attender
|
||||||
|
services.physio.title=🏃 Physiotherapy\n\nPrice: ₹700\n\nWould you like to book?
|
||||||
|
services.physio.yes=Yes
|
||||||
|
services.physio.no=No
|
||||||
|
services.medproc.title=🩺 Medical Procedures
|
||||||
|
services.medproc.btn=View Options
|
||||||
|
services.medproc.list=Procedures
|
||||||
|
services.medproc.opt1=IM Injection
|
||||||
|
services.medproc.opt2=IV Injection
|
||||||
|
services.medproc.opt3=Nebulization
|
||||||
|
services.medproc.opt4=Dressing Charges
|
||||||
|
services.medproc.opt5=Catheterization
|
||||||
|
services.digital.title=💻 Digital Consultation\n\nConsultation Fee: ₹750\n\nWould you like to schedule?
|
||||||
|
services.digital.yes=Yes
|
||||||
|
services.digital.back=Back
|
||||||
|
services.homecare.title=🏡 Home Care Packages
|
||||||
|
services.homecare.btn=View Services
|
||||||
|
services.homecare.list=Services
|
||||||
|
services.homecare.opt1=Nursing Care
|
||||||
|
services.homecare.opt2=Attender Care
|
||||||
|
services.homecare.opt3=Physiotherapy
|
||||||
|
services.homecare.opt4=Medical Procedures
|
||||||
|
services.main.title=🩺 My Services\n\nPlease choose a category:
|
||||||
|
services.main.btn=View Services
|
||||||
|
services.main.list=Service Categories
|
||||||
|
services.main.opt1=Nursing Services
|
||||||
|
services.main.opt2=Attender Services
|
||||||
|
services.main.opt3=Physiotherapy
|
||||||
|
services.main.opt4=Medical Procedures
|
||||||
|
services.main.opt5=Digital Consult
|
||||||
|
services.main.opt6=Home Care Packages
|
||||||
|
|
||||||
|
new.menu.title=How may I help you today?
|
||||||
|
new.menu.opt1=🚨 Emergency Contact
|
||||||
|
new.menu.opt2=👨⚕️ Doctor Appointment
|
||||||
|
new.menu.opt3=🏥 Health Packages
|
||||||
|
new.menu.opt4=🔬 Book Lab Test
|
||||||
|
new.menu.opt5=💼 Job Enquiry
|
||||||
|
new.menu.opt6=📞 Assistance
|
||||||
|
new.menu.btn=Select Option
|
||||||
|
new.menu.list=Main Menu
|
||||||
|
|
||||||
|
new.emergency.text=For emergency assistance, please contact our emergency department immediately.\n📞 Emergency Contact: +91 44 2715 2000
|
||||||
|
new.btn.main_menu=Main Menu
|
||||||
|
new.btn.exit=Exit
|
||||||
|
new.btn.back=⬅️ Back
|
||||||
|
new.exit.message=Thank you for contacting Rajalakshmi Medical College. Have a healthy day! 👋
|
||||||
|
|
||||||
|
new.assistance.text=Certainly. For assistance, you can contact our reception team.\n📞 Reception Contact Number: +91 44 2715 2000
|
||||||
|
|
||||||
|
new.job.ask_phone=Please reply with your 10-digit Contact Number.
|
||||||
|
new.job.invalid_phone=Invalid phone number. Please enter a 10-digit number.
|
||||||
|
new.job.ask_email=Thank you. Now please reply with your Email ID.
|
||||||
|
new.job.invalid_email=Invalid email format. Please enter a valid Email ID.
|
||||||
|
new.job.success=Thank you. Your details have been received successfully.\nOur recruitment team will contact you regarding suitable opportunities.\n📞 HR Contact: +91 44 2715 2000 (hr@rmchospital.com)
|
||||||
|
|
||||||
|
new.doc.dept.title=Please select a speciality.
|
||||||
|
new.doc.dept.btn=View Specialities
|
||||||
|
new.doc.dept.list=Specialities
|
||||||
|
new.doc.sel.title=Please select a Doctor.
|
||||||
|
new.doc.sel.btn=View Doctors
|
||||||
|
new.doc.sel.list=Doctors
|
||||||
|
new.doc.ask_date=Please enter your preferred visit date in DD/MM/YYYY format.
|
||||||
|
new.doc.invalid_date=Invalid date format or date is invalid. Please reply with a valid date in DD/MM/YYYY format.
|
||||||
|
new.doc.time.title=Please choose an available time slot.
|
||||||
|
new.doc.time.btn=View Time Slots
|
||||||
|
new.doc.time.list=Time Slots
|
||||||
|
new.doc.summary=👨⚕️ Doctor: {0}\n🏥 Speciality: {1}\n📅 Date: {2}\n⏰ Time: {3}
|
||||||
|
new.doc.confirm_btn=Confirm Appointment
|
||||||
|
new.doc.cancel_btn=Cancel
|
||||||
|
new.doc.success=✅ Your appointment has been successfully booked.\n\nDoctor: {0}\nSpeciality: {1}\nDate: {2}\nTime: {3}\nAppointment ID: {4}
|
||||||
|
|
||||||
|
new.pkg.title=Please select a Healthcare Package.
|
||||||
|
new.pkg.btn=View Packages
|
||||||
|
new.pkg.list=Packages
|
||||||
|
new.pkg.opt1=Gen Health (₹1500)
|
||||||
|
new.pkg.opt2=Cardiac Pkg (₹3000)
|
||||||
|
new.pkg.opt3=Maternity (₹2500)
|
||||||
|
new.pkg.ask_coupon=Do you have a coupon code? Please type the code below, or click 'Skip Coupon' to proceed.
|
||||||
|
new.pkg.btn_skip=Skip Coupon
|
||||||
|
new.pkg.invalid_coupon=Invalid coupon code. Please try again or click 'Skip Coupon'.
|
||||||
|
new.pkg.ask_date=Please enter your preferred date in DD/MM/YYYY format.
|
||||||
|
new.pkg.ask_time=Please select a preferred time slot.
|
||||||
|
new.pkg.confirm_title=Please confirm your booking details:
|
||||||
|
new.pkg.summary=📦 Package: {0}\n📅 Date: {1}\n⏰ Time: {2}\n💵 Rate: {3} (Coupon: {4})
|
||||||
|
new.pkg.success=✅ Healthcare package scheduled successfully.\n\nPackage: {0}\nDate: {1}\nTime: {2}\nFinal Rate: {3}\nBooking ID: {4}
|
||||||
|
|
||||||
|
new.inv.title=Please choose the investigation type.
|
||||||
|
new.inv.btn=Choose Type
|
||||||
|
new.inv.list=Types
|
||||||
|
new.inv.opt1=🩸 Blood / Lab
|
||||||
|
new.inv.opt2=🩻 Radiology
|
||||||
|
|
||||||
|
new.blood.title=Please select a blood test.
|
||||||
|
new.blood.btn=View Tests
|
||||||
|
new.blood.list=Tests
|
||||||
|
new.blood.opt1=CBC Test (₹300)
|
||||||
|
new.blood.opt2=LFT Test (₹500)
|
||||||
|
new.blood.opt3=Lipid Profile (₹450)
|
||||||
|
new.blood.opt4=HbA1c Test (₹350)
|
||||||
|
new.blood.confirm=You have selected:\n🧪 {0}\nRate: {1}\nWould you like to confirm?
|
||||||
|
new.blood.btn_confirm=Confirm
|
||||||
|
new.blood.btn_cancel=Cancel
|
||||||
|
|
||||||
|
new.radio.title=Please select a Radiology service.
|
||||||
|
new.radio.btn=View Services
|
||||||
|
new.radio.list=Services
|
||||||
|
new.radio.opt1=X-Ray (₹500)
|
||||||
|
new.radio.opt2=CT Scan (₹2500)
|
||||||
|
new.radio.opt3=MRI (₹5000)
|
||||||
|
new.radio.opt4=Ultrasound (₹1200)
|
||||||
|
|
||||||
|
new.btn.confirm=Confirm Booking
|
||||||
|
new.btn.cancel=Cancel
|
||||||
|
new.booking.success=✅ Booking scheduled successfully.\n\nService: {0}\nDate: {1}\nTime: {2}\nRate: {3}\nBooking ID: {4}
|
||||||
110
src/main/resources/messages_hi.properties
Normal file
110
src/main/resources/messages_hi.properties
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
welcome.body=आरएमसी अस्पताल में आपका स्वागत है! कृपया अपनी पसंदीदा भाषा चुनें।
|
||||||
|
menu.btn.services=मेरी सेवाएं
|
||||||
|
menu.btn.tests=मेरे परीक्षण
|
||||||
|
menu.btn.booking=अपॉइंटमेंट बुक करें
|
||||||
|
invalid.option=अमान्य विकल्प। कृपया पुनः प्रयास करें।
|
||||||
|
|
||||||
|
new.menu.title=आज मैं आपकी क्या सहायता कर सकता हूँ?
|
||||||
|
new.menu.opt1=🚨 आपातकालीन संपर्क
|
||||||
|
new.menu.opt2=👨⚕️ डॉक्टर अपॉइंटमेंट
|
||||||
|
new.menu.opt3=🏥 स्वास्थ्य पैकेज
|
||||||
|
new.menu.opt4=🔬 जांच अपॉइंटमेंट
|
||||||
|
new.menu.opt5=💼 नौकरी पूछताछ
|
||||||
|
new.menu.opt6=📞 सहायता
|
||||||
|
new.menu.btn=विकल्प चुनें
|
||||||
|
new.menu.list=मुख्य सूची
|
||||||
|
|
||||||
|
new.emergency.text=आपातकालीन सहायता के लिए, कृपया तुरंत हमारे आपातकालीन विभाग से संपर्क करें।\n📞 आपातकालीन संपर्क: +91 44 2715 2000
|
||||||
|
new.btn.main_menu=मुख्य सूची
|
||||||
|
new.btn.exit=बाहर निकलें
|
||||||
|
new.btn.back=⬅️ वापस
|
||||||
|
new.exit.message=राजलक्ष्मी मेडिकल कॉलेज से संपर्क करने के लिए धन्यवाद। आपका दिन स्वस्थ रहे! 👋
|
||||||
|
|
||||||
|
new.assistance.text=बिल्कुल। सहायता के लिए, आप हमारी रिसेप्शन टीम से संपर्क कर सकते हैं।\n📞 रिसेप्शन संपर्क नंबर: +91 44 2715 2000
|
||||||
|
|
||||||
|
new.job.ask_phone=कृपया अपना 10 अंकों का संपर्क नंबर दर्ज करें।
|
||||||
|
new.job.invalid_phone=अमान्य फ़ोन नंबर। कृपया 10 अंकों का नंबर दर्ज करें।
|
||||||
|
new.job.ask_email=धन्यवाद। अब कृपया अपना ईमेल आईडी दर्ज करें।
|
||||||
|
new.job.invalid_email=अमान्य ईमेल प्रारूप। कृपया एक मान्य ईमेल आईडी दर्ज करें।
|
||||||
|
new.job.success=धन्यवाद। आपके विवरण सफलतापूर्वक प्राप्त हो गए हैं।\nहमारी भर्ती टीम उपयुक्त अवसरों के संबंध में आपसे संपर्क करेगी।\n📞 मानव संसाधन (HR) संपर्क: +91 44 2715 2000 (hr@rmchospital.com)
|
||||||
|
|
||||||
|
new.doc.dept.title=कृपया एक विशेषता चुनें।
|
||||||
|
new.doc.dept.btn=विशेषताएं देखें
|
||||||
|
new.doc.dept.list=विशेषताएं
|
||||||
|
new.doc.sel.title=कृपया एक डॉक्टर चुनें।
|
||||||
|
new.doc.sel.btn=डॉक्टर देखें
|
||||||
|
new.doc.sel.list=डॉक्टर
|
||||||
|
new.doc.ask_date=कृपया अपनी पसंदीदा यात्रा तिथि DD/MM/YYYY प्रारूप में दर्ज करें।
|
||||||
|
new.doc.invalid_date=अमान्य तिथि प्रारूप या तिथि अमान्य है। कृपया DD/MM/YYYY प्रारूप में एक मान्य तिथि दर्ज करें।
|
||||||
|
new.doc.time.title=कृपया एक उपलब्ध समय स्लॉट चुनें।
|
||||||
|
new.doc.time.btn=समय स्लॉट देखें
|
||||||
|
new.doc.time.list=समय स्लॉट
|
||||||
|
new.doc.summary=👨⚕️ डॉक्टर: {0}\n🏥 विभाग: {1}\n📅 तिथि: {2}\n⏰ समय: {3}
|
||||||
|
new.doc.confirm_btn=अपॉइंटमेंट की पुष्टि करें
|
||||||
|
new.doc.cancel_btn=रद्द करें
|
||||||
|
new.doc.success=✅ आपका अपॉइंटमेंट सफलतापूर्वक बुक हो गया है।\n\nडॉक्टर: {0}\nविभाग: {1}\nतिथि: {2}\nसमय: {3}\nअपॉइंटमेंट आईडी: {4}
|
||||||
|
|
||||||
|
new.pkg.title=कृपया एक स्वास्थ्य पैकेज चुनें।
|
||||||
|
new.pkg.btn=पैकेज देखें
|
||||||
|
new.pkg.list=पैकेज
|
||||||
|
new.pkg.opt1=सामान्य स्वास्थ्य (₹1500)
|
||||||
|
new.pkg.opt2=हृदय पैकेज (₹3000)
|
||||||
|
new.pkg.opt3=मातृत्व पैकेज (₹2500)
|
||||||
|
new.pkg.ask_coupon=क्या आपके पास कूपन कोड है? कृपया कूपन कोड दर्ज करें, या आगे बढ़ने के लिए 'कूपन छोड़ें' पर क्लिक करें।
|
||||||
|
new.pkg.btn_skip=कूपन छोड़ें
|
||||||
|
new.pkg.invalid_coupon=अमान्य कूपन कोड। कृपया पुनः प्रयास करें या 'कूपन छोड़ें' पर क्लिक करें।
|
||||||
|
new.pkg.ask_date=कृपया पसंदीदा तिथि DD/MM/YYYY प्रारूप में दर्ज करें।
|
||||||
|
new.pkg.ask_time=कृपया पसंदीदा समय स्लॉट चुनें।
|
||||||
|
new.pkg.confirm_title=कृपया अपने बुकिंग विवरण की पुष्टि करें:
|
||||||
|
new.pkg.summary=📦 पैकेज: {0}\n📅 तिथि: {1}\n⏰ समय: {2}\n💵 दर: {3} (कूपन: {4})
|
||||||
|
new.pkg.success=✅ स्वास्थ्य पैकेज सफलतापूर्वक निर्धारित किया गया है।\n\nपैकेज: {0}\nतिथि: {1}\nसमय: {2}\nअंतिम दर: {3}\nबुकिंग आईडी: {4}
|
||||||
|
|
||||||
|
new.inv.title=कृपया जांच का प्रकार चुनें।
|
||||||
|
new.inv.btn=प्रकार चुनें
|
||||||
|
new.inv.list=प्रकार
|
||||||
|
new.inv.opt1=🩸 रक्त / लैब
|
||||||
|
new.inv.opt2=🩻 रेडियोलॉजी
|
||||||
|
|
||||||
|
new.blood.title=कृपया रक्त परीक्षण चुनें।
|
||||||
|
new.blood.btn=परीक्षण देखें
|
||||||
|
new.blood.list=परीक्षण
|
||||||
|
new.blood.opt1=CBC रक्त जांच (₹300)
|
||||||
|
new.blood.opt2=LFT लीवर जांच (₹500)
|
||||||
|
new.blood.opt3=लिपिड प्रोफाइल (₹450)
|
||||||
|
new.blood.opt4=HbA1c शुगर जांच (₹350)
|
||||||
|
new.blood.confirm=आपने चुना है:\n🧪 {0}\nदर: {1}\nक्या आप पुष्टि करना चाहते हैं?
|
||||||
|
new.blood.btn_confirm=पुष्टि करें
|
||||||
|
new.blood.btn_cancel=रद्द करें
|
||||||
|
|
||||||
|
new.radio.title=कृपया रेडियोलॉजी सेवा चुनें।
|
||||||
|
new.radio.btn=सेवाएं देखें
|
||||||
|
new.radio.list=सेवाएं
|
||||||
|
new.radio.opt1=एक्स-रे (₹500)
|
||||||
|
new.radio.opt2=सीटी स्कैन (₹2500)
|
||||||
|
new.radio.opt3=एमआरआई (₹5000)
|
||||||
|
new.radio.opt4=अल्ट्रासाउंड (₹1200)
|
||||||
|
|
||||||
|
new.btn.confirm=बुकिंग की पुष्टि करें
|
||||||
|
new.btn.cancel=रद्द करें
|
||||||
|
new.booking.success=✅ बुकिंग सफलतापूर्वक निर्धारित की गई है।\n\nसेवा: {0}\nतिथि: {1}\nसमय: {2}\nदर: {3}\nबुकिंग आईडी: {4}
|
||||||
|
|
||||||
|
doc.dept.title=कृपया एक विभाग चुनें।
|
||||||
|
doc.dept.btn=विभाग देखें
|
||||||
|
doc.dept.list=विभाग
|
||||||
|
doc.dept.opt1=सामान्य चिकित्सा
|
||||||
|
doc.dept.opt2=हृदय रोग विज्ञान
|
||||||
|
doc.dept.opt3=हड्डी रोग विज्ञान
|
||||||
|
doc.dept.opt4=न्यूरोलॉजी
|
||||||
|
doc.dept.opt5=बाल रोग विज्ञान
|
||||||
|
doc.dept.opt6=स्त्री रोग विज्ञान
|
||||||
|
doc.dept.opt7=त्वचा विज्ञान
|
||||||
|
doc.dept.opt8=ईएनटी
|
||||||
|
doc.dept.opt9=नेत्र विज्ञान
|
||||||
|
|
||||||
|
booking.test.ask_date=कृपया अपनी पसंदीदा तिथि दर्ज करें।\n📅 DD/MM/YYYY
|
||||||
|
booking.test.ask_time.title=अपना पसंदीदा समय स्लॉट चुनें।
|
||||||
|
booking.test.ask_time.btn=स्लॉट चुनें
|
||||||
|
booking.test.ask_time.list=समय स्लॉट
|
||||||
|
booking.test.opt1=सुबह 7 - 9 बजे
|
||||||
|
booking.test.opt2=सुबह 9 - 11 बजे
|
||||||
|
booking.test.opt3=सुबह 11 - दोपहर 1 बजे
|
||||||
293
src/main/resources/messages_ta.properties
Normal file
293
src/main/resources/messages_ta.properties
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
welcome.body=👋 ராஜலட்சுமி மருத்துவக் கல்லூரிக்கு வரவேற்கிறோம்! நான் உங்கள் மெய்நிகர் சுகாதார உதவியாளர். தயவுசெய்து கீழ்க்கண்ட விருப்பங்களில் ஒன்றைத் தேர்ந்தெடுக்கவும்:
|
||||||
|
menu.btn.services=என் சேவைகள்
|
||||||
|
menu.btn.tests=பரிசோதனைகள்
|
||||||
|
menu.btn.booking=முன்பதிவு செய்க
|
||||||
|
menu.services=🩺 எனது சேவைகள்\n\nதயவுசெய்து ஒரு வகையைத் தேர்ந்தெடுக்கவும்:\n1️⃣ செவிலியர் சேவைகள்\n2️⃣ உதவியாளர் சேவைகள்\n3️⃣ பிசியோதெரபி\n4️⃣ மருத்துவ நடைமுறைகள்\n5️⃣ டிஜிட்டல் ஆலோசனை\n6️⃣ வீட்டு பராமரிப்பு தொகுப்புகள்\n\nவிருப்ப எண்ணுடன் பதிலளிக்கவும்.
|
||||||
|
menu.tests=🏥 ராஜலட்சுமி மருத்துவக் கல்லூரி\nஆய்வக சேவைகளுக்கு வரவேற்கிறோம்.\n\nசுகாதாரத் தொகுப்பு வகையைத் தேர்ந்தெடுக்கவும்.\n1️⃣ காய்ச்சல் பரிசோதனைகள்\n2️⃣ இதய ஆரோக்கியம்\n3️⃣ மூட்டு மற்றும் எலும்பு\n4️⃣ முழுமையான உடல் பரிசோதனை\n5️⃣ கர்ப்ப காலம்\n6️⃣ சர்க்கரை கட்டுப்பாடு\n7️⃣ பெண்கள் நல்வாழ்வு\n8️⃣ பரிசோதனையைத் தேடுங்கள்\n\nவிருப்ப எண்ணுடன் பதிலளிக்கவும்.
|
||||||
|
menu.doctor=👨⚕️ மருத்துவர் சந்திப்பு\nதயவுசெய்து ஒரு விருப்பத்தைத் தேர்ந்தெடுக்கவும்\n1️⃣ புதிய சந்திப்பை முன்பதிவு செய்ய\n2️⃣ எனது சந்திப்புகளைப் பார்க்க\n3️⃣ சந்திப்பை மாற்றியமைக்க\n4️⃣ சந்திப்பை ரத்து செய்ய\n\nவிருப்ப எண்ணுடன் பதிலளிக்கவும்.
|
||||||
|
invalid.option=தவறான விருப்பம். தயவுசெய்து மீண்டும் முயற்சிக்கவும்.
|
||||||
|
|
||||||
|
# Tests Menu
|
||||||
|
tests.main.title=🩺 எனது பரிசோதனைகள்\n\nதயவுசெய்து ஒரு வகையைத் தேர்ந்தெடுக்கவும்:
|
||||||
|
tests.main.btn=பரிசோதனைகளை காண்க
|
||||||
|
tests.main.list=பரிசோதனை பிரிவுகள்
|
||||||
|
tests.main.opt1=காய்ச்சல் பரிசோதனை
|
||||||
|
tests.main.opt2=இதய ஆரோக்கியம்
|
||||||
|
tests.main.opt3=மூட்டு & எலும்பு
|
||||||
|
tests.main.opt4=முழு உடல் பரிசோதனை
|
||||||
|
tests.main.opt5=கர்ப்ப காலம்
|
||||||
|
tests.main.opt6=சர்க்கரை கட்டுப்பாடு
|
||||||
|
tests.main.opt7=பெண்கள் நல்வாழ்வு
|
||||||
|
|
||||||
|
tests.fever.title=🌡️ காய்ச்சல் பரிசோதனைகள்
|
||||||
|
tests.fever.btn=பரிசோதனைகளை காண்க
|
||||||
|
tests.fever.list=பரிசோதனைகள்
|
||||||
|
tests.fever.opt1=காய்ச்சல் அல்ட்ரா
|
||||||
|
tests.fever.opt2=CBC
|
||||||
|
tests.fever.opt3=IgE Total Serum
|
||||||
|
tests.fever.opt4=LFT
|
||||||
|
tests.fever.opt5=மலம் பரிசோதனை
|
||||||
|
tests.fever.desc1=₹1,999 (11 பரிசோதனைகள்)
|
||||||
|
tests.fever.desc2=₹300
|
||||||
|
tests.fever.desc3=₹400
|
||||||
|
tests.fever.desc4=₹500
|
||||||
|
tests.fever.desc5=₹250
|
||||||
|
|
||||||
|
tests.heart.title=❤️ இதய ஆரோக்கிய தொகுப்புகள்
|
||||||
|
tests.heart.btn=தொகுப்புகளை காண்க
|
||||||
|
tests.heart.list=தொகுப்புகள்
|
||||||
|
tests.heart.opt1=ஹார்ட் ஹெல்த் பிளஸ்
|
||||||
|
tests.heart.opt2=அமயா கார்டியாக்
|
||||||
|
tests.heart.opt3=அமயா பேக்கேஜ்
|
||||||
|
tests.heart.opt4=ஹெல்தி ஹார்ட் பேசிக்
|
||||||
|
tests.heart.opt5=ஹார்ட் பேசிக்
|
||||||
|
tests.heart.opt6=ஹார்ட் மாஸ்டர்
|
||||||
|
tests.heart.opt7=உயர் ரத்த அழுத்தம்
|
||||||
|
|
||||||
|
tests.joint.title=🦴 மூட்டு மற்றும் எலும்பு தொகுப்புகள்
|
||||||
|
tests.joint.opt1=ஜாயின்ட் கேர் பிளஸ்
|
||||||
|
tests.joint.opt2=ஆர்த்ரிடிஸ் பேக்கேஜ்
|
||||||
|
|
||||||
|
tests.master.title=🩺 முழுமையான உடல் பரிசோதனை திட்டங்கள்
|
||||||
|
tests.master.btn=திட்டங்களை காண்க
|
||||||
|
tests.master.list=திட்டங்கள்
|
||||||
|
tests.master.opt1=மாஸ்டர் பேசிக்
|
||||||
|
tests.master.opt2=மாஸ்டர் எக்ஸிகியூட்
|
||||||
|
tests.master.opt3=மாஸ்டர் பிரீமியம்
|
||||||
|
|
||||||
|
tests.preg.title=🤰 கர்ப்ப கால தொகுப்புகள்
|
||||||
|
tests.preg.opt1=கர்ப்ப காலம் பேசிக்
|
||||||
|
tests.preg.opt2=கர்ப்ப அட்வான்ஸ்டு
|
||||||
|
|
||||||
|
tests.sugar.title=🩸 சர்க்கரை கட்டுப்பாடு தொடர்
|
||||||
|
tests.sugar.btn=தொகுப்புகளை காண்க
|
||||||
|
tests.sugar.list=தொகுப்புகள்
|
||||||
|
tests.sugar.opt1=சர்க்கரை பேசிக்
|
||||||
|
tests.sugar.opt2=சர்க்கரை பிளஸ்
|
||||||
|
tests.sugar.opt3=நீரிழிவு கண்காணிப்பு
|
||||||
|
tests.sugar.opt4=HbA1c விரிவானது
|
||||||
|
|
||||||
|
tests.women.title=👩 பெண்கள் நல்வாழ்வு திட்டங்கள்
|
||||||
|
tests.women.opt1=பெண்கள் பேசிக்
|
||||||
|
tests.women.opt2=பெண்கள் அட்வான்ஸ்டு
|
||||||
|
|
||||||
|
# Doctor Menu
|
||||||
|
doc.main.title=🩺 மருத்துவர் சந்திப்புகள்
|
||||||
|
doc.main.btn=விருப்பங்களை காண்க
|
||||||
|
doc.main.list=விருப்பங்கள்
|
||||||
|
doc.main.opt1=புதிய முன்பதிவு
|
||||||
|
doc.main.opt2=சந்திப்புகளை பார்
|
||||||
|
doc.main.opt3=மாற்றியமைக்க
|
||||||
|
doc.main.opt4=ரத்து செய்ய
|
||||||
|
|
||||||
|
doc.dept.title=தயவுசெய்து ஒரு துறையை தேர்ந்தெடுக்கவும்.
|
||||||
|
doc.dept.btn=துறைகளை காண்க
|
||||||
|
doc.dept.list=துறைகள்
|
||||||
|
doc.dept.opt1=பொது மருத்துவம்
|
||||||
|
doc.dept.opt2=இருதயவியல்
|
||||||
|
doc.dept.opt3=எலும்பியல்
|
||||||
|
doc.dept.opt4=நரம்பியல்
|
||||||
|
doc.dept.opt5=குழந்தை மருத்துவம்
|
||||||
|
doc.dept.opt6=மகப்பேறியல்
|
||||||
|
doc.dept.opt7=தோல் மருத்துவம்
|
||||||
|
doc.dept.opt8=காது மூக்கு தொண்டை
|
||||||
|
doc.dept.opt9=கண் மருத்துவம்
|
||||||
|
|
||||||
|
doc.sel.title=கிடைக்கும் மருத்துவர்கள்
|
||||||
|
doc.sel.btn=மருத்துவரை தேர்ந்தெடுங்கள்
|
||||||
|
doc.sel.list=மருத்துவர்கள்
|
||||||
|
|
||||||
|
doc.date.title=கிடைக்கும் தேதிகள்
|
||||||
|
doc.date.btn=தேதியை தேர்ந்தெடுங்கள்
|
||||||
|
doc.date.list=தேதிகள்
|
||||||
|
|
||||||
|
doc.time.title=கிடைக்கும் நேரங்கள்
|
||||||
|
doc.time.btn=நேரத்தை தேர்ந்தெடுங்கள்
|
||||||
|
doc.time.list=நேரங்கள்
|
||||||
|
|
||||||
|
doc.summary.title=சந்திப்பு சுருக்கம்
|
||||||
|
doc.summary.btn=விருப்பத்தை தேர்ந்தெடுங்கள்
|
||||||
|
doc.summary.list=விருப்பங்கள்
|
||||||
|
doc.summary.confirm=உறுதிப்படுத்துக
|
||||||
|
doc.summary.change=நேரத்தை மாற்றவும்
|
||||||
|
doc.summary.cancel=ரத்து செய்யவும்
|
||||||
|
|
||||||
|
doc.reschedule.ask=தயவுசெய்து உங்கள் சந்திப்பு ஐடியை உள்ளிடவும்.
|
||||||
|
doc.cancel.ask=தயவுசெய்து உங்கள் சந்திப்பு ஐடியை உள்ளிடவும்.
|
||||||
|
doc.no_apps=உங்களுக்கு வரவிருக்கும் சந்திப்புகள் ஏதும் இல்லை.
|
||||||
|
|
||||||
|
# Confirmations
|
||||||
|
booking.service.confirm=✅ முன்பதிவு உறுதி செய்யப்பட்டது\nசேவை: {0}\nகட்டணம்: {1}\n\nதயவுசெய்து பகிரவும்:\n📍 சேவை முகவரி\nஅல்லது\n📍 நேரலை இருப்பிடம் (Live Location)
|
||||||
|
booking.ask_address=தயவுசெய்து உங்கள் முகவரியை உள்ளிடவும்.
|
||||||
|
booking.ask_date=நன்றி.\nஉங்கள் விருப்பமான தேதியை உள்ளிடவும்.\n\n📅 DD/MM/YYYY
|
||||||
|
booking.ask_time.title=விருப்பமான தொடக்க நேரத்தை தேர்வு செய்யவும்.
|
||||||
|
booking.ask_time.btn=நேரத்தை தேர்ந்தெடுக்கவும்
|
||||||
|
booking.ask_time.list=கிடைக்கும் நேரங்கள்
|
||||||
|
booking.ask_time.opt1=காலை 9 - மதியம் 1
|
||||||
|
booking.ask_time.opt2=மதியம் 1 - மாலை 5
|
||||||
|
booking.ask_time.opt3=மாலை 5 - இரவு 9
|
||||||
|
|
||||||
|
booking.service.success=✅ சேவை வெற்றிகரமாக முன்பதிவு செய்யப்பட்டது\nமுன்பதிவு எண்:\n{0}\nநோயாளி எண்:\n{1}\n\nபணம் செலுத்த உங்கள் நோயாளி எண்ணை மருத்துவமனை பில்லிங் டெஸ்கில் வழங்கவும்.\n\nஎங்கள் செவிலியர் குழு உங்களை விரைவில் தொடர்புகொள்ளும்.\nராஜலட்சுமி மருத்துவக் கல்லூரியை தேர்ந்தெடுத்ததற்கு நன்றி.
|
||||||
|
|
||||||
|
booking.test.confirm_pkg=தேர்ந்தெடுக்கப்பட்ட தொகுப்பு\n\n{0}\nவிலை:\n{1}\nநீங்கள் தொடர விரும்புகிறீர்களா?
|
||||||
|
booking.test.btn_book=முன்பதிவு செய்
|
||||||
|
booking.test.btn_back=பின்செல்
|
||||||
|
booking.test.ask_coll=மாதிரியை எவ்வாறு வழங்க விரும்புகிறீர்கள்?
|
||||||
|
booking.test.coll_home=வீட்டு மாதிரி சேகரிப்பு
|
||||||
|
booking.test.coll_visit=ஆய்வகத்திற்கு வருகை
|
||||||
|
booking.test.ask_date=உங்கள் விருப்பமான தேதியை உள்ளிடவும்.\n📅 DD/MM/YYYY
|
||||||
|
booking.test.ask_time.title=விருப்பமான நேரத்தை தேர்வு செய்யவும்.
|
||||||
|
booking.test.ask_time.btn=நேரத்தை தேர்ந்தெடுக்கவும்
|
||||||
|
booking.test.ask_time.list=நேரங்கள்
|
||||||
|
booking.test.opt1=காலை 7 - காலை 9
|
||||||
|
booking.test.opt2=காலை 9 - காலை 11
|
||||||
|
booking.test.opt3=காலை 11 - மதியம் 1
|
||||||
|
|
||||||
|
booking.test.success=✅ முன்பதிவு வெற்றிகரமாக முடிந்தது\nமுன்பதிவு எண்:\n{0}\nநோயாளி எண்:\n{1}\n\nதொகுப்பு:\n{2}\nகட்டணம்:\n{3}\n\nபணம் செலுத்த உங்கள் நோயாளி எண்ணை மருத்துவமனை பில்லிங் டெஸ்கில் வழங்கவும்.\n\nசேகரிப்பு:\n{4}\nதொழில்நுட்ப வல்லுநர் வருகை\n{5}\n{6}\n\nராஜலட்சுமி மருத்துவக் கல்லூரியை தேர்ந்தெடுத்ததற்கு நன்றி.
|
||||||
|
|
||||||
|
# Final Main Menu
|
||||||
|
menu.final.title=நீங்கள் பிரதான மெனுவுக்குச் செல்ல விரும்புகிறீர்களா?
|
||||||
|
menu.final.yes=பிரதான மெனு
|
||||||
|
menu.final.no=வெளியேறு
|
||||||
|
|
||||||
|
doc.booking.success=✅ சந்திப்பு வெற்றிகரமாக முன்பதிவு செய்யப்பட்டது\n\nசந்திப்பு ஐடி:\n{0}\n\nநோயாளி ஐடி:\n{1}\n\nபணம் செலுத்த உங்கள் நோயாளி எண்ணை மருத்துவமனை பில்லிங் டெஸ்கில் வழங்கவும்.\n\nமருத்துவர்:\n{2}\n\nதுறை:\n{3}\n\nதேதி:\n{4}\n\nநேரம்:\n{5}\n\nஉங்கள் சந்திப்புக்கு 15 நிமிடங்களுக்கு முன்பு வரவும்.\nராஜலட்சுமி மருத்துவக் கல்லூரியை தேர்ந்தெடுத்ததற்கு நன்றி
|
||||||
|
doc.view_apps.title=உங்கள் வரவிருக்கும் சந்திப்புகள்
|
||||||
|
doc.view_apps.item=சந்திப்பு ஐடி:\n{0}\nமருத்துவர்:\n{1}\nதுறை:\n{2}\nதேதி:\n{3}\nநேரம்:\n{4}\nநிலை:\n✅ {5}
|
||||||
|
doc.view_apps.btn=விருப்பத்தை தேர்ந்தெடுக்கவும்
|
||||||
|
doc.view_apps.list=விருப்பங்கள்
|
||||||
|
doc.view_apps.opt1=ரத்து செய்யவும்
|
||||||
|
doc.view_apps.opt2=மாற்றியமைக்க
|
||||||
|
doc.view_apps.opt3=பிரதான மெனு
|
||||||
|
doc.invalid_id=தவறான சந்திப்பு ஐடி. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.
|
||||||
|
doc.reschedule.time.title=கிடைக்கும் புதிய நேரங்கள்
|
||||||
|
doc.reschedule.time.btn=நேரத்தை தேர்ந்தெடுக்கவும்
|
||||||
|
doc.reschedule.time.list=நேரங்கள்
|
||||||
|
doc.reschedule.success=✅ சந்திப்பு மாற்றியமைக்கப்பட்டது\nமருத்துவர்:\n{0}\nபுதிய நேரம்:\n{1}\nதேதி:\n{2}
|
||||||
|
error.generic=பிழை. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.
|
||||||
|
doc.cancel.confirm_msg=நிச்சயமாக ரத்து செய்ய விரும்புகிறீர்களா?
|
||||||
|
doc.cancel.yes=ஆம்
|
||||||
|
doc.cancel.no=இல்லை
|
||||||
|
doc.cancel.success=✅ சந்திப்பு வெற்றிகரமாக ரத்து செய்யப்பட்டது.
|
||||||
|
|
||||||
|
pagination.next=அடுத்து ➡️
|
||||||
|
# Services Menu
|
||||||
|
services.nursing.title=💉 செவிலியர் சேவைகள்\n\nதேர்வு செய்க:
|
||||||
|
services.nursing.opt1=ஆண் செவிலியர்
|
||||||
|
services.nursing.opt2=பெண் செவிலியர்
|
||||||
|
services.attender.title=🤝 உதவியாளர் சேவைகள்
|
||||||
|
services.attender.opt1=ஆண் உதவியாளர்
|
||||||
|
services.attender.opt2=பெண் உதவியாளர்
|
||||||
|
services.physio.title=🏃 பிசியோதெரபி\n\nவிலை: ₹700\n\nநீங்கள் முன்பதிவு செய்ய விரும்புகிறீர்களா?
|
||||||
|
services.physio.yes=ஆம்
|
||||||
|
services.physio.no=இல்லை
|
||||||
|
services.medproc.title=🩺 மருத்துவ நடைமுறைகள்
|
||||||
|
services.medproc.btn=விருப்பங்களை காண்க
|
||||||
|
services.medproc.list=நடைமுறைகள்
|
||||||
|
services.medproc.opt1=IM ஊசி
|
||||||
|
services.medproc.opt2=IV ஊசி
|
||||||
|
services.medproc.opt3=நெபுலைசேஷன்
|
||||||
|
services.medproc.opt4=கட்டு போடுதல்
|
||||||
|
services.medproc.opt5=வடிகுழாய்
|
||||||
|
services.digital.title=💻 டிஜிட்டல் ஆலோசனை\n\nஆலோசனை கட்டணம்: ₹750\n\nநீங்கள் முன்பதிவு செய்ய விரும்புகிறீர்களா?
|
||||||
|
services.digital.yes=ஆம்
|
||||||
|
services.digital.back=பின்செல்
|
||||||
|
services.homecare.title=🏡 வீட்டு பராமரிப்பு தொகுப்புகள்
|
||||||
|
services.homecare.btn=சேவைகளை காண்க
|
||||||
|
services.homecare.list=சேவைகள்
|
||||||
|
services.homecare.opt1=செவிலியர் பராமரிப்பு
|
||||||
|
services.homecare.opt2=உதவியாளர் பராமரிப்பு
|
||||||
|
services.homecare.opt3=பிசியோதெரபி
|
||||||
|
services.homecare.opt4=மருத்துவ நடைமுறைகள்
|
||||||
|
services.main.title=🩺 எனது சேவைகள்\n\nதயவுசெய்து ஒரு வகையைத் தேர்ந்தெடுக்கவும்:
|
||||||
|
services.main.btn=சேவைகளை காண்க
|
||||||
|
services.main.list=சேவை பிரிவுகள்
|
||||||
|
services.main.opt1=செவிலியர் சேவைகள்
|
||||||
|
services.main.opt2=உதவியாளர் சேவைகள்
|
||||||
|
services.main.opt3=பிசியோதெரபி
|
||||||
|
services.main.opt4=மருத்துவ நடைமுறைகள்
|
||||||
|
services.main.opt5=டிஜிட்டல் ஆலோசனை
|
||||||
|
services.main.opt6=வீட்டு பராமரிப்பு
|
||||||
|
|
||||||
|
new.menu.title=இன்று நான் உங்களுக்கு எவ்வாறு உதவ முடியும்?
|
||||||
|
new.menu.opt1=🚨 அவசர தொடர்பு எண்
|
||||||
|
new.menu.opt2=👨⚕️ மருத்துவர் சந்திப்பு
|
||||||
|
new.menu.opt3=🏥 சுகாதார தொகுப்புகள்
|
||||||
|
new.menu.opt4=🔬 பரிசோதனை முன்பதிவு
|
||||||
|
new.menu.opt5=💼 வேலை வாய்ப்பு
|
||||||
|
new.menu.opt6=📞 உதவி
|
||||||
|
new.menu.btn=விருப்பத்தைத் தேர்ந்தெடுக்கவும்
|
||||||
|
new.menu.list=முதன்மை மெனு
|
||||||
|
|
||||||
|
new.emergency.text=அவசर உதவிக்கு, உடனடியாக எங்கள் அவசர சிகிச்சை பிரிவைத் தொடர்பு கொள்ளவும்.\n📞 அவசர தொடர்பு எண்: +91 44 2715 2000
|
||||||
|
new.btn.main_menu=முதன்மை மெனு
|
||||||
|
new.btn.exit=வெளியேறு
|
||||||
|
new.btn.back=⬅️ பின்னால்
|
||||||
|
new.exit.message=ராஜலட்சுமி மருத்துவக் கல்லூரியைத் தொடர்பு கொண்டதற்கு நன்றி. ஆரோக்கியமான நாளாக அமையட்டும்! 👋
|
||||||
|
|
||||||
|
new.assistance.text=நிச்சயமாக. உதவிக்கு, நீங்கள் எங்கள் வரவேற்புக் குழுவைத் தொடர்பு கொள்ளலாம்.\n📞 வரவேற்பு தொடர்பு எண்: +91 44 2715 2000
|
||||||
|
|
||||||
|
new.job.ask_phone=தயவுசெய்து உங்கள் 10 இலக்க தொடர்பு எண்ணை உள்ளிடவும்.
|
||||||
|
new.job.invalid_phone=தவறான தொலைபேசி எண். தயவுசெய்து 10 இலக்க எண்ணை உள்ளிடவும்.
|
||||||
|
new.job.ask_email=நன்றி. இப்போது தயவுசெய்து உங்கள் மின்னஞ்சல் ஐடியை உள்ளிடவும்.
|
||||||
|
new.job.invalid_email=தவறான மின்னஞ்சல் வடிவம். தயவுசெய்து சரியான மின்னஞ்சல் ஐடியை உள்ளிடவும்.
|
||||||
|
new.job.success=நன்றி. உங்கள் विवरणங்கள் வெற்றிகரமாகப் பெறப்பட்டன.\nஎங்கள் வேலைவாய்ப்பு குழு பொருத்தமான வாய்ப்புகள் குறித்து உங்களைத் தொடர்பு கொள்ளும்.\n📞 மனிதவள (HR) தொடர்பு எண்: +91 44 2715 2000 (hr@rmchospital.com)
|
||||||
|
|
||||||
|
new.doc.dept.title=தயவுசெய்து ஒரு சிறப்புத் துறையைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.doc.dept.btn=சிறப்புத் துறைகளைக் காண்க
|
||||||
|
new.doc.dept.list=சிறப்புத் துறைகள்
|
||||||
|
new.doc.sel.title=தயவுசெய்து ஒரு மருத்துவரைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.doc.sel.btn=மருத்துவர்களைக் காண்க
|
||||||
|
new.doc.sel.list=மருத்துவர்கள்
|
||||||
|
new.doc.ask_date=தயவுசெய்து நீங்கள் வர விரும்பும் தேதியை DD/MM/YYYY வடிவத்தில் உள்ளிடவும்.
|
||||||
|
new.doc.invalid_date=தவறான தேதி வடிவம் அல்லது தேதி செல்லாதது. தயவுசெய்து DD/MM/YYYY வடிவத்தில் சரியான தேதியை உள்ளிடவும்.
|
||||||
|
new.doc.time.title=தயவுசெய்து கிடைக்கக்கூடிய நேரத்தைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.doc.time.btn=நேरங்களைக் காண்க
|
||||||
|
new.doc.time.list=நேரங்கள்
|
||||||
|
new.doc.summary=👨⚕️ மருத்துவர்: {0}\n🏥 சிறப்புத் துறை: {1}\n📅 தேதி: {2}\n⏰ நேரம்: {3}
|
||||||
|
new.doc.confirm_btn=முன்பதிவை உறுதிசெய்
|
||||||
|
new.doc.cancel_btn=ரத்துசெய்
|
||||||
|
new.doc.success=✅ உங்கள் மருத்துவ சந்திப்பு வெற்றிகரமாக முன்பதிவு செய்யப்பட்டது.\n\nமருத்துவர்: {0}\nசிறப்புத் துறை: {1}\nதேதி: {2}\nநேரம்: {3}\nசந்திப்பு ஐடி: {4}
|
||||||
|
|
||||||
|
new.pkg.title=தயவுசெய்து ஒரு சுகாதார தொகுப்பைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.pkg.btn=தொகுப்புகளைக் காண்க
|
||||||
|
new.pkg.list=தொகுப்புகள்
|
||||||
|
new.pkg.opt1=பொது பரிசோதனை (₹1500)
|
||||||
|
new.pkg.opt2=இதய தொகுப்பு (₹3000)
|
||||||
|
new.pkg.opt3=மகப்பேறு (₹2500)
|
||||||
|
new.pkg.ask_coupon=உங்களிடம் ஏதேனும் கூப்பன் குறியீடு உள்ளதா? கூப்பன் குறியீட்டை உள்ளிடவும் அல்லது தொடர 'கூப்பனைத் தவிர்' என்பதைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.pkg.btn_skip=கூப்பனைத் தவிர்
|
||||||
|
new.pkg.invalid_coupon=தவறான கூப்பன் குறியீடு. தயவுசெய்து மீண்டும் முயற்சிக்கவும் அல்லது 'கூப்பனைத் தவிர்' என்பதைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.pkg.ask_date=தயவுசெய்து உங்களுக்கு விருப்பமான தேதியை DD/MM/YYYY வடிவத்தில் உள்ளிடவும்.
|
||||||
|
new.pkg.ask_time=தயவுசெய்து உங்களுக்கு விருப்பமான நேரத்தைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.pkg.confirm_title=முன்பதிவு விவரங்களை உறுதிப்படுத்தவும்:
|
||||||
|
new.pkg.summary=📦 தொகுப்பு: {0}\n📅 தேதி: {1}\n⏰ நேரம்: {2}\n💵 கட்டணம்: {3} (கூப்பன்: {4})
|
||||||
|
new.pkg.success=✅ சுகாதார தொகுப்பு வெற்றிகரமாக திட்டமிடப்பட்டது.\n\nதொகுப்பு: {0}\nதேதி: {1}\nநேரம்: {2}\nஇறுதி கட்டணம்: {3}\nமுன்பதிவு ஐடி: {4}
|
||||||
|
|
||||||
|
new.inv.title=தயவுசெய்து பரிசோதனை வகையைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.inv.btn=வகையைத் தேர்ந்தெடுக்கவும்
|
||||||
|
new.inv.list=பரிசோதனை வகைகள்
|
||||||
|
new.inv.opt1=🩸 இரத்தம் / ஆய்வகம்
|
||||||
|
new.inv.opt2=🩻 ரேடியாலजी
|
||||||
|
|
||||||
|
new.blood.title=தயவுசெய்து இரத்தப் பரிசோதனையைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.blood.btn=பரிசோதனைகளைக் காண்க
|
||||||
|
new.blood.list=இரத்தப் பரிசோதனைகள்
|
||||||
|
new.blood.opt1=CBC ரத்தத் தேர்வு (₹300)
|
||||||
|
new.blood.opt2=LFT கல்லீரல் தேர்வு (₹500)
|
||||||
|
new.blood.opt3=லிப்பிட் தேர்வு (₹450)
|
||||||
|
new.blood.opt4=HbA1c சர்க்கரை (₹350)
|
||||||
|
new.blood.confirm=நீங்கள் தேர்ந்தெடுத்துள்ளீர்கள்:\n🧪 {0}\nகட்டணம்: {1}\nஉறுதிப்படுத்த விரும்புகிறீர்களா?
|
||||||
|
new.blood.btn_confirm=உறுதிசெய்
|
||||||
|
new.blood.btn_cancel=ரத்துசெய்
|
||||||
|
|
||||||
|
new.radio.title=தயவுசெய்து ரேடியாலஜி சேவையைத் தேர்ந்தெடுக்கவும்.
|
||||||
|
new.radio.btn=சேவைகளைக் காண்க
|
||||||
|
new.radio.list=சேவைகள்
|
||||||
|
new.radio.opt1=எக்ஸ்-ரே (₹500)
|
||||||
|
new.radio.opt2=சிடி ஸ்கேன் (₹2500)
|
||||||
|
new.radio.opt3=எம்ஆர்ஐ (₹5000)
|
||||||
|
new.radio.opt4=அல்ட்ராசவுண்ட் (₹1200)
|
||||||
|
|
||||||
|
new.btn.confirm=முன்பதிவை உறுதிசெய்
|
||||||
|
new.btn.cancel=ரத்துசெய்
|
||||||
|
new.booking.success=✅ முன்பதிவு வெற்றிகரமாக திட்டமிடப்பட்டது.\n\nசேவை: {0}\nதேதி: {1}\nநேரம்: {2}\nகட்டணம்: {3}\nமுன்பதிவு ஐடி: {4}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
package com.example.whatsappautomation;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.UserState;
|
||||||
|
import com.example.whatsappautomation.repository.AppointmentRepository;
|
||||||
|
import com.example.whatsappautomation.repository.ServiceBookingRepository;
|
||||||
|
import com.example.whatsappautomation.repository.TestBookingRepository;
|
||||||
|
import com.example.whatsappautomation.repository.UserStateRepository;
|
||||||
|
import com.example.whatsappautomation.service.ChatbotService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class InactivityTimeoutTests {
|
||||||
|
|
||||||
|
private UserStateRepository userStateRepository;
|
||||||
|
private ServiceBookingRepository serviceBookingRepository;
|
||||||
|
private TestBookingRepository testBookingRepository;
|
||||||
|
private AppointmentRepository appointmentRepository;
|
||||||
|
private MessageSource messageSource;
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
private ChatbotService chatbotService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
userStateRepository = mock(UserStateRepository.class);
|
||||||
|
serviceBookingRepository = mock(ServiceBookingRepository.class);
|
||||||
|
testBookingRepository = mock(TestBookingRepository.class);
|
||||||
|
appointmentRepository = mock(AppointmentRepository.class);
|
||||||
|
messageSource = mock(MessageSource.class);
|
||||||
|
restTemplate = mock(RestTemplate.class);
|
||||||
|
|
||||||
|
chatbotService = new ChatbotService(
|
||||||
|
userStateRepository,
|
||||||
|
serviceBookingRepository,
|
||||||
|
testBookingRepository,
|
||||||
|
appointmentRepository,
|
||||||
|
messageSource
|
||||||
|
);
|
||||||
|
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "restTemplate", restTemplate);
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "accessToken", "fakeToken");
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "timeoutSeconds", 2); // 2 seconds timeout for quick testing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUserInactiveForTwoSeconds_SessionExpires() throws InterruptedException {
|
||||||
|
String from = "1234567890";
|
||||||
|
String phoneId = "987654321";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
|
||||||
|
// Process a message to schedule the timer
|
||||||
|
chatbotService.processMessage(from, "Some command", phoneId);
|
||||||
|
|
||||||
|
// Verify scheduled task was registered
|
||||||
|
verify(userStateRepository, atLeastOnce()).save(any(UserState.class));
|
||||||
|
assertNotNull(userState.getLastInteractionTime());
|
||||||
|
assertEquals("987654321", userState.getLastPhoneNumberId());
|
||||||
|
|
||||||
|
// Wait for the timeout of 2 seconds (wait 3 seconds to be safe)
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
// Verify user state transitions to EXPIRED
|
||||||
|
ArgumentCaptor<UserState> userStateCaptor = ArgumentCaptor.forClass(UserState.class);
|
||||||
|
verify(userStateRepository, atLeast(2)).save(userStateCaptor.capture());
|
||||||
|
|
||||||
|
UserState finalState = userStateCaptor.getAllValues().get(userStateCaptor.getAllValues().size() - 1);
|
||||||
|
assertEquals("EXPIRED", finalState.getState());
|
||||||
|
assertNull(finalState.getCategory());
|
||||||
|
assertNull(finalState.getSubCategory());
|
||||||
|
|
||||||
|
// Verify fallback message was sent in English
|
||||||
|
ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
|
||||||
|
verify(restTemplate, atLeastOnce()).postForEntity(anyString(), entityCaptor.capture(), eq(String.class));
|
||||||
|
|
||||||
|
boolean foundFallback = false;
|
||||||
|
for (HttpEntity entity : entityCaptor.getAllValues()) {
|
||||||
|
String body = entity.getBody().toString();
|
||||||
|
if (body.contains("It looks like your previous conversation has ended") && body.contains("Hi or Hello")) {
|
||||||
|
foundFallback = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundFallback, "Expected English fallback message to be sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUserSendsMessageBeforeTimeout_TimerResets() throws InterruptedException {
|
||||||
|
String from = "1234567890";
|
||||||
|
String phoneId = "987654321";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
|
||||||
|
// Initial message schedules timer
|
||||||
|
chatbotService.processMessage(from, "First message", phoneId);
|
||||||
|
long initialTime = userState.getLastInteractionTime();
|
||||||
|
|
||||||
|
// Wait 1 second (less than 2s timeout) and send another message
|
||||||
|
Thread.sleep(1000);
|
||||||
|
chatbotService.processMessage(from, "Second message", phoneId);
|
||||||
|
long resetTime = userState.getLastInteractionTime();
|
||||||
|
|
||||||
|
assertTrue(resetTime > initialTime, "Last interaction time should have increased");
|
||||||
|
|
||||||
|
// Wait 1.5 seconds (total 2.5s since first message, but only 1.5s since second message)
|
||||||
|
Thread.sleep(1500);
|
||||||
|
|
||||||
|
// Verify state is NOT EXPIRED because the 2-second timer has not yet run out from the second interaction
|
||||||
|
assertNotEquals("EXPIRED", userState.getState());
|
||||||
|
|
||||||
|
// Now wait another 1.5 seconds (timer should now fire for the second interaction)
|
||||||
|
Thread.sleep(1500);
|
||||||
|
assertEquals("EXPIRED", userState.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExpiredSessionWithGreeting_StartsNewConversation() {
|
||||||
|
String from = "1234567890";
|
||||||
|
String phoneId = "987654321";
|
||||||
|
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("EXPIRED");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
|
||||||
|
// Sending "Hi" to start new conversation
|
||||||
|
chatbotService.processMessage(from, "Hi", phoneId);
|
||||||
|
|
||||||
|
// Since it's a greeting, reset is triggered and state becomes LANGUAGE_SELECTION,
|
||||||
|
// which then triggers interactive language selection message.
|
||||||
|
assertEquals("LANGUAGE_SELECTION", userState.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testExpiredSessionWithNonGreeting_SendsReminderMessage() {
|
||||||
|
String from = "1234567890";
|
||||||
|
String phoneId = "987654321";
|
||||||
|
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("EXPIRED");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
|
||||||
|
// Sending a non-greeting
|
||||||
|
chatbotService.processMessage(from, "Book Appointment", phoneId);
|
||||||
|
|
||||||
|
// The state remains EXPIRED
|
||||||
|
assertEquals("EXPIRED", userState.getState());
|
||||||
|
|
||||||
|
// Verify reminder message was sent
|
||||||
|
ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
|
||||||
|
verify(restTemplate, times(1)).postForEntity(anyString(), entityCaptor.capture(), eq(String.class));
|
||||||
|
String body = entityCaptor.getValue().getBody().toString();
|
||||||
|
assertTrue(body.contains("To start a new conversation, please type Hi or Hello"),
|
||||||
|
"Should remind the user to start a new conversation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testTamilFallbackMessage() throws InterruptedException {
|
||||||
|
String from = "1234567890";
|
||||||
|
String phoneId = "987654321";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("ta"); // Tamil preference
|
||||||
|
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
|
||||||
|
// Initial interaction
|
||||||
|
chatbotService.processMessage(from, "விருப்பம்", phoneId);
|
||||||
|
|
||||||
|
// Wait for the timeout
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
assertEquals("EXPIRED", userState.getState());
|
||||||
|
|
||||||
|
// Verify fallback message sent in Tamil
|
||||||
|
ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
|
||||||
|
verify(restTemplate, atLeastOnce()).postForEntity(anyString(), entityCaptor.capture(), eq(String.class));
|
||||||
|
|
||||||
|
boolean foundTamilFallback = false;
|
||||||
|
for (HttpEntity entity : entityCaptor.getAllValues()) {
|
||||||
|
String body = entity.getBody().toString();
|
||||||
|
if (body.contains("உரையாடல் முடிவடைந்துவிட்டது") && body.contains("Hi அல்லது Hello")) {
|
||||||
|
foundTamilFallback = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundTamilFallback, "Expected Tamil fallback message to be sent");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
package com.example.whatsappautomation;
|
||||||
|
|
||||||
|
import com.example.whatsappautomation.entity.Appointment;
|
||||||
|
import com.example.whatsappautomation.entity.ServiceBooking;
|
||||||
|
import com.example.whatsappautomation.entity.TestBooking;
|
||||||
|
import com.example.whatsappautomation.entity.UserState;
|
||||||
|
import com.example.whatsappautomation.repository.AppointmentRepository;
|
||||||
|
import com.example.whatsappautomation.repository.ServiceBookingRepository;
|
||||||
|
import com.example.whatsappautomation.repository.TestBookingRepository;
|
||||||
|
import com.example.whatsappautomation.repository.UserStateRepository;
|
||||||
|
import com.example.whatsappautomation.service.ChatbotService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class NewWorkflowTests {
|
||||||
|
|
||||||
|
private UserStateRepository userStateRepository;
|
||||||
|
private ServiceBookingRepository serviceBookingRepository;
|
||||||
|
private TestBookingRepository testBookingRepository;
|
||||||
|
private AppointmentRepository appointmentRepository;
|
||||||
|
private MessageSource messageSource;
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
private ChatbotService chatbotService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
userStateRepository = mock(UserStateRepository.class);
|
||||||
|
serviceBookingRepository = mock(ServiceBookingRepository.class);
|
||||||
|
testBookingRepository = mock(TestBookingRepository.class);
|
||||||
|
appointmentRepository = mock(AppointmentRepository.class);
|
||||||
|
messageSource = mock(MessageSource.class);
|
||||||
|
restTemplate = mock(RestTemplate.class);
|
||||||
|
|
||||||
|
chatbotService = new ChatbotService(
|
||||||
|
userStateRepository,
|
||||||
|
serviceBookingRepository,
|
||||||
|
testBookingRepository,
|
||||||
|
appointmentRepository,
|
||||||
|
messageSource
|
||||||
|
);
|
||||||
|
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "restTemplate", restTemplate);
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "accessToken", "fakeToken");
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "newWorkflowEnabled", true);
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "timeoutSeconds", 120);
|
||||||
|
|
||||||
|
// Standard stubbing for messageSource to return keys/formatted messages
|
||||||
|
when(messageSource.getMessage(anyString(), any(), any(Locale.class)))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
String key = invocation.getArgument(0);
|
||||||
|
Object[] args = invocation.getArgument(1);
|
||||||
|
if (args != null && args.length > 0) {
|
||||||
|
StringBuilder sb = new StringBuilder(key);
|
||||||
|
for (Object arg : args) {
|
||||||
|
sb.append(" [").append(arg).append("]");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
|
||||||
|
when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(String.class)))
|
||||||
|
.thenReturn(ResponseEntity.ok("SUCCESS"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFeatureFlagDisabled_FallsBackToOldWorkflow() {
|
||||||
|
ReflectionTestUtils.setField(chatbotService, "newWorkflowEnabled", false);
|
||||||
|
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("LANGUAGE_SELECTION");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
chatbotService.processMessage(from, "LANG_EN", "987654321");
|
||||||
|
|
||||||
|
// When old workflow runs LANG_EN, it should route to the old state machine.
|
||||||
|
// In the old workflow, LANG_EN transitions to MAIN_MENU.
|
||||||
|
assertEquals("MAIN_MENU", userState.getState());
|
||||||
|
assertEquals("en", userState.getLanguagePreference());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLanguageSelection_English_Tamil_Hindi() {
|
||||||
|
String from = "1234567890";
|
||||||
|
|
||||||
|
// English
|
||||||
|
UserState userState1 = new UserState(from);
|
||||||
|
userState1.setState("LANGUAGE_SELECTION");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState1));
|
||||||
|
chatbotService.processMessage(from, "LANG_EN", "987654321");
|
||||||
|
assertEquals("MAIN_MENU", userState1.getState());
|
||||||
|
assertEquals("en", userState1.getLanguagePreference());
|
||||||
|
|
||||||
|
// Tamil
|
||||||
|
UserState userState2 = new UserState(from);
|
||||||
|
userState2.setState("LANGUAGE_SELECTION");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState2));
|
||||||
|
chatbotService.processMessage(from, "LANG_TA", "987654321");
|
||||||
|
assertEquals("MAIN_MENU", userState2.getState());
|
||||||
|
assertEquals("ta", userState2.getLanguagePreference());
|
||||||
|
|
||||||
|
// Hindi
|
||||||
|
UserState userState3 = new UserState(from);
|
||||||
|
userState3.setState("LANGUAGE_SELECTION");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState3));
|
||||||
|
chatbotService.processMessage(from, "LANG_HI", "987654321");
|
||||||
|
assertEquals("MAIN_MENU", userState3.getState());
|
||||||
|
assertEquals("hi", userState3.getLanguagePreference());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmergencyFlow() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Go to Emergency
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_EMERGENCY", "987654321");
|
||||||
|
assertEquals("NEW_EMERGENCY", userState.getState());
|
||||||
|
|
||||||
|
// Select Main Menu
|
||||||
|
chatbotService.processMessage(from, "NEW_EMERGENCY_MAIN_MENU", "987654321");
|
||||||
|
assertEquals("MAIN_MENU", userState.getState());
|
||||||
|
|
||||||
|
// Go back to Emergency
|
||||||
|
chatbotService.processMessage(from, "1", "987654321"); // Option 1
|
||||||
|
assertEquals("NEW_EMERGENCY", userState.getState());
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
chatbotService.processMessage(from, "NEW_EMERGENCY_EXIT", "987654321");
|
||||||
|
assertEquals("EXPIRED", userState.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAssistanceFlow() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Go to Assistance
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_ASSISTANCE", "987654321");
|
||||||
|
assertEquals("NEW_ASSISTANCE", userState.getState());
|
||||||
|
|
||||||
|
// Select Main Menu
|
||||||
|
chatbotService.processMessage(from, "NEW_ASSISTANCE_MAIN_MENU", "987654321");
|
||||||
|
assertEquals("MAIN_MENU", userState.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testJobEnquiryFlow() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Start Job Enquiry
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_JOB", "987654321");
|
||||||
|
assertEquals("NEW_JOB_PHONE", userState.getState());
|
||||||
|
|
||||||
|
// Enter invalid phone
|
||||||
|
chatbotService.processMessage(from, "abc", "987654321");
|
||||||
|
assertEquals("NEW_JOB_PHONE", userState.getState()); // Should stay
|
||||||
|
|
||||||
|
// Enter valid phone
|
||||||
|
chatbotService.processMessage(from, "9876543210", "987654321");
|
||||||
|
assertEquals("NEW_JOB_EMAIL", userState.getState());
|
||||||
|
assertEquals("9876543210", userState.getSubCategory());
|
||||||
|
|
||||||
|
// Enter invalid email
|
||||||
|
chatbotService.processMessage(from, "invalid-email", "987654321");
|
||||||
|
assertEquals("NEW_JOB_EMAIL", userState.getState()); // Should stay
|
||||||
|
|
||||||
|
// Enter valid email
|
||||||
|
chatbotService.processMessage(from, "hr@rmchospital.com", "987654321");
|
||||||
|
assertEquals("NEW_JOB_CONFIRM", userState.getState());
|
||||||
|
assertEquals("hr@rmchospital.com", userState.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDoctorAppointmentFlow() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Start Doc Appointment
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_DOCTOR", "987654321");
|
||||||
|
assertEquals("NEW_DOC_SPECIALITY", userState.getState());
|
||||||
|
|
||||||
|
// Select Cardiology
|
||||||
|
chatbotService.processMessage(from, "DEPT_CARDIO", "987654321");
|
||||||
|
assertEquals("NEW_DOC_DOCTOR", userState.getState());
|
||||||
|
assertEquals("Cardiology", userState.getCategory());
|
||||||
|
|
||||||
|
// Select Doctor Priya
|
||||||
|
chatbotService.processMessage(from, "DOC_PRIYA", "987654321");
|
||||||
|
assertEquals("NEW_DOC_DATE", userState.getState());
|
||||||
|
assertEquals("Dr. Priya Sharma", userState.getSelectedItem());
|
||||||
|
|
||||||
|
// Enter invalid date format
|
||||||
|
chatbotService.processMessage(from, "2026/08/17", "987654321");
|
||||||
|
assertEquals("NEW_DOC_DATE", userState.getState()); // Should stay
|
||||||
|
|
||||||
|
// Enter valid date
|
||||||
|
chatbotService.processMessage(from, "17/08/2026", "987654321");
|
||||||
|
assertEquals("NEW_DOC_TIME", userState.getState());
|
||||||
|
assertEquals("17/08/2026", userState.getBookingDate());
|
||||||
|
|
||||||
|
// Select Time slot 10 AM
|
||||||
|
chatbotService.processMessage(from, "TIME_10AM", "987654321");
|
||||||
|
assertEquals("NEW_DOC_CONFIRM", userState.getState());
|
||||||
|
assertEquals("10:00 AM", userState.getBookingTime());
|
||||||
|
|
||||||
|
// Confirm booking
|
||||||
|
chatbotService.processMessage(from, "APT_CONFIRM", "987654321");
|
||||||
|
|
||||||
|
// Verify appointment saved in database
|
||||||
|
ArgumentCaptor<Appointment> appCaptor = ArgumentCaptor.forClass(Appointment.class);
|
||||||
|
verify(appointmentRepository, times(1)).save(appCaptor.capture());
|
||||||
|
Appointment saved = appCaptor.getValue();
|
||||||
|
assertEquals("Cardiology", saved.getDepartment());
|
||||||
|
assertEquals("Dr. Priya Sharma", saved.getDoctor());
|
||||||
|
assertEquals("17/08/2026", saved.getAppointmentDate());
|
||||||
|
assertEquals("10:00 AM", saved.getAppointmentTime());
|
||||||
|
assertEquals("Confirmed", saved.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHealthcarePackagesFlow_WithDiscount() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Start Healthcare Package
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_PACKAGES", "987654321");
|
||||||
|
assertEquals("NEW_PKG_SELECT", userState.getState());
|
||||||
|
|
||||||
|
// Select Advanced Cardiac Package (₹3000)
|
||||||
|
chatbotService.processMessage(from, "PKG_CARDIO", "987654321");
|
||||||
|
assertEquals("NEW_PKG_COUPON", userState.getState());
|
||||||
|
assertEquals("Advanced Cardiac Package", userState.getSelectedItem());
|
||||||
|
assertEquals("₹3,000", userState.getPrice());
|
||||||
|
|
||||||
|
// Enter valid coupon code RMC10 (10% discount)
|
||||||
|
chatbotService.processMessage(from, "RMC10", "987654321");
|
||||||
|
assertEquals("NEW_PKG_DATE", userState.getState());
|
||||||
|
assertEquals("₹2,700", userState.getPrice()); // 3000 - 10% = 2700
|
||||||
|
|
||||||
|
// Enter Date
|
||||||
|
chatbotService.processMessage(from, "18/08/2026", "987654321");
|
||||||
|
assertEquals("NEW_PKG_TIME", userState.getState());
|
||||||
|
|
||||||
|
// Select time slot
|
||||||
|
chatbotService.processMessage(from, "PKG_TIME_11AM", "987654321");
|
||||||
|
assertEquals("NEW_PKG_CONFIRM", userState.getState());
|
||||||
|
assertEquals("11:00 AM", userState.getBookingTime());
|
||||||
|
|
||||||
|
// Confirm
|
||||||
|
chatbotService.processMessage(from, "PKG_CONFIRM", "987654321");
|
||||||
|
|
||||||
|
// Verify package saved in database
|
||||||
|
ArgumentCaptor<ServiceBooking> bookingCaptor = ArgumentCaptor.forClass(ServiceBooking.class);
|
||||||
|
verify(serviceBookingRepository, times(1)).save(bookingCaptor.capture());
|
||||||
|
ServiceBooking saved = bookingCaptor.getValue();
|
||||||
|
assertEquals("Advanced Cardiac Package", saved.getServiceName());
|
||||||
|
assertEquals("₹2,700", saved.getPrice());
|
||||||
|
assertEquals("18/08/2026", saved.getBookingDate());
|
||||||
|
assertEquals("11:00 AM", saved.getBookingTime());
|
||||||
|
assertEquals("CONFIRMED", saved.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testBloodLabInvestigationFlow() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("MAIN_MENU");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Start Investigation
|
||||||
|
chatbotService.processMessage(from, "NEW_MENU_INVESTIGATION", "987654321");
|
||||||
|
assertEquals("NEW_INV_TYPE", userState.getState());
|
||||||
|
|
||||||
|
// Select Blood
|
||||||
|
chatbotService.processMessage(from, "INV_BLOOD", "987654321");
|
||||||
|
assertEquals("NEW_BLOOD_SELECT", userState.getState());
|
||||||
|
|
||||||
|
// Select Complete Blood Count (CBC)
|
||||||
|
chatbotService.processMessage(from, "BLOOD_CBC", "987654321");
|
||||||
|
assertEquals("NEW_BLOOD_CONFIRM_TEST", userState.getState());
|
||||||
|
|
||||||
|
// Confirm selection
|
||||||
|
chatbotService.processMessage(from, "BLOOD_CONFIRM_YES", "987654321");
|
||||||
|
assertEquals("NEW_BLOOD_DATE", userState.getState());
|
||||||
|
|
||||||
|
// Enter Date
|
||||||
|
chatbotService.processMessage(from, "19/08/2026", "987654321");
|
||||||
|
assertEquals("NEW_BLOOD_TIME", userState.getState());
|
||||||
|
|
||||||
|
// Select Time
|
||||||
|
chatbotService.processMessage(from, "SLOT_7AM", "987654321");
|
||||||
|
assertEquals("NEW_BLOOD_CONFIRM", userState.getState());
|
||||||
|
|
||||||
|
// Confirm Booking
|
||||||
|
chatbotService.processMessage(from, "BLOOD_FINAL_CONFIRM", "987654321");
|
||||||
|
|
||||||
|
// Verify Lab Booking saved in database
|
||||||
|
ArgumentCaptor<TestBooking> testCaptor = ArgumentCaptor.forClass(TestBooking.class);
|
||||||
|
verify(testBookingRepository, times(1)).save(testCaptor.capture());
|
||||||
|
TestBooking saved = testCaptor.getValue();
|
||||||
|
assertEquals("Complete Blood Count (CBC)", saved.getPackageName());
|
||||||
|
assertEquals("₹300", saved.getPrice());
|
||||||
|
assertEquals("19/08/2026", saved.getBookingDate());
|
||||||
|
assertEquals("7 AM - 9 AM", saved.getBookingTime());
|
||||||
|
assertEquals("CONFIRMED", saved.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNavigationCommands_Back() {
|
||||||
|
String from = "1234567890";
|
||||||
|
UserState userState = new UserState(from);
|
||||||
|
userState.setState("NEW_DOC_DOCTOR");
|
||||||
|
userState.setCategory("Cardiology");
|
||||||
|
userState.setLanguagePreference("en");
|
||||||
|
when(userStateRepository.findById(from)).thenReturn(Optional.of(userState));
|
||||||
|
|
||||||
|
// Send 'Back' command to go back to Speciality
|
||||||
|
chatbotService.processMessage(from, "Back", "987654321");
|
||||||
|
assertEquals("NEW_DOC_SPECIALITY", userState.getState());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.example.whatsappautomation;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class WhatsAppAutomationApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
}
|
||||||
49
test_restart.py
Normal file
49
test_restart.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"object": "whatsapp_business_account",
|
||||||
|
"entry": [
|
||||||
|
{
|
||||||
|
"id": "1068191009070265",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"value": {
|
||||||
|
"messaging_product": "whatsapp",
|
||||||
|
"metadata": {
|
||||||
|
"display_phone_number": "15551914329",
|
||||||
|
"phone_number_id": "1236658909534007"
|
||||||
|
},
|
||||||
|
"contacts": [
|
||||||
|
{
|
||||||
|
"profile": {
|
||||||
|
"name": "Dev"
|
||||||
|
},
|
||||||
|
"wa_id": "919994834737",
|
||||||
|
"user_id": "IN.1458184416042595"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"from": "919994834737",
|
||||||
|
"from_user_id": "IN.1458184416042595",
|
||||||
|
"id": "wamid.HBgMOTE2Mzc5NzgzOTExFQIAEhgWM0VCMDU3MDk2N0Q2ODk0MzE2N0U4NwA=",
|
||||||
|
"timestamp": "1786945902",
|
||||||
|
"interactive": {
|
||||||
|
"type": "button_reply",
|
||||||
|
"button_reply": {
|
||||||
|
"id": "MENU_SERVICES",
|
||||||
|
"title": "My Services"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "interactive"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"field": "messages"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.post('http://localhost:8085/webhook', json=payload)
|
||||||
Reference in New Issue
Block a user