-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Send notification emails when contributors are inactive #206
Conversation
2f260eb
to
9bffc73
Compare
9bffc73
to
7505baa
Compare
7505baa
to
8d6ea36
Compare
@@ -22,7 +23,7 @@ | |||
* | |||
* @return bool | |||
*/ | |||
function send_email( $to, $subject, $message, $pledge_id ) { | |||
function send_email( $to, $subject, $message, $pledge_id = false ) { | |||
$headers = array( | |||
'From: WordPress - Five for the Future <donotreply@wordpress.org>', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some mixed messages here - we ask people to reply but then have donotreply
as the From address. I realise the Reply-To will work, but some users might not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point. I was thinking it was used in order to match the Return-Path
, so that spam filters don't think we're forging From
. I just checked though, and the Return-Path
is bounce@wordpress.org
.
We could change it to that. It technically has the same meaning, but most folks won't be familiar with the term, so it's not as bad.
I'm leery of changing the From
to getinvolved@
though, because of those spam filters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I set it to bounce@
in 7cad4bd, but I'm open to other ideas if you think there's a better way.
$inactivity_threshold = strtotime( INACTIVITY_THRESHOLD_MONTHS . ' months ago' ); | ||
|
||
foreach ( $contributors as $index => $contributor ) { | ||
if ( $contributor['last_logged_in'] > $inactivity_threshold ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need some logic to handle a missing/invalid last_logged_in
value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should always exist at this point, it'll be 0
if nothing exists in the db.
$full_user['last_logged_in'] = intval( strtotime( $full_user['last_logged_in'] ?? '' ) ); // Convert `false` to `0`. |
* Notify an inactive contributor. | ||
*/ | ||
function notify_inactive_contributor( array $contributor ) : void { | ||
if ( ! Email\send_contributor_inactive_email( $contributor ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could there be a situation where sending fails repeatedly? If it's a permanent error like an invalid email address we'd effectively be silently retrying forever, if I'm reading correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 that's true.
It looks like account registration and editing both validate the address with Core's is_email()
. That's not RFC-compliant, but that might actually help us in this case, since IIRC there are lots of "valid" addresses that aren't routable. There's probably some edge cases though.
wp_mail()
also uses is_email()
to validate. So, I think an address will always either:
- be blocked by
wp_mail()
and therefore not trigger any spam filters that hurt our IP reputation; or - be sent to the address's mail server, and therefore get an updated
5ftf_last_inactivity_email
In the case of 1
, though, that could maybe eventually cause some users to never get the email. If enough edge cases clumped together inside a batch group, then they could get stuck at the front of it, causing the others at the end of the group to always be ignored.
five-for-the-future/plugins/wporg-5ftf/includes/contributor.php
Lines 596 to 599 in 8d6ea36
// Limit to 25 emails per cron run, to avoid triggering spam filters. | |
if ( count( $contributors ) > 25 ) { | |
$contributors = array_slice( $contributors, 0, 25 ); | |
} |
Adding a shuffle()
before slicing would avoid that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated 041abcf to include shuffle()
$field_names = array_flip( XProfile\FIELD_IDS ); | ||
|
||
foreach ( $user_xprofiles as $user ) { | ||
$user->user_id = absint( $user->user_id ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absint( $user->user_id )
makes me wonder what edge case this is intended for, and whether there's a better way to handle it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC it's just because the DB returns it as a string, but in other places we need to compare it to an int. We have to use strict comparison because the linter will complain if we don't. It also just makes me feel more comfortable, knowing that it's what I expect.
return trim( $team ); | ||
}, $contributor['teams_names'] ); | ||
|
||
$message = sprintf( " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a last-chance check to make sure all the variables are set? Just to guard against silly errors like "you pledged 0 hours" and other things that are likely to wind up in a screenshot on social media :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔
$name
falls back touser_nicename
, which should be guaranteed to exist$contributor['hours_per_week']
andteam_names
have checks to make sure they're valid
five-for-the-future/plugins/wporg-5ftf/includes/contributor.php
Lines 650 to 651 in 8d6ea36
$user->hours_per_week = absint( $user->hours_per_week ?? 0 ); | |
$user->team_names = (array) $user->team_names ?? array(); |
five-for-the-future/plugins/wporg-5ftf/includes/contributor.php
Lines 666 to 669 in 8d6ea36
if ( $xprofile->hours_per_week <= 0 || empty( $xprofile->team_names ) ) { | |
unset( $xprofiles[ $index ] ); | |
continue; | |
} |
It feels a bit WET/overkill to duplicate that in the email function, but I can see the concern.
Sometimes we do want folks to reply to messages, and the `donotreply` address contradicts that. `bounce@` has a similar meaning, but is less known. `bounce@` has the advantage of matching the `Return-Path`, which may reduce the chance of a message being flagged as spam for forging the `From` header. See #206 (comment)
8d6ea36
to
7cad4bd
Compare
Sometimes we do want folks to reply to messages, and the `donotreply` address contradicts that. `bounce@` has a similar meaning, but is less known. `bounce@` has the advantage of matching the `Return-Path`, which may reduce the chance of a message being flagged as spam for forging the `From` header. See #206 (comment)
7cad4bd
to
67db5a0
Compare
Sometimes we do want folks to reply to messages, and the `donotreply` address contradicts that. `bounce@` has a similar meaning, but is less known. `bounce@` has the advantage of matching the `Return-Path`, which may reduce the chance of a message being flagged as spam for forging the `From` header. See WordPress/five-for-the-future#206 (comment)
Fixes most of #27, but doesn't send an email to the sponsoring company. That can be a future iteration while @angelasjin is working on the details of that.
This sends the email to folks that haven't logged in in 3 months. Once #119 and #189 are done, we can update it to look at 5ftF contributions specifically.
It needs polishing, but I think it's close to a state that I'm happy with, and close enough to get some review while I finish the polishing.
I initially tried using more complex SQL queries to reduce the number of rows that would be fetched, and the amount of PHP code needed, but that ended up being more trouble than it was worth.
See #205 for a discussion about the email copy.