mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Remove standalone SMS support in favor of a "Mail, SMS, and other media are mostly the same thing" approach
Summary: Ref T920. Over time, mail has become much more complex and I think considering "mail", "sms", "postcards", "whatsapp", etc., to be mostly-the-same is now a more promising avenue than building separate stacks for each one. Throw away all the standalone SMS code, including the Twilio config options. I have a separate diff that adds Twilio as a mail adapter and functions correctly, but it needs some more work to bring upstream. This permanently destroys the `sms` table, which no real reachable code ever wrote to. I'll call this out in the changelog. Test Plan: - Grepped for `SMS` and `Twilio`. - Ran storage upgrade. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T920 Differential Revision: https://secure.phabricator.com/D19939
This commit is contained in:
parent
e856e791f3
commit
cfcd35d8a3
16 changed files with 1 additions and 707 deletions
1
resources/sql/autopatches/20190101.sms.01.drop.sql
Normal file
1
resources/sql/autopatches/20190101.sms.01.drop.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE {$NAMESPACE}_metamta.sms;
|
|
@ -1,21 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(__FILE__)));
|
|
||||||
require_once $root.'/scripts/__init_script__.php';
|
|
||||||
|
|
||||||
$args = new PhutilArgumentParser($argv);
|
|
||||||
$args->setTagline(pht('manage SMS'));
|
|
||||||
$args->setSynopsis(<<<EOSYNOPSIS
|
|
||||||
**sms** __command__ [__options__]
|
|
||||||
Manage Phabricator SMS stuff.
|
|
||||||
|
|
||||||
EOSYNOPSIS
|
|
||||||
);
|
|
||||||
$args->parseStandardArguments();
|
|
||||||
|
|
||||||
$workflows = id(new PhutilClassMapQuery())
|
|
||||||
->setAncestorClass('PhabricatorSMSManagementWorkflow')
|
|
||||||
->execute();
|
|
||||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
|
||||||
$args->parseWorkflows($workflows);
|
|
|
@ -4239,19 +4239,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
|
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
|
||||||
'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
|
'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php',
|
||||||
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
|
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
|
||||||
'PhabricatorSMS' => 'infrastructure/sms/storage/PhabricatorSMS.php',
|
|
||||||
'PhabricatorSMSConfigOptions' => 'applications/config/option/PhabricatorSMSConfigOptions.php',
|
|
||||||
'PhabricatorSMSDAO' => 'infrastructure/sms/storage/PhabricatorSMSDAO.php',
|
|
||||||
'PhabricatorSMSDemultiplexWorker' => 'infrastructure/sms/worker/PhabricatorSMSDemultiplexWorker.php',
|
|
||||||
'PhabricatorSMSImplementationAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationAdapter.php',
|
|
||||||
'PhabricatorSMSImplementationTestBlackholeAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTestBlackholeAdapter.php',
|
|
||||||
'PhabricatorSMSImplementationTwilioAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTwilioAdapter.php',
|
|
||||||
'PhabricatorSMSManagementListOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementListOutboundWorkflow.php',
|
|
||||||
'PhabricatorSMSManagementSendTestWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementSendTestWorkflow.php',
|
|
||||||
'PhabricatorSMSManagementShowOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementShowOutboundWorkflow.php',
|
|
||||||
'PhabricatorSMSManagementWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementWorkflow.php',
|
|
||||||
'PhabricatorSMSSendWorker' => 'infrastructure/sms/worker/PhabricatorSMSSendWorker.php',
|
|
||||||
'PhabricatorSMSWorker' => 'infrastructure/sms/worker/PhabricatorSMSWorker.php',
|
|
||||||
'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
|
'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
|
||||||
'PhabricatorSSHKeyGenerator' => 'infrastructure/util/PhabricatorSSHKeyGenerator.php',
|
'PhabricatorSSHKeyGenerator' => 'infrastructure/util/PhabricatorSSHKeyGenerator.php',
|
||||||
'PhabricatorSSHKeysSettingsPanel' => 'applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php',
|
'PhabricatorSSHKeysSettingsPanel' => 'applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php',
|
||||||
|
@ -10269,19 +10256,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorResourceSite' => 'PhabricatorSite',
|
'PhabricatorResourceSite' => 'PhabricatorSite',
|
||||||
'PhabricatorRobotsController' => 'PhabricatorController',
|
'PhabricatorRobotsController' => 'PhabricatorController',
|
||||||
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||||
'PhabricatorSMS' => 'PhabricatorSMSDAO',
|
|
||||||
'PhabricatorSMSConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
|
||||||
'PhabricatorSMSDAO' => 'PhabricatorLiskDAO',
|
|
||||||
'PhabricatorSMSDemultiplexWorker' => 'PhabricatorSMSWorker',
|
|
||||||
'PhabricatorSMSImplementationAdapter' => 'Phobject',
|
|
||||||
'PhabricatorSMSImplementationTestBlackholeAdapter' => 'PhabricatorSMSImplementationAdapter',
|
|
||||||
'PhabricatorSMSImplementationTwilioAdapter' => 'PhabricatorSMSImplementationAdapter',
|
|
||||||
'PhabricatorSMSManagementListOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow',
|
|
||||||
'PhabricatorSMSManagementSendTestWorkflow' => 'PhabricatorSMSManagementWorkflow',
|
|
||||||
'PhabricatorSMSManagementShowOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow',
|
|
||||||
'PhabricatorSMSManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
|
||||||
'PhabricatorSMSSendWorker' => 'PhabricatorSMSWorker',
|
|
||||||
'PhabricatorSMSWorker' => 'PhabricatorWorker',
|
|
||||||
'PhabricatorSQLPatchList' => 'Phobject',
|
'PhabricatorSQLPatchList' => 'Phobject',
|
||||||
'PhabricatorSSHKeyGenerator' => 'Phobject',
|
'PhabricatorSSHKeyGenerator' => 'Phobject',
|
||||||
'PhabricatorSSHKeysSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorSSHKeysSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSConfigOptions
|
|
||||||
extends PhabricatorApplicationConfigOptions {
|
|
||||||
|
|
||||||
public function getName() {
|
|
||||||
return pht('SMS');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription() {
|
|
||||||
return pht('Configure SMS.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIcon() {
|
|
||||||
return 'fa-mobile';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGroup() {
|
|
||||||
return 'core';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOptions() {
|
|
||||||
$adapter_description = pht(
|
|
||||||
'Adapter class to use to transmit SMS to an external provider. A given '.
|
|
||||||
'external provider will most likely need more configuration which will '.
|
|
||||||
'most likely require registration and payment for the service.');
|
|
||||||
|
|
||||||
return array(
|
|
||||||
$this->newOption(
|
|
||||||
'sms.default-sender',
|
|
||||||
'string',
|
|
||||||
null)
|
|
||||||
->setDescription(pht('Default "from" number.'))
|
|
||||||
->addExample('8675309', 'Jenny still has this number')
|
|
||||||
->addExample('18005555555', 'Maybe not a real number'),
|
|
||||||
$this->newOption(
|
|
||||||
'sms.default-adapter',
|
|
||||||
'class',
|
|
||||||
null)
|
|
||||||
->setBaseClass('PhabricatorSMSImplementationAdapter')
|
|
||||||
->setSummary(pht('Control how SMS is sent.'))
|
|
||||||
->setDescription($adapter_description),
|
|
||||||
$this->newOption(
|
|
||||||
'twilio.account-sid',
|
|
||||||
'string',
|
|
||||||
null)
|
|
||||||
->setDescription(pht('Account ID on Twilio service.'))
|
|
||||||
->setLocked(true)
|
|
||||||
->addExample('gf5kzccfn2sfknpnadvz7kokv6nz5v', pht('30 characters')),
|
|
||||||
$this->newOption(
|
|
||||||
'twilio.auth-token',
|
|
||||||
'string',
|
|
||||||
null)
|
|
||||||
->setDescription(pht('Authorization token from Twilio service.'))
|
|
||||||
->setHidden(true)
|
|
||||||
->addExample('f3jsi4i67wiwt6w54hf2zwvy3fjf5h', pht('30 characters')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
abstract class PhabricatorSMSImplementationAdapter extends Phobject {
|
|
||||||
|
|
||||||
private $fromNumber;
|
|
||||||
private $toNumber;
|
|
||||||
private $body;
|
|
||||||
|
|
||||||
public function setFrom($number) {
|
|
||||||
$this->fromNumber = $number;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFrom() {
|
|
||||||
return $this->fromNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTo($number) {
|
|
||||||
$this->toNumber = $number;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTo() {
|
|
||||||
return $this->toNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setBody($body) {
|
|
||||||
$this->body = $body;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBody() {
|
|
||||||
return $this->body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 16 characters or less, to be used in database columns and exposed
|
|
||||||
* to administrators during configuration directly.
|
|
||||||
*/
|
|
||||||
abstract public function getProviderShortName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the message. Generally, this means connecting to some service and
|
|
||||||
* handing data to it. SMS APIs are generally asynchronous, so truly
|
|
||||||
* determining success or failure is probably impossible synchronously.
|
|
||||||
*
|
|
||||||
* That said, if the adapter determines that the SMS will never be
|
|
||||||
* deliverable, or there is some other known failure, it should throw
|
|
||||||
* an exception.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
abstract public function send();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Most (all?) SMS APIs are asynchronous, but some do send back some
|
|
||||||
* initial information. Use this hook to determine what the updated
|
|
||||||
* sentStatus should be and what the provider is using for an SMS ID,
|
|
||||||
* as well as throw exceptions if there are any failures.
|
|
||||||
*
|
|
||||||
* @return array Tuple of ($sms_id and $sent_status)
|
|
||||||
*/
|
|
||||||
abstract public function getSMSDataFromResult($result);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Due to the asynchronous nature of sending SMS messages, it can be
|
|
||||||
* necessary to poll the provider regarding the sent status of a given
|
|
||||||
* sms.
|
|
||||||
*
|
|
||||||
* For now, this *MUST* be implemented and *MUST* work.
|
|
||||||
*/
|
|
||||||
abstract public function pollSMSSentStatus(PhabricatorSMS $sms);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function to handle sending an SMS.
|
|
||||||
*/
|
|
||||||
public static function sendSMS(array $to_numbers, $body) {
|
|
||||||
PhabricatorWorker::scheduleTask(
|
|
||||||
'PhabricatorSMSDemultiplexWorker',
|
|
||||||
array(
|
|
||||||
'toNumbers' => $to_numbers,
|
|
||||||
'body' => $body,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'priority' => PhabricatorWorker::PRIORITY_ALERTS,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is useful for testing, but otherwise your SMS ends up in a blackhole.
|
|
||||||
*/
|
|
||||||
final class PhabricatorSMSImplementationTestBlackholeAdapter
|
|
||||||
extends PhabricatorSMSImplementationAdapter {
|
|
||||||
|
|
||||||
public function getProviderShortName() {
|
|
||||||
return 'testtesttest';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function send() {
|
|
||||||
// I guess this is what a blackhole looks like
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSMSDataFromResult($result) {
|
|
||||||
return array(
|
|
||||||
Filesystem::readRandomCharacters(40),
|
|
||||||
PhabricatorSMS::STATUS_SENT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function pollSMSSentStatus(PhabricatorSMS $sms) {
|
|
||||||
if ($sms->getID()) {
|
|
||||||
return PhabricatorSMS::STATUS_SENT;
|
|
||||||
}
|
|
||||||
return PhabricatorSMS::STATUS_SENT_UNCONFIRMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSImplementationTwilioAdapter
|
|
||||||
extends PhabricatorSMSImplementationAdapter {
|
|
||||||
|
|
||||||
public function getProviderShortName() {
|
|
||||||
return 'twilio';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phutil-external-symbol class Services_Twilio
|
|
||||||
*/
|
|
||||||
private function buildClient() {
|
|
||||||
$root = dirname(phutil_get_library_root('phabricator'));
|
|
||||||
require_once $root.'/externals/twilio-php/Services/Twilio.php';
|
|
||||||
$account_sid = PhabricatorEnv::getEnvConfig('twilio.account-sid');
|
|
||||||
$auth_token = PhabricatorEnv::getEnvConfig('twilio.auth-token');
|
|
||||||
return new Services_Twilio($account_sid, $auth_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phutil-external-symbol class Services_Twilio_RestException
|
|
||||||
*/
|
|
||||||
public function send() {
|
|
||||||
$client = $this->buildClient();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$message = $client->account->sms_messages->create(
|
|
||||||
$this->formatNumberForSMS($this->getFrom()),
|
|
||||||
$this->formatNumberForSMS($this->getTo()),
|
|
||||||
$this->getBody(),
|
|
||||||
array());
|
|
||||||
} catch (Services_Twilio_RestException $e) {
|
|
||||||
$message = sprintf(
|
|
||||||
'HTTP Code %d: %s',
|
|
||||||
$e->getStatus(),
|
|
||||||
$e->getMessage());
|
|
||||||
|
|
||||||
// Twilio tries to provide a link to more specific details if they can.
|
|
||||||
if ($e->getInfo()) {
|
|
||||||
$message .= sprintf(' For more information, see %s.', $e->getInfo());
|
|
||||||
}
|
|
||||||
throw new PhabricatorWorkerPermanentFailureException($message);
|
|
||||||
}
|
|
||||||
return $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSMSDataFromResult($result) {
|
|
||||||
return array($result->sid, $this->getSMSStatus($result->status));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function pollSMSSentStatus(PhabricatorSMS $sms) {
|
|
||||||
$client = $this->buildClient();
|
|
||||||
$message = $client->account->messages->get($sms->getProviderSMSID());
|
|
||||||
|
|
||||||
return $this->getSMSStatus($message->status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See https://www.twilio.com/docs/api/rest/sms#sms-status-values.
|
|
||||||
*/
|
|
||||||
private function getSMSStatus($twilio_status) {
|
|
||||||
switch ($twilio_status) {
|
|
||||||
case 'failed':
|
|
||||||
$status = PhabricatorSMS::STATUS_FAILED;
|
|
||||||
break;
|
|
||||||
case 'sent':
|
|
||||||
$status = PhabricatorSMS::STATUS_SENT;
|
|
||||||
break;
|
|
||||||
case 'sending':
|
|
||||||
case 'queued':
|
|
||||||
default:
|
|
||||||
$status = PhabricatorSMS::STATUS_SENT_UNCONFIRMED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We expect numbers to be plainly entered - i.e. the preg_replace here
|
|
||||||
* should do nothing - but try hard to format anyway.
|
|
||||||
*
|
|
||||||
* Twilio uses E164 format, e.g. +15551234567
|
|
||||||
*/
|
|
||||||
private function formatNumberForSMS($number) {
|
|
||||||
$number = preg_replace('/[^0-9]/', '', $number);
|
|
||||||
$first_char = substr($number, 0, 1);
|
|
||||||
switch ($first_char) {
|
|
||||||
case '1':
|
|
||||||
$prepend = '+';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$prepend = '+1';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $prepend.$number;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSManagementListOutboundWorkflow
|
|
||||||
extends PhabricatorSMSManagementWorkflow {
|
|
||||||
|
|
||||||
protected function didConstruct() {
|
|
||||||
$this
|
|
||||||
->setName('list-outbound')
|
|
||||||
->setSynopsis(pht('List outbound SMS messages sent by Phabricator.'))
|
|
||||||
->setExamples('**list-outbound**')
|
|
||||||
->setArguments(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'limit',
|
|
||||||
'param' => 'N',
|
|
||||||
'default' => 100,
|
|
||||||
'help' => pht(
|
|
||||||
'Show a specific number of SMS messages (default 100).'),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
$sms_messages = id(new PhabricatorSMS())->loadAllWhere(
|
|
||||||
'1 = 1 ORDER BY id DESC LIMIT %d',
|
|
||||||
$args->getArg('limit'));
|
|
||||||
|
|
||||||
if (!$sms_messages) {
|
|
||||||
$console->writeErr("%s\n", pht('No sent SMS.'));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$table = id(new PhutilConsoleTable())
|
|
||||||
->setShowHeader(false)
|
|
||||||
->addColumn('id', array('title' => pht('ID')))
|
|
||||||
->addColumn('status', array('title' => pht('Status')))
|
|
||||||
->addColumn('recv', array('title' => pht('Recipient')));
|
|
||||||
|
|
||||||
foreach (array_reverse($sms_messages) as $sms) {
|
|
||||||
$table->addRow(array(
|
|
||||||
'id' => $sms->getID(),
|
|
||||||
'status' => $sms->getSendStatus(),
|
|
||||||
'recv' => $sms->getToNumber(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$table->draw();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSManagementSendTestWorkflow
|
|
||||||
extends PhabricatorSMSManagementWorkflow {
|
|
||||||
|
|
||||||
protected function didConstruct() {
|
|
||||||
$this
|
|
||||||
->setName('send-test')
|
|
||||||
->setSynopsis(
|
|
||||||
pht(
|
|
||||||
'Simulate sending an SMS. This may be useful to test your SMS '.
|
|
||||||
'configuration, or while developing new SMS adapters.'))
|
|
||||||
->setExamples("**send-test** --to 12345678 --body 'pizza time yet?'")
|
|
||||||
->setArguments(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'to',
|
|
||||||
'param' => 'number',
|
|
||||||
'help' => pht('Send SMS "To:" the specified number.'),
|
|
||||||
'repeat' => true,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'name' => 'body',
|
|
||||||
'param' => 'text',
|
|
||||||
'help' => pht('Send SMS with the specified body.'),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
$tos = $args->getArg('to');
|
|
||||||
$body = $args->getArg('body');
|
|
||||||
|
|
||||||
PhabricatorWorker::setRunAllTasksInProcess(true);
|
|
||||||
PhabricatorSMSImplementationAdapter::sendSMS($tos, $body);
|
|
||||||
|
|
||||||
$console->writeErr(
|
|
||||||
"%s\n\n phabricator/ $ ./bin/sms list-outbound \n\n",
|
|
||||||
pht(
|
|
||||||
'Send completed! You can view the list of SMS messages sent by '.
|
|
||||||
'running this command:'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSManagementShowOutboundWorkflow
|
|
||||||
extends PhabricatorSMSManagementWorkflow {
|
|
||||||
|
|
||||||
protected function didConstruct() {
|
|
||||||
$this
|
|
||||||
->setName('show-outbound')
|
|
||||||
->setSynopsis(pht('Show diagnostic details about outbound SMS.'))
|
|
||||||
->setExamples(
|
|
||||||
'**show-outbound** --id 1 --id 2')
|
|
||||||
->setArguments(
|
|
||||||
array(
|
|
||||||
array(
|
|
||||||
'name' => 'id',
|
|
||||||
'param' => 'id',
|
|
||||||
'help' => pht('Show details about outbound SMS with given ID.'),
|
|
||||||
'repeat' => true,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
|
|
||||||
$ids = $args->getArg('id');
|
|
||||||
if (!$ids) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
"Use the '%s' flag to specify one or more SMS messages to show.",
|
|
||||||
'--id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$messages = id(new PhabricatorSMS())->loadAllWhere(
|
|
||||||
'id IN (%Ld)',
|
|
||||||
$ids);
|
|
||||||
|
|
||||||
if ($ids) {
|
|
||||||
$ids = array_fuse($ids);
|
|
||||||
$missing = array_diff_key($ids, $messages);
|
|
||||||
if ($missing) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht(
|
|
||||||
'Some specified SMS messages do not exist: %s',
|
|
||||||
implode(', ', array_keys($missing))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$last_key = last_key($messages);
|
|
||||||
foreach ($messages as $message_key => $message) {
|
|
||||||
$info = array();
|
|
||||||
|
|
||||||
$info[] = pht('PROPERTIES');
|
|
||||||
$info[] = pht('ID: %d', $message->getID());
|
|
||||||
$info[] = pht('Status: %s', $message->getSendStatus());
|
|
||||||
$info[] = pht('To: %s', $message->getToNumber());
|
|
||||||
$info[] = pht('From: %s', $message->getFromNumber());
|
|
||||||
|
|
||||||
$info[] = null;
|
|
||||||
$info[] = pht('BODY');
|
|
||||||
$info[] = $message->getBody();
|
|
||||||
$info[] = null;
|
|
||||||
|
|
||||||
$console->writeOut('%s', implode("\n", $info));
|
|
||||||
|
|
||||||
if ($message_key != $last_key) {
|
|
||||||
$console->writeOut("\n%s\n\n", str_repeat('-', 80));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
abstract class PhabricatorSMSManagementWorkflow
|
|
||||||
extends PhabricatorManagementWorkflow {}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMS
|
|
||||||
extends PhabricatorSMSDAO {
|
|
||||||
|
|
||||||
const MAXIMUM_SEND_TRIES = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status constants should be 16 characters or less. See status entries
|
|
||||||
* for details on what they indicate about the underlying SMS.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// in the beginning, all SMS are unsent
|
|
||||||
const STATUS_UNSENT = 'unsent';
|
|
||||||
// that nebulous time when we've sent it from Phabricator but haven't
|
|
||||||
// heard anything from the external API
|
|
||||||
const STATUS_SENT_UNCONFIRMED = 'sent-unconfirmed';
|
|
||||||
// "success"
|
|
||||||
const STATUS_SENT = 'sent';
|
|
||||||
// "fail" but we'll try again
|
|
||||||
const STATUS_FAILED = 'failed';
|
|
||||||
// we're giving up on our external API partner
|
|
||||||
const STATUS_FAILED_PERMANENTLY = 'permafailed';
|
|
||||||
|
|
||||||
const SHORTNAME_PLACEHOLDER = 'phabricator';
|
|
||||||
|
|
||||||
protected $providerShortName;
|
|
||||||
protected $providerSMSID;
|
|
||||||
// numbers can be up to 20 digits long
|
|
||||||
protected $toNumber;
|
|
||||||
protected $fromNumber;
|
|
||||||
protected $body;
|
|
||||||
protected $sendStatus;
|
|
||||||
|
|
||||||
public static function initializeNewSMS($body) {
|
|
||||||
// NOTE: these values will be updated to correct values when the
|
|
||||||
// SMS is sent for the first time. In particular, the ProviderShortName
|
|
||||||
// and ProviderSMSID are totally garbage data before a send it attempted.
|
|
||||||
return id(new PhabricatorSMS())
|
|
||||||
->setBody($body)
|
|
||||||
->setSendStatus(self::STATUS_UNSENT)
|
|
||||||
->setProviderShortName(self::SHORTNAME_PLACEHOLDER)
|
|
||||||
->setProviderSMSID(Filesystem::readRandomCharacters(40));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getConfiguration() {
|
|
||||||
return array(
|
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
|
||||||
'providerShortName' => 'text16',
|
|
||||||
'providerSMSID' => 'text40',
|
|
||||||
'toNumber' => 'text20',
|
|
||||||
'fromNumber' => 'text20?',
|
|
||||||
'body' => 'text',
|
|
||||||
'sendStatus' => 'text16?',
|
|
||||||
),
|
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
|
||||||
'key_provider' => array(
|
|
||||||
'columns' => array('providerSMSID', 'providerShortName'),
|
|
||||||
'unique' => true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
) + parent::getConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTableName() {
|
|
||||||
// Slightly non-standard, but otherwise this class needs "MetaMTA" in its
|
|
||||||
// name. :/
|
|
||||||
return 'sms';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasBeenSentAtLeastOnce() {
|
|
||||||
return ($this->getProviderShortName() !=
|
|
||||||
self::SHORTNAME_PLACEHOLDER);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
abstract class PhabricatorSMSDAO
|
|
||||||
extends PhabricatorLiskDAO {
|
|
||||||
|
|
||||||
|
|
||||||
public function getApplicationName() {
|
|
||||||
return 'metamta';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSDemultiplexWorker extends PhabricatorSMSWorker {
|
|
||||||
|
|
||||||
protected function doWork() {
|
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
|
||||||
|
|
||||||
$task_data = $this->getTaskData();
|
|
||||||
|
|
||||||
$to_numbers = idx($task_data, 'toNumbers');
|
|
||||||
if (!$to_numbers) {
|
|
||||||
// If we don't have any to numbers, don't send any sms.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($to_numbers as $number) {
|
|
||||||
// NOTE: we will set the fromNumber and the proper provider data
|
|
||||||
// in the `PhabricatorSMSSendWorker`.
|
|
||||||
$sms = PhabricatorSMS::initializeNewSMS($task_data['body']);
|
|
||||||
$sms->setToNumber($number);
|
|
||||||
$sms->save();
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorSMSSendWorker',
|
|
||||||
array(
|
|
||||||
'smsID' => $sms->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class PhabricatorSMSSendWorker extends PhabricatorSMSWorker {
|
|
||||||
|
|
||||||
public function getMaximumRetryCount() {
|
|
||||||
return PhabricatorSMS::MAXIMUM_SEND_TRIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWaitBeforeRetry(PhabricatorWorkerTask $task) {
|
|
||||||
return phutil_units('1 minute in seconds');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function doWork() {
|
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
|
||||||
|
|
||||||
$task_data = $this->getTaskData();
|
|
||||||
|
|
||||||
$sms = id(new PhabricatorSMS())
|
|
||||||
->loadOneWhere('id = %d', $task_data['smsID']);
|
|
||||||
|
|
||||||
if (!$sms) {
|
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
|
||||||
pht('SMS object was not found.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// this has the potential to be updated asynchronously
|
|
||||||
if ($sms->getSendStatus() == PhabricatorSMS::STATUS_SENT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$adapter = PhabricatorEnv::getEnvConfig('sms.default-adapter');
|
|
||||||
$adapter = newv($adapter, array());
|
|
||||||
if ($sms->hasBeenSentAtLeastOnce()) {
|
|
||||||
$up_to_date_status = $adapter->pollSMSSentStatus($sms);
|
|
||||||
if ($up_to_date_status) {
|
|
||||||
$sms->setSendStatus($up_to_date_status);
|
|
||||||
if ($up_to_date_status == PhabricatorSMS::STATUS_SENT) {
|
|
||||||
$sms->save();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO - re-jigger this so we can try if appropos (e.g. rate limiting)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$from_number = PhabricatorEnv::getEnvConfig('sms.default-sender');
|
|
||||||
// always set the from number if we get this far in case of configuration
|
|
||||||
// changes.
|
|
||||||
$sms->setFromNumber($from_number);
|
|
||||||
|
|
||||||
$adapter->setTo($sms->getToNumber());
|
|
||||||
$adapter->setFrom($sms->getFromNumber());
|
|
||||||
$adapter->setBody($sms->getBody());
|
|
||||||
// give the provider name the same treatment as phone number
|
|
||||||
$sms->setProviderShortName($adapter->getProviderShortName());
|
|
||||||
|
|
||||||
if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
|
|
||||||
$sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY);
|
|
||||||
$sms->save();
|
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
|
||||||
pht(
|
|
||||||
'Phabricator is running in silent mode. See `%s` '.
|
|
||||||
'in the configuration to change this setting.',
|
|
||||||
'phabricator.silent'));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$result = $adapter->send();
|
|
||||||
list($sms_id, $sent_status) = $adapter->getSMSDataFromResult($result);
|
|
||||||
} catch (PhabricatorWorkerPermanentFailureException $e) {
|
|
||||||
$sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY);
|
|
||||||
$sms->save();
|
|
||||||
throw $e;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY);
|
|
||||||
$sms->save();
|
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
|
||||||
$e->getMessage());
|
|
||||||
}
|
|
||||||
$sms->setProviderSMSID($sms_id);
|
|
||||||
$sms->setSendStatus($sent_status);
|
|
||||||
$sms->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
abstract class PhabricatorSMSWorker
|
|
||||||
extends PhabricatorWorker {}
|
|
Loading…
Reference in a new issue