Skip to content

Commit 52b5c42

Browse files
authored
Feature: support bi-directional sms messaging using available gateways. (#1351)
1 parent 2c00deb commit 52b5c42

File tree

25 files changed

+379
-19
lines changed

25 files changed

+379
-19
lines changed

docs/integrations-guide/textbee.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ TextBee allows you to use your own Android device as an SMS gateway, providing u
3535
3. Click **Generate API Key**
3636
4. Copy the API key - you'll need this for MPM configuration
3737
5. Note your **Device ID** from the dashboard or app
38+
6. (Optional) To enable incoming SMS, create a **Webhook Subscription** for the `MESSAGE_RECEIVED` event and copy the **Webhook Secret**
3839

3940
## Step 4: Enable TextBee Plugin in MPM
4041

@@ -51,9 +52,21 @@ TextBee allows you to use your own Android device as an SMS gateway, providing u
5152
3. Enter the following credentials:
5253
- **API Key**: Paste the API key from Step 3
5354
- **Device ID**: Enter your device ID from Step 3
55+
- **Webhook Secret** (optional): Paste the webhook secret from Step 3 to enable incoming SMS
5456
4. Click **Save** to store the configuration
5557
5. The plugin will validate your credentials automatically
5658

59+
### Incoming SMS (Optional)
60+
61+
To receive incoming SMS from customers, you need to configure a webhook in the TextBee dashboard:
62+
63+
1. In the TextBee dashboard, navigate to **Webhooks**
64+
2. Create a new webhook subscription for the `MESSAGE_RECEIVED` event
65+
3. Set the webhook URL to: `https://your-mpm-domain/api/textbee-sms-gateway/callback/{company-id}/incoming-messages`
66+
- Replace `{company-id}` with your MPM company ID
67+
4. Copy the webhook secret and paste it in the MPM TextBee configuration
68+
5. Incoming SMS will now be stored and processed by MPM
69+
5770
## Step 6: Select TextBee as Your SMS Gateway
5871

5972
1. Navigate to **Settings****Configuration****Main Settings**

docs/usage-guide/sms.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ TextBee allows you to use your own Android device as an SMS gateway, providing u
3434

3535
- Up to 98% cost savings
3636
- Uses your own Android device
37+
- Bidirectional SMS (send and receive)
3738
- Free plan: 300 messages/month
3839
- Pro plan: 5,000 messages/month
3940

src/backend/app/Events/SmsStoredEvent.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Events;
44

5+
use App\Models\Sms;
56
use Illuminate\Foundation\Events\Dispatchable;
67
use Illuminate\Queue\SerializesModels;
78

@@ -11,8 +12,9 @@
1112
* Dispatch this event to asynchronously store an SMS.
1213
* A corresponding listener will send the event.
1314
*
14-
* @property string $sender The sender of the SMS.
15-
* @property string $message The message content of the SMS.
15+
* @property string $sender The sender of the SMS.
16+
* @property string $message The message content of the SMS.
17+
* @property Sms|null $sms The SMS model instance, if available.
1618
*/
1719
class SmsStoredEvent {
1820
use Dispatchable;
@@ -21,5 +23,6 @@ class SmsStoredEvent {
2123
public function __construct(
2224
public string $sender,
2325
public string $message,
26+
public ?Sms $sms = null,
2427
) {}
2528
}

src/backend/app/Http/Controllers/SmsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public function store(StoreSmsRequest $request): ApiResource {
183183
$sms = $this->smsService->createSms($smsData);
184184

185185
match ($this->smsService->checkMessageType($message)) {
186-
$this->smsService::FEEDBACK => event(new SmsStoredEvent($sender, $message)),
186+
$this->smsService::FEEDBACK => event(new SmsStoredEvent($sender, $message, $sms)),
187187
$this->smsService::TICKET => $this->commentService->storeComment($sender, $message),
188188
default => new ApiResource($sms),
189189
};

src/backend/app/Plugins/AfricasTalking/Http/Controllers/AfricasTalkingCallbackController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ public function incoming(Request $request): JsonResponse {
2929
$senderId = $sender ? $sender->id : null;
3030

3131
$smsData = [
32-
'receiver' => $phoneNumber,
32+
'receiver' => $address->phone ?? $phoneNumber,
3333
'body' => $message,
3434
'sender_id' => $senderId,
3535
'direction' => Sms::DIRECTION_INCOMING,
3636
'status' => Sms::STATUS_DELIVERED,
3737
];
3838

39-
$this->smsService->createSms($smsData);
40-
event(new SmsStoredEvent($phoneNumber, $message));
39+
$sms = $this->smsService->createSms($smsData);
40+
event(new SmsStoredEvent($phoneNumber, $message, $sms));
4141

4242
return response()->json(['status' => 'success']);
4343
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace App\Plugins\AfricasTalking\Listeners;
4+
5+
use App\Events\SmsStoredEvent;
6+
use App\Listeners\SmsListener as GlobalSmsListener;
7+
use App\Models\MpmPlugin;
8+
use App\Services\MainSettingsService;
9+
10+
class SmsListener {
11+
public function __construct(
12+
private GlobalSmsListener $globalSmsListener,
13+
private MainSettingsService $mainSettingsService,
14+
) {}
15+
16+
public function handle(SmsStoredEvent $event): void {
17+
$mainSettings = $this->mainSettingsService->getAll()->first();
18+
19+
if ($mainSettings?->sms_gateway_id !== MpmPlugin::AFRICAS_TALKING) {
20+
return;
21+
}
22+
23+
$this->globalSmsListener->handle($event);
24+
}
25+
}

src/backend/app/Plugins/AfricasTalking/Providers/EventServiceProvider.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
namespace App\Plugins\AfricasTalking\Providers;
44

5+
use App\Events\SmsStoredEvent;
6+
use App\Plugins\AfricasTalking\Listeners\SmsListener;
57
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
68

79
class EventServiceProvider extends ServiceProvider {
10+
protected $listen = [
11+
SmsStoredEvent::class => [SmsListener::class],
12+
];
13+
814
/**
915
* Register any events for your application.
1016
*/

src/backend/app/Plugins/SparkMeter/Listeners/SmsListener.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace App\Plugins\SparkMeter\Listeners;
44

5-
use App\Models\Meter\Meter;
5+
use App\Events\SmsStoredEvent;
66
use App\Plugins\SparkMeter\Exceptions\SparkAPIResponseException;
77
use App\Plugins\SparkMeter\Models\SmCustomer;
88
use App\Plugins\SparkMeter\Services\CustomerService;
@@ -56,8 +56,7 @@ public function onSmsStored(string $sender, string $message): void {
5656
}
5757
}
5858

59-
public function handle(string $sender, string $message): void {
60-
// TODO: Uncomment this when spark-meter package is refactored with device->meter approach
61-
// $this->onSmsStored($sender, $message);
59+
public function handle(SmsStoredEvent $event): void {
60+
$this->onSmsStored($event->sender, $event->message);
6261
}
6362
}

src/backend/app/Plugins/SparkMeter/Services/CustomerService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ static function ($q) use ($phoneNumber) {
529529

530530
return $this->smCustomer->newQuery()->with(['site', 'mpmPerson.devices.device'])->where(
531531
'mpm_customer_id',
532-
$person->id
532+
$person?->id
533533
)->first();
534534
}
535535
}

src/backend/app/Plugins/SteamaMeter/Listeners/SmsListener.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Plugins\SteamaMeter\Listeners;
44

5+
use App\Events\SmsStoredEvent;
56
use App\Plugins\SteamaMeter\Models\SteamaCustomer;
67
use App\Plugins\SteamaMeter\Services\SteamaCustomerService;
78
use App\Plugins\SteamaMeter\Services\SteamaSmsFeedbackWordService;
@@ -35,8 +36,7 @@ public function onSmsStored(string $sender, string $message): void {
3536
}
3637
}
3738

38-
public function handle(string $sender, string $message): void {
39-
// TODO: Uncomment this when steamaco-meter package is refactored with device->meter approach
40-
// $this->onSmsStored($sender, $message);
39+
public function handle(SmsStoredEvent $event): void {
40+
$this->onSmsStored($event->sender, $event->message);
4141
}
4242
}

0 commit comments

Comments
 (0)