Skip to content

Instantly share code, notes, and snippets.

@Jach
Created July 1, 2023 19:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jach/55169177457086ce93a7236facc831b9 to your computer and use it in GitHub Desktop.
Save Jach/55169177457086ce93a7236facc831b9 to your computer and use it in GitHub Desktop.
Companion to the local script over at https://gist.github.com/Jach/41082f105f5779531e041a6d3afb21db which talks to this PHP service when a new community post has been made
<?php
/* USAGE:
* $send = array();
* $send['to'] = 'to@d.com';
* $send['from'] = 'from@d.com'; // defaults to 'admin@thisdomain.com'
* $send['subject'] = 'Sub';
* $send['html'] = '<a href="site">click me</a>'; // newlines == <br /> automagically
*
* $mail = new EasyMail($send);
* $mail->send();
*
* You can also send multiple messages.
*
* $mail->send($other_sendarray);
*
* Examples:
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="200"></td>
<td width="200"></td>
<td width="200"></td>
</tr>
</table>
<!-- End example table -->
<a href="some link" style="color:red; text-decoration:none;" target ="_blank" title="title"><span style="color:red;">blah blah</span></a>
<img src="full path to image" alt="Your alt text" title="Your title text" width="x" height="x" style="display:block;" />
*
*/
class EasyMail {
function __construct($params) {
if (!isset($_SERVER['SERVER_NAME'])) {
$domain = 'thejach.com';
} else {
$domain = str_replace('www.', '', $_SERVER['SERVER_NAME']);
}
$this->to = isset($params['to']) ? $params['to'] : '';
$this->from = 'donotreply@thejach.com';// isset($params['from']) ? $params['from'] : 'admin@' . $domain;
$this->subject = isset($params['subject']) ? $params['subject'] : 'Message From ' . $domain;
$this->html = isset($params['html']) ? mb_ereg_replace("\n", "\n<br />", $params['html']) : '';
$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-Type: text/html; charset=utf-8' . "\r\n";
$headers .= 'Content-Transfer-Encoding: base64' . "\r\n";
$headers .= "From: {$this->from}\r\n";
$this->headers = $headers;
}
function send($params=array()) {
if (!empty($params)) {
$this->__construct($params);
}
// Thanks to github.com/seanpowell/Email-Boilerplate
$this->boiler = <<<EOD
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{$this->subject}</title>
<style type="text/css">
/* Client-specific Styles */
#outlook a{padding:0;} /* Force Outlook to provide a "view in browser" button. */
body{width:100% !important;} .ReadMsgBody{width:100%;} .ExternalClass{width:100%;} /* Force Hotmail to display emails at full width */
body{-webkit-text-size-adjust:none; -ms-text-size-adjust:none;} /* Prevent Webkit and Windows Mobile platforms from changing default font sizes. */
/* Reset Styles */
body{margin:0; padding:0;}
img{height:auto; line-height:100%; outline:none; text-decoration:none;}
#backgroundTable{height:100% !important; margin:0; padding:0; width:100% !important;}
p {
margin: 1em 0;
}
h1, h2, h3, h4, h5, h6 {
color: black !important;
line-height: 100% !important;
}
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
color: blue !important;
}
h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
color: red !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
}
h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
color: purple !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
}
table td {
border-collapse:collapse;
}
.yshortcuts, .yshortcuts a, .yshortcuts a:link,.yshortcuts a:visited, .yshortcuts a:hover, .yshortcuts a span { color: black; text-decoration: none !important; border-bottom: none !important; background: none !important;} /* Body text color for the New Yahoo. This example sets the font of Yahoo's Shortcuts to black. */
</style>
</head>
<body>
<table cellpadding="0" cellspacing="0" border="0" id="backgroundTable">
<tr>
<td>
{$this->html}
</td>
</tr>
</table>
<!-- End of wrapper table -->
</body>
</html>
EOD;
$this->boiler = base64_encode($this->boiler);
return mail($this->to, $this->subject, $this->boiler, $this->headers, '-f ' . $this->from);
}
}
// MIT license for boilerplate:
/*
* MIT License http://htmlemailboilerplate.com/license.html
Copyright (c) 2010-2011 Sean Powell, The Engage Group
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
?>
<?php
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
header("Content-Security-Policy: default-src 'self'; script-src 'nonce-nonce'; upgrade-insecure-requests;");
ob_start();
function db() {
$db = new SQLite3('/var/www/storage/yt_notif_emails.db');
$db->busyTimeout(60*1000);
return $db;
}
$whitelisted_channels = array('Coyo');
$secret = '123456';
function channel_id($channel) {
global $whitelisted_channels;
return array_search($channel, $whitelisted_channels, true) || 0;
}
function unsub_id($secret, $email, $channel_id) {
return hash('sha256', $secret . $email . $channel_id);
}
echo <<<EOD
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Subscribe to community post email notifications</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="container">
<h1>YouTube Community Post Email Notifications</h1>
EOD;
if (isset($_POST['submitted'])) {
if (isset($_POST['email']) && strpos($_POST['email'], '@') !== false) {
$db = db();
$db->exec('CREATE TABLE IF NOT EXISTS emails (email TEXT, channel_id INTEGER, PRIMARY KEY (email, channel_id))');
$query = $db->prepare('INSERT INTO emails (email, channel_id) VALUES (?, ?)');
if ($query) {
$query->bindValue(1, $_POST['email'], SQLITE3_TEXT);
$query->bindValue(2, channel_id($_POST['channel']), SQLITE3_INTEGER);
$result = $query->execute();
if ($result) {
echo '<p>Your email has been registered.</p>';
} else {
echo '<p class="error">An error occurred, please try again later.</p>';
}
} else {
echo '<p class="error">An error with the DB occurred, please try again later.</p>';
}
} else {
echo '<p class="error">Please provide a valid email address.</p>';
}
} else if (isset($_GET['unsubscribe'])) {
if (isset($_GET['confirm'])) {
$db = db();
$query = 'SELECT email, channel_id FROM emails';
$results = $db->query($query);
$to_remove = '';
while ($row = $results->fetchArray()) {
$match = (unsub_id($secret, $row[0], $row[1]) == $_GET['unsubscribe']);
if ($match) {
$to_remove = $row[0];
break;
}
}
if ($to_remove != '') {
$query = $db->prepare('DELETE FROM emails WHERE email = ? AND channel_id = 0');
if ($query) {
$query->bindValue(1, $to_remove);
if ($query->execute()) {
echo '<p>You have been unsubscribed.</p>';
}
}
}
} else {
$full_url = htmlspecialchars("{$_SERVER['REQUEST_URI']}", ENT_QUOTES, 'UTF-8') . '&confirm=true';
echo <<<EOD
<script type="text/javascript" nonce="nonce">
window.location = '$full_url';
</script>
<noscript><p>Confirm your decision to unsubscribe by clicking <a href="$full_url">here</a>.</p></noscript>
EOD;
}
} else if (isset($_POST['notify']) && isset($_POST['content']) && isset($_POST['secret']) && $_POST['secret'] == $secret) {
include_once '../../../app/classes/EasyMail.php';
$dt = date('Y-m-d H:i:s');
$db = db();
$query = 'SELECT email FROM emails WHERE channel_id = 0';
$results = $db->query($query);
while ($row = $results->fetchArray()) {
$unsub_url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '?unsubscribe=' . unsub_id($secret, $row[0], 0);
$send = array();
$send['to'] = $row[0];
$send['from'] = 'noreply@thejach.com';
$send['subject'] = 'New YouTube Community Post From Coyo';
$content = trim($_POST['content']);
$send['html'] = <<<EOD
<div style="background-color: #f5e7d0; font-size: medium">
<div style="margin: 0px auto; min-width: 480px; max-width: 600px; background-color: #f0f0f0; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
<span style="padding: 5px;">Coyo has posted on her <a href="https://www.youtube.com/channel/UCabMjG8p6G5xLkPJgEoTnDg/community">YouTube Community Page</a>:</span>
<div style="background-color: #ffffff; font-weight: bold; padding: 5px;">$content
</div>
<span style="padding: 5px;">To unsubscribe from future emails, please click <a href="$unsub_url">here</a>.</span>
<span style="font-size: small;">Sent at $dt.</span>
</div>
</div>
EOD;
$mail = new EasyMail($send);
$mail->send();
}
} else {
echo <<<EOD
<a href="https://www.youtube.com/channel/UCabMjG8p6G5xLkPJgEoTnDg/community"><img src="/imgs/1hrNrfXi_400x400.jpg" class="coyo-av" alt="Coyo Avatar"/></a>
<p>This is an <em><strong>unofficial</strong></em> page for Coyodachi to subscribe to email notifications of Coyo's community tab posts.</p>
<p>これは、Coyoのコミュニティタブの投稿のメール通知を購読するための<em><strong>非公式</strong></em>ページです。[機械翻訳]</p>
<form action="index.php" method="post">
<div class="register">
<div class="hide">
<label for="channel" class="channel-label">Selected YouTube Channel</label>
<select id="channel" name="channel">
EOD;
foreach ($whitelisted_channels as $idx => $channel) {
echo ' <option value="' . $channel . '" ' . ($idx == 0 ? 'selected' : '') . ">$channel</option>\n";
}
echo <<<EOD
</select>
</div>
<input type="text" name="email" placeholder="Enter your email address / メール"/>
<input type="submit" value="Subscribe"/>
<input type="hidden" name="submitted" value="true"/>
</div>
</form>
<p>
Your email will only be used for these notifications and always includes a link to unsubscribe from further emails.
There are no delivery guarantees but if all is well you should get a notice of new posts within 5 minutes.</p>
<p>If you'd like to get browser push notifications instead of providing an email address, or would like this for other YouTube channels,
or have any other comments/complaints/feedback/notice of abuse (don't make me add a captcha or more),
go ahead and message me by whatever means (e.g. <a href="https://twitter.com/jachy">my twitter</a> or <a href="/about#contact">other means</a>) and it'll be considered.
Alternatively, if you want to try modifying and running the hacky scripts powering this yourself, I wrote up some info <a href="/">here</a>.</p>
EOD;
}
echo <<<EOD
</div>
</body>
</html>
EOD;
ob_end_flush();
?>
body {
font-family: Arial, sans-serif;
background-color: #f5e7d0; /* a soft, sandy brown */
}
.container {
max-width: 770px;
margin: 0 auto;
padding: 5px 50px 50px 50px;
text-align: center;
background-color: #f0f0f0; /* light grey */
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); /* subtle shadow for depth */
}
h1 {
color: #8b4513; /* a darker, coyote-esque brown */
}
.register {
margin-top: 30px;
}
select {
width: 70%;
padding: 10px;
border: 1px solid #8b4513;
margin-bottom: 20px;
}
input[type="submit"] {
width: 70%;
padding: 10px;
background-color: #8b4513;
color: #ffffff;
border: none;
cursor: pointer;
}
input[type="text"] {
width: 70%;
padding: 10px;
border: 1px solid #8b4513;
margin-bottom: 20px;
box-sizing: border-box;
}
input[type="submit"]:hover {
background-color: #a0522d; /* a darker hover effect */
}
.coyo-av {
width: 200px;
height: 200px;
display: block;
margin: 20px auto;
}
.channel-label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
.error {
color: red;
}
.hide {
position: absolute;
visibility: hidden;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment