@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

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

+1 -707
+1
resources/sql/autopatches/20190101.sms.01.drop.sql
··· 1 + DROP TABLE {$NAMESPACE}_metamta.sms;
-21
scripts/sms/manage_sms.php
··· 1 - #!/usr/bin/env php 2 - <?php 3 - 4 - $root = dirname(dirname(dirname(__FILE__))); 5 - require_once $root.'/scripts/__init_script__.php'; 6 - 7 - $args = new PhutilArgumentParser($argv); 8 - $args->setTagline(pht('manage SMS')); 9 - $args->setSynopsis(<<<EOSYNOPSIS 10 - **sms** __command__ [__options__] 11 - Manage Phabricator SMS stuff. 12 - 13 - EOSYNOPSIS 14 - ); 15 - $args->parseStandardArguments(); 16 - 17 - $workflows = id(new PhutilClassMapQuery()) 18 - ->setAncestorClass('PhabricatorSMSManagementWorkflow') 19 - ->execute(); 20 - $workflows[] = new PhutilHelpArgumentWorkflow(); 21 - $args->parseWorkflows($workflows);
-26
src/__phutil_library_map__.php
··· 4239 4239 'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php', 4240 4240 'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php', 4241 4241 'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php', 4242 - 'PhabricatorSMS' => 'infrastructure/sms/storage/PhabricatorSMS.php', 4243 - 'PhabricatorSMSConfigOptions' => 'applications/config/option/PhabricatorSMSConfigOptions.php', 4244 - 'PhabricatorSMSDAO' => 'infrastructure/sms/storage/PhabricatorSMSDAO.php', 4245 - 'PhabricatorSMSDemultiplexWorker' => 'infrastructure/sms/worker/PhabricatorSMSDemultiplexWorker.php', 4246 - 'PhabricatorSMSImplementationAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationAdapter.php', 4247 - 'PhabricatorSMSImplementationTestBlackholeAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTestBlackholeAdapter.php', 4248 - 'PhabricatorSMSImplementationTwilioAdapter' => 'infrastructure/sms/adapter/PhabricatorSMSImplementationTwilioAdapter.php', 4249 - 'PhabricatorSMSManagementListOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementListOutboundWorkflow.php', 4250 - 'PhabricatorSMSManagementSendTestWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementSendTestWorkflow.php', 4251 - 'PhabricatorSMSManagementShowOutboundWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementShowOutboundWorkflow.php', 4252 - 'PhabricatorSMSManagementWorkflow' => 'infrastructure/sms/management/PhabricatorSMSManagementWorkflow.php', 4253 - 'PhabricatorSMSSendWorker' => 'infrastructure/sms/worker/PhabricatorSMSSendWorker.php', 4254 - 'PhabricatorSMSWorker' => 'infrastructure/sms/worker/PhabricatorSMSWorker.php', 4255 4242 'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php', 4256 4243 'PhabricatorSSHKeyGenerator' => 'infrastructure/util/PhabricatorSSHKeyGenerator.php', 4257 4244 'PhabricatorSSHKeysSettingsPanel' => 'applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php', ··· 10269 10256 'PhabricatorResourceSite' => 'PhabricatorSite', 10270 10257 'PhabricatorRobotsController' => 'PhabricatorController', 10271 10258 'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine', 10272 - 'PhabricatorSMS' => 'PhabricatorSMSDAO', 10273 - 'PhabricatorSMSConfigOptions' => 'PhabricatorApplicationConfigOptions', 10274 - 'PhabricatorSMSDAO' => 'PhabricatorLiskDAO', 10275 - 'PhabricatorSMSDemultiplexWorker' => 'PhabricatorSMSWorker', 10276 - 'PhabricatorSMSImplementationAdapter' => 'Phobject', 10277 - 'PhabricatorSMSImplementationTestBlackholeAdapter' => 'PhabricatorSMSImplementationAdapter', 10278 - 'PhabricatorSMSImplementationTwilioAdapter' => 'PhabricatorSMSImplementationAdapter', 10279 - 'PhabricatorSMSManagementListOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow', 10280 - 'PhabricatorSMSManagementSendTestWorkflow' => 'PhabricatorSMSManagementWorkflow', 10281 - 'PhabricatorSMSManagementShowOutboundWorkflow' => 'PhabricatorSMSManagementWorkflow', 10282 - 'PhabricatorSMSManagementWorkflow' => 'PhabricatorManagementWorkflow', 10283 - 'PhabricatorSMSSendWorker' => 'PhabricatorSMSWorker', 10284 - 'PhabricatorSMSWorker' => 'PhabricatorWorker', 10285 10259 'PhabricatorSQLPatchList' => 'Phobject', 10286 10260 'PhabricatorSSHKeyGenerator' => 'Phobject', 10287 10261 'PhabricatorSSHKeysSettingsPanel' => 'PhabricatorSettingsPanel',
-60
src/applications/config/option/PhabricatorSMSConfigOptions.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSConfigOptions 4 - extends PhabricatorApplicationConfigOptions { 5 - 6 - public function getName() { 7 - return pht('SMS'); 8 - } 9 - 10 - public function getDescription() { 11 - return pht('Configure SMS.'); 12 - } 13 - 14 - public function getIcon() { 15 - return 'fa-mobile'; 16 - } 17 - 18 - public function getGroup() { 19 - return 'core'; 20 - } 21 - 22 - public function getOptions() { 23 - $adapter_description = pht( 24 - 'Adapter class to use to transmit SMS to an external provider. A given '. 25 - 'external provider will most likely need more configuration which will '. 26 - 'most likely require registration and payment for the service.'); 27 - 28 - return array( 29 - $this->newOption( 30 - 'sms.default-sender', 31 - 'string', 32 - null) 33 - ->setDescription(pht('Default "from" number.')) 34 - ->addExample('8675309', 'Jenny still has this number') 35 - ->addExample('18005555555', 'Maybe not a real number'), 36 - $this->newOption( 37 - 'sms.default-adapter', 38 - 'class', 39 - null) 40 - ->setBaseClass('PhabricatorSMSImplementationAdapter') 41 - ->setSummary(pht('Control how SMS is sent.')) 42 - ->setDescription($adapter_description), 43 - $this->newOption( 44 - 'twilio.account-sid', 45 - 'string', 46 - null) 47 - ->setDescription(pht('Account ID on Twilio service.')) 48 - ->setLocked(true) 49 - ->addExample('gf5kzccfn2sfknpnadvz7kokv6nz5v', pht('30 characters')), 50 - $this->newOption( 51 - 'twilio.auth-token', 52 - 'string', 53 - null) 54 - ->setDescription(pht('Authorization token from Twilio service.')) 55 - ->setHidden(true) 56 - ->addExample('f3jsi4i67wiwt6w54hf2zwvy3fjf5h', pht('30 characters')), 57 - ); 58 - } 59 - 60 - }
-88
src/infrastructure/sms/adapter/PhabricatorSMSImplementationAdapter.php
··· 1 - <?php 2 - 3 - abstract class PhabricatorSMSImplementationAdapter extends Phobject { 4 - 5 - private $fromNumber; 6 - private $toNumber; 7 - private $body; 8 - 9 - public function setFrom($number) { 10 - $this->fromNumber = $number; 11 - return $this; 12 - } 13 - 14 - public function getFrom() { 15 - return $this->fromNumber; 16 - } 17 - 18 - public function setTo($number) { 19 - $this->toNumber = $number; 20 - return $this; 21 - } 22 - 23 - public function getTo() { 24 - return $this->toNumber; 25 - } 26 - 27 - public function setBody($body) { 28 - $this->body = $body; 29 - return $this; 30 - } 31 - 32 - public function getBody() { 33 - return $this->body; 34 - } 35 - 36 - /** 37 - * 16 characters or less, to be used in database columns and exposed 38 - * to administrators during configuration directly. 39 - */ 40 - abstract public function getProviderShortName(); 41 - 42 - /** 43 - * Send the message. Generally, this means connecting to some service and 44 - * handing data to it. SMS APIs are generally asynchronous, so truly 45 - * determining success or failure is probably impossible synchronously. 46 - * 47 - * That said, if the adapter determines that the SMS will never be 48 - * deliverable, or there is some other known failure, it should throw 49 - * an exception. 50 - * 51 - * @return null 52 - */ 53 - abstract public function send(); 54 - 55 - /** 56 - * Most (all?) SMS APIs are asynchronous, but some do send back some 57 - * initial information. Use this hook to determine what the updated 58 - * sentStatus should be and what the provider is using for an SMS ID, 59 - * as well as throw exceptions if there are any failures. 60 - * 61 - * @return array Tuple of ($sms_id and $sent_status) 62 - */ 63 - abstract public function getSMSDataFromResult($result); 64 - 65 - /** 66 - * Due to the asynchronous nature of sending SMS messages, it can be 67 - * necessary to poll the provider regarding the sent status of a given 68 - * sms. 69 - * 70 - * For now, this *MUST* be implemented and *MUST* work. 71 - */ 72 - abstract public function pollSMSSentStatus(PhabricatorSMS $sms); 73 - 74 - /** 75 - * Convenience function to handle sending an SMS. 76 - */ 77 - public static function sendSMS(array $to_numbers, $body) { 78 - PhabricatorWorker::scheduleTask( 79 - 'PhabricatorSMSDemultiplexWorker', 80 - array( 81 - 'toNumbers' => $to_numbers, 82 - 'body' => $body, 83 - ), 84 - array( 85 - 'priority' => PhabricatorWorker::PRIORITY_ALERTS, 86 - )); 87 - } 88 - }
-31
src/infrastructure/sms/adapter/PhabricatorSMSImplementationTestBlackholeAdapter.php
··· 1 - <?php 2 - 3 - /** 4 - * This is useful for testing, but otherwise your SMS ends up in a blackhole. 5 - */ 6 - final class PhabricatorSMSImplementationTestBlackholeAdapter 7 - extends PhabricatorSMSImplementationAdapter { 8 - 9 - public function getProviderShortName() { 10 - return 'testtesttest'; 11 - } 12 - 13 - public function send() { 14 - // I guess this is what a blackhole looks like 15 - } 16 - 17 - public function getSMSDataFromResult($result) { 18 - return array( 19 - Filesystem::readRandomCharacters(40), 20 - PhabricatorSMS::STATUS_SENT, 21 - ); 22 - } 23 - 24 - public function pollSMSSentStatus(PhabricatorSMS $sms) { 25 - if ($sms->getID()) { 26 - return PhabricatorSMS::STATUS_SENT; 27 - } 28 - return PhabricatorSMS::STATUS_SENT_UNCONFIRMED; 29 - } 30 - 31 - }
-99
src/infrastructure/sms/adapter/PhabricatorSMSImplementationTwilioAdapter.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSImplementationTwilioAdapter 4 - extends PhabricatorSMSImplementationAdapter { 5 - 6 - public function getProviderShortName() { 7 - return 'twilio'; 8 - } 9 - 10 - /** 11 - * @phutil-external-symbol class Services_Twilio 12 - */ 13 - private function buildClient() { 14 - $root = dirname(phutil_get_library_root('phabricator')); 15 - require_once $root.'/externals/twilio-php/Services/Twilio.php'; 16 - $account_sid = PhabricatorEnv::getEnvConfig('twilio.account-sid'); 17 - $auth_token = PhabricatorEnv::getEnvConfig('twilio.auth-token'); 18 - return new Services_Twilio($account_sid, $auth_token); 19 - } 20 - 21 - /** 22 - * @phutil-external-symbol class Services_Twilio_RestException 23 - */ 24 - public function send() { 25 - $client = $this->buildClient(); 26 - 27 - try { 28 - $message = $client->account->sms_messages->create( 29 - $this->formatNumberForSMS($this->getFrom()), 30 - $this->formatNumberForSMS($this->getTo()), 31 - $this->getBody(), 32 - array()); 33 - } catch (Services_Twilio_RestException $e) { 34 - $message = sprintf( 35 - 'HTTP Code %d: %s', 36 - $e->getStatus(), 37 - $e->getMessage()); 38 - 39 - // Twilio tries to provide a link to more specific details if they can. 40 - if ($e->getInfo()) { 41 - $message .= sprintf(' For more information, see %s.', $e->getInfo()); 42 - } 43 - throw new PhabricatorWorkerPermanentFailureException($message); 44 - } 45 - return $message; 46 - } 47 - 48 - public function getSMSDataFromResult($result) { 49 - return array($result->sid, $this->getSMSStatus($result->status)); 50 - } 51 - 52 - public function pollSMSSentStatus(PhabricatorSMS $sms) { 53 - $client = $this->buildClient(); 54 - $message = $client->account->messages->get($sms->getProviderSMSID()); 55 - 56 - return $this->getSMSStatus($message->status); 57 - } 58 - 59 - /** 60 - * See https://www.twilio.com/docs/api/rest/sms#sms-status-values. 61 - */ 62 - private function getSMSStatus($twilio_status) { 63 - switch ($twilio_status) { 64 - case 'failed': 65 - $status = PhabricatorSMS::STATUS_FAILED; 66 - break; 67 - case 'sent': 68 - $status = PhabricatorSMS::STATUS_SENT; 69 - break; 70 - case 'sending': 71 - case 'queued': 72 - default: 73 - $status = PhabricatorSMS::STATUS_SENT_UNCONFIRMED; 74 - break; 75 - } 76 - return $status; 77 - } 78 - 79 - /** 80 - * We expect numbers to be plainly entered - i.e. the preg_replace here 81 - * should do nothing - but try hard to format anyway. 82 - * 83 - * Twilio uses E164 format, e.g. +15551234567 84 - */ 85 - private function formatNumberForSMS($number) { 86 - $number = preg_replace('/[^0-9]/', '', $number); 87 - $first_char = substr($number, 0, 1); 88 - switch ($first_char) { 89 - case '1': 90 - $prepend = '+'; 91 - break; 92 - default: 93 - $prepend = '+1'; 94 - break; 95 - } 96 - return $prepend.$number; 97 - } 98 - 99 - }
-54
src/infrastructure/sms/management/PhabricatorSMSManagementListOutboundWorkflow.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSManagementListOutboundWorkflow 4 - extends PhabricatorSMSManagementWorkflow { 5 - 6 - protected function didConstruct() { 7 - $this 8 - ->setName('list-outbound') 9 - ->setSynopsis(pht('List outbound SMS messages sent by Phabricator.')) 10 - ->setExamples('**list-outbound**') 11 - ->setArguments( 12 - array( 13 - array( 14 - 'name' => 'limit', 15 - 'param' => 'N', 16 - 'default' => 100, 17 - 'help' => pht( 18 - 'Show a specific number of SMS messages (default 100).'), 19 - ), 20 - )); 21 - } 22 - 23 - public function execute(PhutilArgumentParser $args) { 24 - $console = PhutilConsole::getConsole(); 25 - $viewer = $this->getViewer(); 26 - 27 - $sms_messages = id(new PhabricatorSMS())->loadAllWhere( 28 - '1 = 1 ORDER BY id DESC LIMIT %d', 29 - $args->getArg('limit')); 30 - 31 - if (!$sms_messages) { 32 - $console->writeErr("%s\n", pht('No sent SMS.')); 33 - return 0; 34 - } 35 - 36 - $table = id(new PhutilConsoleTable()) 37 - ->setShowHeader(false) 38 - ->addColumn('id', array('title' => pht('ID'))) 39 - ->addColumn('status', array('title' => pht('Status'))) 40 - ->addColumn('recv', array('title' => pht('Recipient'))); 41 - 42 - foreach (array_reverse($sms_messages) as $sms) { 43 - $table->addRow(array( 44 - 'id' => $sms->getID(), 45 - 'status' => $sms->getSendStatus(), 46 - 'recv' => $sms->getToNumber(), 47 - )); 48 - } 49 - 50 - $table->draw(); 51 - return 0; 52 - } 53 - 54 - }
-47
src/infrastructure/sms/management/PhabricatorSMSManagementSendTestWorkflow.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSManagementSendTestWorkflow 4 - extends PhabricatorSMSManagementWorkflow { 5 - 6 - protected function didConstruct() { 7 - $this 8 - ->setName('send-test') 9 - ->setSynopsis( 10 - pht( 11 - 'Simulate sending an SMS. This may be useful to test your SMS '. 12 - 'configuration, or while developing new SMS adapters.')) 13 - ->setExamples("**send-test** --to 12345678 --body 'pizza time yet?'") 14 - ->setArguments( 15 - array( 16 - array( 17 - 'name' => 'to', 18 - 'param' => 'number', 19 - 'help' => pht('Send SMS "To:" the specified number.'), 20 - 'repeat' => true, 21 - ), 22 - array( 23 - 'name' => 'body', 24 - 'param' => 'text', 25 - 'help' => pht('Send SMS with the specified body.'), 26 - ), 27 - )); 28 - } 29 - 30 - public function execute(PhutilArgumentParser $args) { 31 - $console = PhutilConsole::getConsole(); 32 - $viewer = $this->getViewer(); 33 - 34 - $tos = $args->getArg('to'); 35 - $body = $args->getArg('body'); 36 - 37 - PhabricatorWorker::setRunAllTasksInProcess(true); 38 - PhabricatorSMSImplementationAdapter::sendSMS($tos, $body); 39 - 40 - $console->writeErr( 41 - "%s\n\n phabricator/ $ ./bin/sms list-outbound \n\n", 42 - pht( 43 - 'Send completed! You can view the list of SMS messages sent by '. 44 - 'running this command:')); 45 - } 46 - 47 - }
-72
src/infrastructure/sms/management/PhabricatorSMSManagementShowOutboundWorkflow.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSManagementShowOutboundWorkflow 4 - extends PhabricatorSMSManagementWorkflow { 5 - 6 - protected function didConstruct() { 7 - $this 8 - ->setName('show-outbound') 9 - ->setSynopsis(pht('Show diagnostic details about outbound SMS.')) 10 - ->setExamples( 11 - '**show-outbound** --id 1 --id 2') 12 - ->setArguments( 13 - array( 14 - array( 15 - 'name' => 'id', 16 - 'param' => 'id', 17 - 'help' => pht('Show details about outbound SMS with given ID.'), 18 - 'repeat' => true, 19 - ), 20 - )); 21 - } 22 - 23 - public function execute(PhutilArgumentParser $args) { 24 - $console = PhutilConsole::getConsole(); 25 - 26 - $ids = $args->getArg('id'); 27 - if (!$ids) { 28 - throw new PhutilArgumentUsageException( 29 - pht( 30 - "Use the '%s' flag to specify one or more SMS messages to show.", 31 - '--id')); 32 - } 33 - 34 - $messages = id(new PhabricatorSMS())->loadAllWhere( 35 - 'id IN (%Ld)', 36 - $ids); 37 - 38 - if ($ids) { 39 - $ids = array_fuse($ids); 40 - $missing = array_diff_key($ids, $messages); 41 - if ($missing) { 42 - throw new PhutilArgumentUsageException( 43 - pht( 44 - 'Some specified SMS messages do not exist: %s', 45 - implode(', ', array_keys($missing)))); 46 - } 47 - } 48 - 49 - $last_key = last_key($messages); 50 - foreach ($messages as $message_key => $message) { 51 - $info = array(); 52 - 53 - $info[] = pht('PROPERTIES'); 54 - $info[] = pht('ID: %d', $message->getID()); 55 - $info[] = pht('Status: %s', $message->getSendStatus()); 56 - $info[] = pht('To: %s', $message->getToNumber()); 57 - $info[] = pht('From: %s', $message->getFromNumber()); 58 - 59 - $info[] = null; 60 - $info[] = pht('BODY'); 61 - $info[] = $message->getBody(); 62 - $info[] = null; 63 - 64 - $console->writeOut('%s', implode("\n", $info)); 65 - 66 - if ($message_key != $last_key) { 67 - $console->writeOut("\n%s\n\n", str_repeat('-', 80)); 68 - } 69 - } 70 - } 71 - 72 - }
-4
src/infrastructure/sms/management/PhabricatorSMSManagementWorkflow.php
··· 1 - <?php 2 - 3 - abstract class PhabricatorSMSManagementWorkflow 4 - extends PhabricatorManagementWorkflow {}
-75
src/infrastructure/sms/storage/PhabricatorSMS.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMS 4 - extends PhabricatorSMSDAO { 5 - 6 - const MAXIMUM_SEND_TRIES = 5; 7 - 8 - /** 9 - * Status constants should be 16 characters or less. See status entries 10 - * for details on what they indicate about the underlying SMS. 11 - */ 12 - 13 - // in the beginning, all SMS are unsent 14 - const STATUS_UNSENT = 'unsent'; 15 - // that nebulous time when we've sent it from Phabricator but haven't 16 - // heard anything from the external API 17 - const STATUS_SENT_UNCONFIRMED = 'sent-unconfirmed'; 18 - // "success" 19 - const STATUS_SENT = 'sent'; 20 - // "fail" but we'll try again 21 - const STATUS_FAILED = 'failed'; 22 - // we're giving up on our external API partner 23 - const STATUS_FAILED_PERMANENTLY = 'permafailed'; 24 - 25 - const SHORTNAME_PLACEHOLDER = 'phabricator'; 26 - 27 - protected $providerShortName; 28 - protected $providerSMSID; 29 - // numbers can be up to 20 digits long 30 - protected $toNumber; 31 - protected $fromNumber; 32 - protected $body; 33 - protected $sendStatus; 34 - 35 - public static function initializeNewSMS($body) { 36 - // NOTE: these values will be updated to correct values when the 37 - // SMS is sent for the first time. In particular, the ProviderShortName 38 - // and ProviderSMSID are totally garbage data before a send it attempted. 39 - return id(new PhabricatorSMS()) 40 - ->setBody($body) 41 - ->setSendStatus(self::STATUS_UNSENT) 42 - ->setProviderShortName(self::SHORTNAME_PLACEHOLDER) 43 - ->setProviderSMSID(Filesystem::readRandomCharacters(40)); 44 - } 45 - 46 - protected function getConfiguration() { 47 - return array( 48 - self::CONFIG_COLUMN_SCHEMA => array( 49 - 'providerShortName' => 'text16', 50 - 'providerSMSID' => 'text40', 51 - 'toNumber' => 'text20', 52 - 'fromNumber' => 'text20?', 53 - 'body' => 'text', 54 - 'sendStatus' => 'text16?', 55 - ), 56 - self::CONFIG_KEY_SCHEMA => array( 57 - 'key_provider' => array( 58 - 'columns' => array('providerSMSID', 'providerShortName'), 59 - 'unique' => true, 60 - ), 61 - ), 62 - ) + parent::getConfiguration(); 63 - } 64 - 65 - public function getTableName() { 66 - // Slightly non-standard, but otherwise this class needs "MetaMTA" in its 67 - // name. :/ 68 - return 'sms'; 69 - } 70 - 71 - public function hasBeenSentAtLeastOnce() { 72 - return ($this->getProviderShortName() != 73 - self::SHORTNAME_PLACEHOLDER); 74 - } 75 - }
-11
src/infrastructure/sms/storage/PhabricatorSMSDAO.php
··· 1 - <?php 2 - 3 - abstract class PhabricatorSMSDAO 4 - extends PhabricatorLiskDAO { 5 - 6 - 7 - public function getApplicationName() { 8 - return 'metamta'; 9 - } 10 - 11 - }
-30
src/infrastructure/sms/worker/PhabricatorSMSDemultiplexWorker.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSDemultiplexWorker extends PhabricatorSMSWorker { 4 - 5 - protected function doWork() { 6 - $viewer = PhabricatorUser::getOmnipotentUser(); 7 - 8 - $task_data = $this->getTaskData(); 9 - 10 - $to_numbers = idx($task_data, 'toNumbers'); 11 - if (!$to_numbers) { 12 - // If we don't have any to numbers, don't send any sms. 13 - return; 14 - } 15 - 16 - foreach ($to_numbers as $number) { 17 - // NOTE: we will set the fromNumber and the proper provider data 18 - // in the `PhabricatorSMSSendWorker`. 19 - $sms = PhabricatorSMS::initializeNewSMS($task_data['body']); 20 - $sms->setToNumber($number); 21 - $sms->save(); 22 - $this->queueTask( 23 - 'PhabricatorSMSSendWorker', 24 - array( 25 - 'smsID' => $sms->getID(), 26 - )); 27 - } 28 - } 29 - 30 - }
-85
src/infrastructure/sms/worker/PhabricatorSMSSendWorker.php
··· 1 - <?php 2 - 3 - final class PhabricatorSMSSendWorker extends PhabricatorSMSWorker { 4 - 5 - public function getMaximumRetryCount() { 6 - return PhabricatorSMS::MAXIMUM_SEND_TRIES; 7 - } 8 - 9 - public function getWaitBeforeRetry(PhabricatorWorkerTask $task) { 10 - return phutil_units('1 minute in seconds'); 11 - } 12 - 13 - protected function doWork() { 14 - $viewer = PhabricatorUser::getOmnipotentUser(); 15 - 16 - $task_data = $this->getTaskData(); 17 - 18 - $sms = id(new PhabricatorSMS()) 19 - ->loadOneWhere('id = %d', $task_data['smsID']); 20 - 21 - if (!$sms) { 22 - throw new PhabricatorWorkerPermanentFailureException( 23 - pht('SMS object was not found.')); 24 - } 25 - 26 - // this has the potential to be updated asynchronously 27 - if ($sms->getSendStatus() == PhabricatorSMS::STATUS_SENT) { 28 - return; 29 - } 30 - 31 - $adapter = PhabricatorEnv::getEnvConfig('sms.default-adapter'); 32 - $adapter = newv($adapter, array()); 33 - if ($sms->hasBeenSentAtLeastOnce()) { 34 - $up_to_date_status = $adapter->pollSMSSentStatus($sms); 35 - if ($up_to_date_status) { 36 - $sms->setSendStatus($up_to_date_status); 37 - if ($up_to_date_status == PhabricatorSMS::STATUS_SENT) { 38 - $sms->save(); 39 - return; 40 - } 41 - } 42 - // TODO - re-jigger this so we can try if appropos (e.g. rate limiting) 43 - return; 44 - } 45 - 46 - $from_number = PhabricatorEnv::getEnvConfig('sms.default-sender'); 47 - // always set the from number if we get this far in case of configuration 48 - // changes. 49 - $sms->setFromNumber($from_number); 50 - 51 - $adapter->setTo($sms->getToNumber()); 52 - $adapter->setFrom($sms->getFromNumber()); 53 - $adapter->setBody($sms->getBody()); 54 - // give the provider name the same treatment as phone number 55 - $sms->setProviderShortName($adapter->getProviderShortName()); 56 - 57 - if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { 58 - $sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY); 59 - $sms->save(); 60 - throw new PhabricatorWorkerPermanentFailureException( 61 - pht( 62 - 'Phabricator is running in silent mode. See `%s` '. 63 - 'in the configuration to change this setting.', 64 - 'phabricator.silent')); 65 - } 66 - 67 - try { 68 - $result = $adapter->send(); 69 - list($sms_id, $sent_status) = $adapter->getSMSDataFromResult($result); 70 - } catch (PhabricatorWorkerPermanentFailureException $e) { 71 - $sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY); 72 - $sms->save(); 73 - throw $e; 74 - } catch (Exception $e) { 75 - $sms->setSendStatus(PhabricatorSMS::STATUS_FAILED_PERMANENTLY); 76 - $sms->save(); 77 - throw new PhabricatorWorkerPermanentFailureException( 78 - $e->getMessage()); 79 - } 80 - $sms->setProviderSMSID($sms_id); 81 - $sms->setSendStatus($sent_status); 82 - $sms->save(); 83 - } 84 - 85 - }
-4
src/infrastructure/sms/worker/PhabricatorSMSWorker.php
··· 1 - <?php 2 - 3 - abstract class PhabricatorSMSWorker 4 - extends PhabricatorWorker {}