Enabling Text Message Reminders Using Modern Events Calendar by Webnus

As of December 2024, Webnus’ Modern Events Calendar doesn’t support sending text (SMS) reminders out of the box.  However, you can fork its code to be able to do so; here’s one approach below, using Twilio (which costs about $7 per 1k messages):

Sending an SMS at time of registration

  1. Create the following new PHP file in your theme, let’s call it mec-sms.php.  Be sure to specify your account_sid, auth_token, and twilio phone number.
            <?php

class DsaMecSms
{
    const ACCOUNT_SID = 'XXX';
    const AUTH_TOKEN = 'XXX';
    const TWILIO_PHONE_NUMBER = '+19704271234'; // Your Twilio phone number
    const TWILIO_API_URL = 'https://api.twilio.com/2010-04-01/Accounts';

    const DSA_SMS_DEBUG = true;

    private $postData;

    public function __construct($postData)
    {
        $this->postData = $postData;
    }

    public function processBooking()
    {
        try {
            $bookingData = $this->postData;

            if (self::DSA_SMS_DEBUG) {
                $this->logToFile("Received Booking Data: " . print_r($bookingData, true));
            }

            $bookingId = $bookingData['id'] ?? null;
            $eventTitle = $bookingData['event']['title'] ?? 'Unknown Event';
            $startDate = $bookingData['start'] ?? 'Unknown Start Date';
            $endDate = $bookingData['end'] ?? 'Unknown End Date';

            if (self::DSA_SMS_DEBUG) {
                $this->logToFile("Processing event: $eventTitle, Booking ID: $bookingId");
            }

            $attendees = $bookingData['attendees'] ?? [];
            foreach ($attendees as $attendee) {
                $attendeeName = $attendee['name'] ?? 'Unknown Name';
                $phoneNumber = $attendee['fields'][0]['value'] ?? null;

                if (!$phoneNumber) {
                    $this->logToFile("Skipping attendee due to missing phone number.");
                    continue;
                }

                $this->sendConfirmationMessage($attendeeName, $eventTitle, $startDate, $phoneNumber);
            }
        } catch (Exception $e) {
            $this->logToFile("Error in processBooking: " . $e->getMessage());
            throw $e;
        }
    }

    private function sendConfirmationMessage($name, $event, $start, $phone)
    {
        $message = $this->generateConfirmationMessage($name, $event, $start);

        if (self::DSA_SMS_DEBUG) {
            $this->logToFile("Sending Message: $message to $phone");
        }

        $this->sendSMS($phone, $message);
    }

    private function generateConfirmationMessage($name, $event, $start)
    {
        $eventDate = date('l, F j, Y', strtotime($start));
        $eventTime = date('g:i A', strtotime($start));
        $eventLocation = "Student Resolution Center";

        return sprintf(
            "Workshop reminder: %s on %s at %s with the Student Resolution Center. Call 970-491-7165 to cancel.",
            $event,
            $eventDate,
            $eventTime
        );
    }

    private function sendSMS($toPhone, $message)
    {
        $url = sprintf(
            "%s/%s/Messages.json",
            self::TWILIO_API_URL,
            self::ACCOUNT_SID
        );

        $postFields = http_build_query([
            'To' => $toPhone,
            'From' => self::TWILIO_PHONE_NUMBER,
            'Body' => $message,
        ]);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_USERPWD, self::ACCOUNT_SID . ':' . self::AUTH_TOKEN);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode != 201) { // Twilio returns 201 for successful message creation
            $this->logToFile("Failed to send SMS to $toPhone. Response: $response");
            throw new Exception("Error sending SMS: $response");
        }

        $this->logToFile("SMS sent successfully to $toPhone.");
    }

    private function logToFile($message)
    {
        $logFile = __DIR__ . '/mec-booking-data.log';
        file_put_contents($logFile, date('[Y-m-d H:i:s] ') . $message . PHP_EOL, FILE_APPEND);
    }
}


// Prevent the entry-point code from running if included directly
if (!defined('DSA_SMS_SCRIPT')) {
    header('Content-Type: application/json');
  
    try {
        $input = $_POST;
        if (empty($input)) {
            $input = json_decode(file_get_contents('php://input'), true);
        }
        if (!$input) {
            throw new Exception("Invalid or missing input data");
        }
  
        $dsaMecSms = new DsaMecSms($input);
        $dsaMecSms->processBooking();
  
        echo json_encode(['status' => 'success', 'message' => 'Booking processed successfully.']);
    } catch (Exception $e) {
        http_response_code(400);
        echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
    }
  
    exit; // Prevent further execution
  }


        
  1. Next enable the booking module at https://YOURSITE/wp-admin/admin.php?page=MEC-settings&tab=MEC-booking.
  2. Under the Advanced tab, tick the box for “Enable webhooks module”
  3. In this example, I checked, “Auto verification for free bookings” to avoid needing to verify bookings
  4. Next visit Booking –> Booking Form, as you will need to add a new attendee field for Telephone number and hit Save.
  5. Next you should see a Webhooks link under the M.E. Calendar sidebar. E.g., https://YOURSITE/wp-admin/edit.php?post_type=mec-webhooks
  6. Click  Add Webhook.
  7. Leave the hook as Booking Confirmed, Request as POST, and enter the url path to the file you just created.  E.g., https://YOURSITE/wp-content/themes/hello-child/mec-sms.php and click Publish
  8. Be sure to create a fake event that has at least one ticket option so that visitors can book themselves

Sending an SMS Reminder

If you additionally want to send a reminder text message in advance, here’s how:

  1. Go to Notifications –> Booking Reminder and click, “Enable booking reminder notification”
  2. In this example, I’m going to send reminders one day in advance, so set Hours to be 24 and enter a subject and body.  Click Save
  3. Next we are going to have to fork one of MEC’s files to include SMS functionality.  Open/wp-content/plugins/modern-events-calendar/app/crons/booking-reminder.php in your editor of choice
  4. We are going to replace the foreach loop on lines 106-115.  First remove those lines and paste in the following code:
            foreach ($bookings as $booking) {
            // Skip if the booking's post status is 'trash'
            if ('trash' === get_post_status($booking->ID)) {
                continue; // Skip this iteration and move to the next booking
            }
            
            $event_id = get_post_meta($booking->ID, 'mec_event_id', true);
            $end = $db->select("SELECT `tend` FROM `#__mec_dates` WHERE `post_id`='" . $event_id . "' AND `tstart`='" . $booking->mec_timestamp . "' LIMIT 1", 'loadResult');

            $timestamps = $booking->mec_timestamp . ':' . $end;

            $result = $notif->booking_reminder($booking->ID, $timestamps);
            if ($result) {
                $sent_reminders++;
            }

            try {
                
                // Define constant to skip direct entry-point code
                define('MEC_SMS_SCRIPT', true);

                // to include the SMS functionality
                require_once get_stylesheet_directory() . '/mec-sms.php';

                // Fetch phone number from serialized attendees data
                $attendees_data = get_post_meta($booking->ID, 'mec_attendees', true);
                $attendees_data = maybe_unserialize($attendees_data); // Unserialize if needed

                // The following code will help you determine the position of phone # in the array
                //echo '<pre>'; 
                //print_r($attendees_data);  
                //echo '</pre>';

                if (is_array($attendees_data) && isset($attendees_data[0])) {
                    // Extract phone number from the 'reg' array (index 3)
                    $phone = $attendees_data[0]['reg'][2] ?? null;

                    if ($phone) {
                        $message = sprintf(
                            "Reminder: Your event '%s' is starting soon. Please be on time.",
                            get_the_title($event_id) // Fetch the event title
                        );

                            // Prepare the booking data in the format expected by the DsaMecSms class
                            $postData = [
                                'id' => $booking->ID,
                                'event' => [
                                    'title' => get_the_title($event_id),
                                ],
                                'start' => date('Y-m-d H:i:s', $booking->mec_timestamp),
                                'attendees' => [
                                    [
                                        'name' => $booking->post_title,
                                        'email' => '', // Extract email if available, or set as blank
                                        'fields' => [
                                            ['value' => $phone], // Phone number field
                                        ],
                                    ],
                                ],
                            ];

                            // Instantiate the DsaMecSms class
                            $smsHandler = new DsaMecSms($postData);

                            // Process the booking to send SMS
                            $smsHandler->processBooking();
                        
                    } else {
                        //echo 'Phone number not found.';
                    }
                } else {
                    //echo 'No attendee data found.';
                }
            } catch (Exception $e) {
                echo 'Message: ' . $e->getMessage();
            }
        }
        
    1. Next let’s create an event that is 24 hours into the future.  Once that is done, register for the event using a valid phone number that can receive text messages.  You should receive both an e-mail and text reminder.
    2. Finally, per the directions at https://YOURSITE/wp-admin/admin.php?page=MEC-settings&tab=MEC-notifications#booking_reminder you will need to set a cron job that hits your site once an hour.  I use UpTime robot for this task, specifically set to hit https://YOURSITE/wp-content/plugins/modern-events-calendar/app/crons/booking-reminder.php once per hour.

That’s it!  Given that we have just forked the MEc plugin, you will need to remember to overwrite lines 106-115 in the booking-reminder.php file each time you upgrade, but other than that, you should be all set to send e-mail and text reminders to your visitors!