Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Critical Math

    Critical Math

    In this downtime of little new TV and a need to distract once in a while, I proposed we watch all of S1 Critical Role (which I really only half ass watched until Percy died and came back). So on Thursday at late Saturday nights we watch old-school Critical Role. Sometimes we sneak in another episode but basically we’ve been grinding through.

    The other day we got to Omens and had to pause while we argued about math.

    We’d hit a point where Matthew Mercer was rolling for an absent Ashley Johnson. She was the cleric and he rolled her healing spell which restores people for 4D8+5 hit points. Matt got a total of 20.

    “Oh,” says my wife, disappointed. “Below average.”

    I mused aloud, “I know the average of 2D6 is 7, but I forget the math.”

    This led to an old argument about the law of averages and statistics, which is to say there is a one in eight chance of rolling an eight (8) on any given roll of a 1D8. No matter how many times people say the dice are good or bad, you have the same chance every time. Flipping a coin 100 times will give you roughly 50 heads and 50 tails. Over time, the wiggliness of Xeno’s Paradox will keep the numbers from a true 50-50,but the point remains you will always have a 50% chance of tossing a heads or a tails.

    Related to this, though, are the odds of two heads in a row. The problem with the math is a combination of fairness and a concept known as “the Gambler’s Fallacy.” It’s also called “the Monte Carlo Fallacy” because of the time it happened at Monte Carlo. See if the odds of a heads is 1 in 2, then the odds of heads twice in a row is 1 in 4. Three in a row is 1 in 8, because you double it on down. Four in a row is 1/16, and five is 1/32, and this goes on.

    And this confuses people because even if the odds of flipping a coin to heads 20 times in a row is 1 in 1,048,576, this does not change that the odds of flipping heads for #21 is both one in 2 and 1 in 2,097,152. To bake your noodle a little more, if you’ve flipped a coin 20 times for heads, the odds of flipping the 21st to be tails is … also 2,097,152.

    This is because we’re looking at two different things here. The probability of a heads or a tails will always and forever be 50%. Period. The odds of rolling a Natural 20 on 1D20 will always be 1/20th (5% for those wondering). Those will never change. This is Bayes’ theorem in action.

    We all get screwed up about this because we believe that fairness (i.e. the permanent 50%-ness of the toss) means that previous failures (or successes) will change the probability of the next toss. It won’t.

    Let’s say we’re rolling 1D20 and it’s a fair roller. Our goal is to get a 20 and the probability of that is 5%.

    But what about the odds of rolling a 20 at least once in 20 rolls? Well that’s 64%.

    There’s a 95% chance of not rolling a 20 (or any other given number) but again that doesn’t change. Every roll you have a 95% chance of not rolling a 20 and a 5% chance of rolling a 1 or a 20 (unless you’re Wil Wheaton or Taliesin Jaffe). But this makes us ask another question. If there’s a 64% chance of a critical success (i.e. a 20) in 20 rolls of 1D20, does that change if my first roll is a non-20?

    Kind of. It’s 62%.

    Yes, it went down. Why? Because you have fewer chances to win! So the odds of a critical drop every time you roll a non-critical. And frankly this explains half of Wil Wheaton — he’s rolled so many non-critical, the odds are astronomical for him to actually roll one. On the other hand, his capacity for rolling 1s makes him the Joe DiMaggio of Failure for D&D. Sorry, Wil.

    This is exactly the same kind of argument my father made back in 2016 (see Hot Hands And Playoffs from 2016). And I came to the same conclusion. Wheaton and Jaffe are what we call outliers.

    An Aside: For anyone who’s confused, Wil Wheaton (yes Wesley Crusher from Star Trek, also a great writer, a gamer, and a generally amazing human) plays D&D. He has an incredibly capacity to fail his rolls, to the point that a term was coined: Wheatoning (see also the math on Wheatoning). Opposite this is Taliesin Jaffe (an actor, director, voice actor, an all around cool guy) who has a bizarre talent at successes. At least, he did as Percy in Series 1 of Critical Role, with a dice called “The Golden Snitch.” This die was later stolen. Long story. Taliesin still rolls crazy high, so most Critical Role fans see them as the opposite ends of the spectrum.

    Whew. Okay that was a lot of math to tell you that while people want to think “If I roll a non-20 10 times in a row, the odds go UP for my next roll to be a 20!” and the truth is that the odds go down. There is no universal mathematical law of fairness, just of averages.

    Back to the Critical Role episode in question. So Mercer rolled a 20 for 4D8+5. That’s pretty normal for Ashley Johnson (she has a tendency to roll below average, but not to the point that you think she’s cursed like poor Wheaton). At the time, I couldn’t remember the math, but I remembered the experiment, which was that the majority of the faces on a d6 add up to 7.

    You may be thinking “Mika, there are only three: 6 + 1, 5 + 2, 4 + 3.” and that’s true but it’s actually 6 because the inverses are true. But ask yourself how many possibilities are there with 2D6? There are 11. (2 through 12, you can’t roll a 1 on two dice). And 3 of the 11 are a seven so that’s 27% odds of a 7?

    No. It’s 16%. Again we’re talking about frequency versus probability. There are 11 possible outcomes, but there are 36 possible variants. You always have six chances to get a 7, one to get a 2, and one to get a 12. This means that to get the probability (i.e. the odds) of rolling a 7, you have to take the frequency of getting a 7 (again, that’s 6) and divide it by the total number of frequencies (36). And 6/36 is 16.67%.

    Here’s a table to help:

    Dice RollFrequencyProbability
    212.8%
    325.6%
    438.3%
    5411.1%
    6513.9%
    7616.7%
    8513.9%
    9411.1%
    1038.3%
    1125.6%
    1212.8%

    It’s annoying to think that you always have two sets of probabilities, but if it helps we’re talking about likelihood versus probability. The likelihood of rolling a 7 on 2D6 is 16.8% (rounding up). The probability is 1 in 11. With one die, it’s always a flat sameness (likelihood of rolling a 20 is 1 in 20, so is the probability).

    We’ve gone around a bit. I know. Here’s the fun stuff. Before arguing math stuff, my wife says “Oh just add up the opposite sides of a die.”

    In my defence I never looked.

    A quick check on this and we learned some really hilarious factoids:

    1. She’s right (12+1 = 13 and 2d12 is in fact average 13).
    2. This works because the math is “highest number on the die plus the lowest number equals the average of two dice of the same face.”
    3. On standard dice the 1 and the high number are opposite (6 and 1, 8 and 1, etc).
    4. All the opposite sides add up to the average.
    5. On a D10 this breaks because the 0 is opposite a 9, which is technically highest and lowest, but they add up to 9 (or 19, D10s are weird). The average is 11.
    6. One of my D8s is misprinted and has 8 opposite 7. It was the first one we checked..

    Of course the first die we checked happened to be the misprint. The purple die in the photo below is buck wild and all wrong.

    Two eight-sided dice. The off-white one (properly) shows a 5 below the 8, the purple shows a 3.

    This is the only die in that set (in fact, of all our regularly used dice) that has been misprinted. And while technically the change in weight (different numbers are different sizes and have a different amount of paint on each bit) means the die rolls wrong, it’s not statically significant for me to worry much. Dice have a standard set so that numbers can be trusted. Also it makes math easier for people. The average of 2D20 is *drumroll* 21.

    Let’s go back to Ashley Johnson. Mercer rolled a 20 on 4D8+5. Is that below average? Oh yes it is.

    The average of 4D8 is 18 because 2D8 = 8 + 1 – 9 times 2 is 18. Add 5 and the new average becomes 23. a 20 is 3 points under. But at least you can’t roll under a 9!

    Bonus Section

    The wonderful thing about Math is you can see parallels. Once you know 2D6 is 7, you can start to jump. 4D16 is 14 and so on. But… what does ‘6 + 1’ mean. How would you get there?

    Me I look for patterns. 6 is the maximum of one die. One is one-half the number of dice rolled. Then it stands to reason that 4D6 is going to be 6 plus something with 4, right? 6 plus 8 is 14, so is that our pattern? No because going back one means “6 plus 2 is 8” and that isn’t seven. So how did we get the one? One is one half the number of dice…

    DiceAverageMax + DiceMax + 1/2 Dice
    2D676 + 2 = 86 + 1 = 7
    4D6146 + 8 = 146 + 2 = 8
    6D6216 + 6 = 186 + 3 = 9

    None of those are right but … Do you see the patterns?

    First the average starts at 7 and you add 7 for every 2 dice you add.

    We need to add something to a six, but it has to factor in with the number of dice. So how do we get to that one again? We know that the only way to make a one out of the first example (2D6) is to divide the dice by 2. For 2D6 we’re adding 2, which means what plus 2 gets to 14? A 12. And what is 12 divided by 6? A 2…

    Hang on then. We have a possible pattern!

    ( maximum score of one die * half the dice rolled ) + ( half number of dice rolled )

    DiceAverage( Max * 1/2 Dice ) + ( 1/2 Dice)
    2D67( 6 * 1 ) + ( 2 / 2 ) = 6 + 1 = 7
    4D614( 6 * 2 ) + ( 4 / 2 ) = 12 + 2 = 14
    6D621( 6 * 3 ) + ( 6 / 2 ) = 18 + 3 = 21
    8D628( 6 * 4 ) + ( 8 / 2 ) = 24 + 4 = 28
    10D635( 6 * 5 ) + ( 10 / 2 ) = 30 + 5 = 35

    Bingo baby! We have our pattern! But does this work for odd numbers? It must for the formula to be correct…

    DiceAverageForumula
    1D67( 6 * .5 ) + ( 1 / 2 )
    3D610.5( 6 * 1.5 ) + ( 3 / 2 )
    5D617.5( 6 * 2.5 ) + ( 5 / 2 )
    7D624.5( 6 * 3.5 ) + ( 7 / 2 )
    9D631.5( 6 * 4.5 ) + ( 9 / 2 )

    Yep, the math keeps working!

    And this, friends, is how you reverse engineer a formula.

  • Bummer Of A Birthmark, Hal

    Bummer Of A Birthmark, Hal

    I gave a talk in 2019 at WordCamp NYC about what happens when you’re the target. Anyone in any form of a ‘leadership’ or visible role of authority in any community has had a bad day where they woke up and found out everyone hates them.

    Not that they’re actually doing anything wrong, but people are targeting them for perceived slights. Regardless of right or wrong, all anyone wants is for their phone to stop pinging, their email to calm down, those Facerange and Twooter groups to stop attacking, and maybe everyone could have a beer.

    I have absolutely been there before. For the last decade I’ve worked with the support forums and plugin review teams in myriad roles, including representing those teams to the community. I’ve had a lot of bad days. The good news is I’ve learned that are things you can do to protect yourself and to alleviate the problems.

    It Is/Isn’t Your Fault

    If you’ve been in any sort of leadership or front-facing role, you’ve probably gotten this at least once. Someone has a bad day, maybe they got banned, maybe they got fired, maybe they just failed on their own. Whatever the reason, it’s YOUR fault. They shouted at you, they screamed in person perhaps, and they left you shaking and a little scared about what the heck was going on and what do you do?

    Before I jump into how to protect yourself, which will be the majority of this talk, I want to stress something. No matter what, these situations are not ever entirely your fault. Any time something like this happens, it’s from a breakdown in communication, and that speaks to both sides.

    However. You do have to take some responsibility here for your own actions. If you don’t, you’ll find yourself here again and again, over and over, and that’s really stressful. So when these things happens, yes, reflect on what you did, but also keep in mind you didn’t do this alone.

    Regardless of fault, you have a right to protect yourself. This isn’t an inalienable right. This isn’t a law. This is my firm belief that you have a right to take measures to protect yourself from people who have gone crazy on you. It doesn’t matter if it’s your fault or not, it matters that you should protect yourself.

    What Happened?

    In order to understand how to protect yourself, you need to be aware of what you did. That’s why I said it’s your fault. You did, or you were perceived to have done, something. Keep a hold of that word, perceived, because it matters a great deal. If people think you did a thing, it has the same net effect on their actions, but drastically changes your emotions.

    More than once I’ve woken up to my Twitter mentions and emails filled with people losing their minds about how evil I am. In 2018 it was all about Gutenberg. To be clear, I was accused of deleting bad reviews on the Gutenberg plugin. Since I hadn’t been doing that, it took a lot of stress and reading to figure out why the mob was actually mad at me. In one case, it was a developer who tweeted, at-ing me, complaining it was unfair that Gutenberg had reviews removed, but he couldn’t get his one-star’s removed. That one tweet, for some reason, infuriated the masses and I had DMs and @-messages demanding I explain myself.

    I had to ask myself “Did I actually do this?” Did I actually delete reviews in a way that could cause this reaction? This was false and I knew it, because I had not deleted a single review about Gutenberg. However due to my history as a forum moderator, the finger was pointed at me. Here, what I had done was act as a moderator of some renown at some point in my past.

    Now that I knew what was going on and where it started and that I didn’t do anything, I had to uncover what actually happened. I’m still a forum admin, so I logged in and looked at the posts and I could see who had moderated what. And then I privately pinged those people and asked for details. In talking to the other moderators, I determined that the removal of Gutenberg reviews were valid. The 1-stars were made by sock puppets, which is to say fake accounts made by people to unethically alter a star rating. It happens a lot.

    Now What?

    Okay great, now what? Now it’s time to take action and decide what to do about these people. You have two options though. You can respond to them or … not. They both have a lot of pros and cons, but there is one universal truth you need to know going in: Whatever you chose, to reply or not, you will be wrong.

    There is absolutely no way to ‘win’ or even come out ahead here. You just can’t. If you reply, people will hate your answers. If you don’t, people will claim it’s proof. There’s no safe course here. So you need to make sure you understand why you’re doing this.

    Why You ReplyWhy You Don’t Reply
    Reply if you want to have your say in the matter. That’s it. It doesn’t matter if you’re right or wrong, or if you’re apologizing or not. You’re trying to have your chance to talk. By replying you’re opening up the doors for a discussion. Don’t pick this option if you don’t want to talk to people!Don’t reply if you know it’s a muggs game and you’ll just waste time arguing with people who’ve made up their minds about you. Not replying feels like a safer choice, except it eats at you so much. You’re going to hear people rip into you over and over, and you will have to stick to your guns and not reply.

    And if you’re still not decided, remember that sometimes you can’t reply. That usually happens when you’re aware of a bigger issue that’s preventing public disclosure, or you’ve signed an NDA, or your company asked you not to… Those are really hard because you absolutely cannot engage with people when this happens. You have to suck it up.

    There’s one middle road here. You apologize. This is really hard, though, because no matter how you do it, someone will grab on your word choices and use them as proof one way or the other. Usually it’ll be how they prove you’re terrible.

    It is a good rule in life never to apologize. The right sort of people do not want apologies, and the wrong sort take a mean advantage of them.

    P.G. Wodehouse, The Man Upstairs and Other Stories

    How to Apologize

    I have three rules for how to apologize. Those three rules have served me well, because it reminds me to level-set that no matter what I say, I’m not going to come out ‘ahead’, and I should expect nothing at all in return.

    1. Be respectful
    2. Be sincere
    3. Expect nothing

    There are some things you can be mindful of. Don’t use ‘if’ statements, like “I’m sorry IF this hurt you…” Take ownership of the consequences, regardless of your original intent. It doesn’t matter why a thing happened, it matters that you actually apologize for what happened. You can use “But”, just be mindful that it’s not for making an excuse.

    You still should consider an apology when you’re not the reason for the drama. However this gives you a little room, because now you can use those weasel works. “I’m sorry you feel this way.” Notice the feel part? That should normally be avoided. Here, we want to use it because it’s actually the only thing you can claim auspice over. You acknowledge their emotions as valid. Which they are.

    The follow up to that is you need send them to the right people. “I’m sorry you feel this way. You should talk to X about that. Here’s how…” This is not the equivalent of sending someone to your manager, you’re just getting them to the right people. Oh, but be a mensch and tell the other person what’s incoming.

    And remember: forgiveness is not the point

    I know this is hard to swallow. When you apologize, you never do it in order to be forgiven. Never. Ever.. If you are, then you’re going about it all wrong. You apologize because you hurt someone. It doesn’t matter if you meant to or not, and it doesn’t matter if you can fix it or not. It matters that someone is hurt, and you did it. It’s up to them to forgive you if they want to, but you owe them a sincere apology.

    And just so we’re clear, I’ve screwed this up too. Just as recently as last spring. It’s going to happen. No one is perfect. Try not to do it again.

    Practical Defense

    Now that you’ve done some ‘active’ things, you need to take the steps to protect yourself. These are hard because it starts with not looking at it.

    Don’t look at what they say about you. Its in our nature to want to know what people are saying about us, but I’m here to tell you not to look. Don’t look. Ignore the comments on other forums and blog posts. Walk away from what’s out there.

    If you do look, document. And there will be things that come at you regardless. You’re going to want to keep a record. I have a spreadsheet with the title and date of every single email someone sent regarding an altercation with Plugins. 300 emails a month, on average, for three months. It was painful to record, but I did it to have a history of his behavior. Which is still going on.

    Are you getting emails? Block them. Did they make a secondary account? Block that. Did they make 69 accounts over multiple email providers and rotate through the accounts to try and talk to you? By the way, yes, that happened. You block them all and you report them. You keep doing this.

    Put their emails in your comment blacklist. Don’t dismiss this. If you use Jetpack contact forms, you can use the blacklist to block them from that. IP block if you have to, though I don’t recommend that. Do what you can stop them from getting to you. If you can’t turn off comments (like I did here), then I recommend requiring all first-time comments be approved, and using the Comment Probation plugin.

    What about social media? If they’re ‘friends,’ I recommend you unfollow and possibly mute. There are people in WordPress whom I’ve muted, because we don’t get along and will argue about everything. It’s not worth it to fight, so I block and I mute very fast. This is for my own sanity because emotional attacks hurt worse.

    It someone calls you names, it hurts. If someone attacks your choices, it hurts. Well when someone continues to belabor a point, argue past the point of sense, and absorb hours of your time, they’re hurting you. You are allowed to ask them to stop and leave you alone. Of course, this doesn’t often work.

    The Warning Signs

    As many people will tell you, asking someone to stop, even a simple “I don’t want to continue this conversation here, please email X,” can result in unexpected explosions. This is an escalation in behaviour, as someone is demonstrating a distinct lack of respect for you, and human decency. Usually this is because they’re hurt too and lashing out, and it’s hard for people to look past that.

    Bear in mind, a threat doesn’t just mean “You better not walk down a dark alley alone” — and yes, someone said that once. Sometimes a threat is “I sent a package to your office.” Now, I bet nearly every non-male reading this just nodded. For those of you who didn’t, let me elaborate.

    When an online conversation crosses into the ‘physical world’ (for lack of a better term), it’s a major red flag. If you’ve been tweeting or emailing someone, and they send you, say, an apology letter, or email a photo of their company apologizing, you need to worry. This is because they’re attempting to play to your emotions.

    When they make that next step, though, claiming to send you flowers, that’s when you need to get a hold of authority figures and friends. Fast. I will warn you, if the person making the claim is out of state or out of country, it’s very hard to get legal help. You can, but it’s hard. If you work at a specific location, make sure they know. Make sure people you live with are aware. Anyone you think might be targeted, you need to warn.

    There are a number of micro-aggressions that indicate this behavior, from Sealioning to Gaslighting. But that’s a talk in and of itself. What you should hang on to here is that you need to trust your gut. Women, people of color, queers, any minority, we’re pretty in tune with that bad feeling that a conversation is going to go sour. Trust that. If someone turns to you and says “Hey, this person looks like they’re escalating,” then you should listen.

    Get Help

    I said it before, let me say it again. Give your teams a heads up. I had someone follow me all the way to my company, and we had to get legal involved because of threats expressed. I’ve even had to have a security officer on site for a WordCamp talk because someone went far enough that I felt concerned for my physical safety. These aren’t jokes. These are people who have lost the ability to see reason.

    You need to tell people in charge. If you’re afraid to tell your boss, you can try this with them or your HR rep or a trusted co-worker:

    I’m sorry to bring some personal issues into work, but there’s someone who has been harassing me, and I think they’re going to bring it into the workspace.

    No template is perfect or nuanced enough to handle all situations, and if you need help figuring out how to tell your employers, grab a trusted friend and ask for help.

    Beyond warning people you work with, get help. Ask for what you need, even if you know it’s the wrong person to ask. They may know who to talk to. I needed a new feature built into WordPress’ tool for plugin reviews to blacklist people so we stopped getting 30 emails in a day in our inbox. Speak up. Your teammates and friends should have your back. And if they don’t listen, go louder and over their heads as high as you need to. Go public if you have to.

    Practical Defense

    Even if you do all this, you have to keep in mind that once you are pointed at as ‘the bad guy’ people will go bonkers. They will be obsessed with every single thing you do. And this means you cannot bait them. Look, I love a good subtweet as much as anyone, but for the duration of this drama, you must not poke the bears. Don’t even drop a hint. While being harassed by said the aforementioned serial emailer (we’re up to 1000 emails now by the way), I complained about someone else, my cable company as it happened, but he took it to mean I was talking about him. It sucked.

    This is the scary thing, and the reason you’ve got to walk away from them. When they get obsessive, reading thousands of tweets deep or dredging up a forum post from before you were a moderator to prove a point, they’ve gone past sense and into obsession. This is terrifying. Which is why you’ve got to put your shields up.

    I want to point out the specific things you can do here. These are generally easy to do from a technical perspective, but not emotionally.

    Twitter

    First you de-friend. If they’re not a friend, you mute. If they escalate, you block. Some people you will jump right to a block because they’re just so wrong. But do it and walk away. The nice thing about a block and a mute is that it prevents you from reading their tweets at all.

    Turn off Twitter notifications for young accounts and people who don’t follow you. Use the quality filters. Disable DMs from people you don’t follow.

    If somerone attacks you or is vulgar, report the tweets and block them. Blocking an account you’ve reported will increase the chances that Twitter will actually do anything. Also ask your friends to report and block anything else they made public. It will help.

    Facebook & Instagram

    So I hate Facebook for a lot of reasons, and this is one. See, pretty much all you can do is build a wall. Facebook cares more about selling your personal information than protecting you from harassment. All you can do is lock your account away and block people. Report, yes, but if my wife’s death threat is any hint, they will do nothing.

    Still, I recommend you report content. You need to report the individual posts as well as the user account.

    Also curate the hell out of your friends. If you can’t remember why you friended them, it’s a good time to un-friend.

    Everything Else? You set your account private and block judiciously. You don’t have to worry about Google+ any more, but lordy, I promise that was a nightmare trying to block people. Snapchat is pretty ephemeral, things don’t stick around long, so it’s not an easy place to manage but still report and block.

    I have to mention this because we use Slack for WordPress.org work. And here, there is only one thing you can do when someone’s harassing you. You need to find an admin. Go into the Slack group and click “Customize Slack.” Then pick “About this workspace”. Click on the “Admins & Owners” tab. Ping one, explain the tl;dr and make sure you have logs of your harassment. Good luck.

    On a forum? Ask for moderator help. If this is an in-public ask, keep it simple. “I need a moderator. Someone is harassing me. Who can I speak to about this?” If you’re on WordPress.org’s forum, tap the ‘report topic’ button after you post and a Moderator will be alerted. Or come to the #forums slack channel and ask for help.

    I Hope You Never Have to Do This

    I really do. I hope none of you ever have to do this, and that your takeaway is “Gosh, I should make it easier for people to protect themselves on my systems!” And if you are going through this, protect yourself as best you can and remember, just because you’re the bad guy doesn’t mean the other person is a hero.

  • There Are No Top Influencers

    There Are No Top Influencers

    It’s that time of year where people post their ‘top X influencers’ for whatever they happen to be blogging about. It’s not a secret I hate those lists. In fact, I ask to be left off of them entirely.

    All Lists are Incomplete

    No matter what, no matter if you list 100 people, you’re going to leave someone out. This is a huge problem because those people will be hurt. The common complaint you hear is that a list cannot possibly list everyone, and that’s exactly the point. You know from the start you won’t have everyone listed, so you’re just going to pick the people you like best. And this is because…

    All Lists are Biased

    A couple years ago I saw a top-40 list that was 97.5% male. That means there was one woman on that list. Equally bad, there was only one non-white person on the list. They were not the same person, which meant this list left off someone who should have been terribly important since she led a major WordPress core release that very year. Leaving off hugely qualified people because of your unconscious (I hope) bias means you further work against the progress to be found with representation. And really that points to the next problem….

    All Lists are Personal

    If I was to list the biggest influences on, say, WordPress for me, I would include my father and my wife. To his dying day, my father emailed me a PDF and asked me to upload the content to his blog. My wife constantly asks me for help remembering the rare parts of WordPress. It’s that kind of experience that drives me. They influence me every day to make things easier for the non-technical. Another major influence are my co-editors on LezWatch.TV who ask me things that I feel should be obvious but clearly are not. Which means …

    All Lists are Pointless

    My mother is a huge influencer in my life. But you’re not going to get anything from following her. The developers I follow are ones who speak and talk in ways my brain has no problem following. The designers have taught me how to visualize (something I’m terrible at). The political wonks aren’t just an echo chamber, they’re thoughtful and educational. I follow a Sappho bot because I like her poetry. But none of that, not one thing, will help you get better at development or WordPress or anything really other than knowing I’m a human who likes a lot of weird stuff.

    We’re Solving the Wrong Problem

    What’s the point of these lists anyway?

    I can only come up with a couple reasons people make them:

    1. Currying favour with the people on the lists to make them feel important
    2. Lists are easier than actually writing a post with content

    That’s all I’ve got. And that brings me to my point.

    No One is a “Top Influencer Anyway”

    The person who influences WordPress the most is probably someone you never noticed.

    People tell me I should be listed and I point out that my ‘influence’ is not seen by the majority of people who use WordPress. They never see a plugin review or the work we put into making things safe and stable for them. And that? That is as it should be! How many users can name the release leads? Those names don’t matter to them, and they shouldn’t.

    Dad never cared if Nacin or Helen or Mel or Matt lead a release. He didn’t even care that I know them. He cared that WordPress worked and did what he needed.

    Isn’t that what we all care about? Not the personal aggrandizement of a few select individuals, but of the collective success of the WordPress project.

    Make Lists Matter

    If you want a list that matters, make a list of the best talks/blog posts/event-things you experienced in a year and explain how they influenced you. Tell people about what you learned and how you use it. Explain why things matter.

    But lists?

    Come on, we can do better.

  • Shut Your Pi-hole

    Shut Your Pi-hole

    One of the things that bothers me about the internet is tracking and ads. Mostly I hate trying to read things like IMDB and having it take a minute (60 to 90 seconds) to load, because of ads.

    Now I have nothing against ads! I use them on my sites to limited success. I click on them on Instagram from time to time. But the way many sites use ads and tracking is a little obscene to me. It makes it impossible to browse the net. And worse, the add-ons for our browsers don’t always work.

    Enter the Pi-hole

    I blame my friend Jan, who loves Raspberry Pi’s, for the thought. A Raspberry Pi is a mini computer. It’s the bare bones you need to computer. If you think about how small something like an Amazon Fire Stick is, you have the idea. They’re itsty. But they work. And they work because they have a stripped down operating system that does very little but the basics.

    On top of that, we can add software like Pi-hole. A Pi-hole is custom software that watches your internet traffic and blocks ads before they get to your computer. It works by intercepting DNS traffic and checking it against blacklists. If something, like an ad network, is on a blacklist, it blocks.

    Finding Your Pi

    In order to build this you need a Pi. I bought a kit. I don’t want to hear about it. I don’t need more, I didn’t want more. I wanted, and I got, the basic kit that had what I needed. I bought a Vilros Pi Zero W, which means it’s the smallest, least extendable Pi out there.

    The kit came with the following:

    • A Pi circuit board
    • A case (with three lids)
    • Rubber feet for the case
    • A heat sink
    • A camera connector (but no camera)
    • A 2×20 pin set
    • Adapters for HDMI and power and USB

    In addition I bought an Ethernet connector because I didn’t want to use WiFi, and a mini SD card.

    Putting it together is like LEGO’s, however there were NO directions. Did I need the pins? Did I need the camera? In the end, I attached the heat sink and left it at that. The pins only matter if I’m soldering things and I don’t want a camera.

    Installing the OS

    The tricky part here is normally people say “connect a monitor and keyboard…” but I didn’t want to do that. I don’t have a spare monitor (though I could use my TV and briefly did for a debug) and I don’t have a spare keyboard. All of this I did on my MacBook.

    Go to the official page and download Raspbian LITE. This is the minimalistic version of the official operating system. Download the zip and open it. You’ll get an IMG file. Hold on to it. You also want to download an image builder. I used Balena Etcher, which is a free and open source tool to flash an SD card with an operating system. Or in layman’s terms, it installs that image you downloaded onto the card and magically makes it work.

    Insert your SD card and run Balena. Tell it to install your image to the SD card. This takes about 5 minutes, depending on your systems. Once it’s done, you will need to eject the card and then reinsert it. This is because the MacOS can be tetchy. Once the disk is mounted on your Mac, you need to add an empty file called SSH to the main folder. This will allow you to SSH into the Pi when you’re done.

    Plug It In

    Okay! Now we go back to the hardware. Eject your disk from your Mac and insert it into your Pi. Put the Pi into the case and click it into place. Make sure the various ports line up with the holes. Once that’s in, put the lid on and attach the HDMI adapter (just in case), the Ethernet Adapter (and plug that cable in) and the power.

    Your Pi will boot in a matter of seconds. Go get a glass of water. Come back. Now we’re ready to go!

    Update All the Things

    Before we install Pi-hole, we want to upgrade everything.

    To do that we need to know our Pi’s IP. I went to my router’s web interface and scanned the list of devices for raspberrypi. It’s under the DHCP allocation table. Get the IP address of your Pi. With that address, SSH into your Pi.

    ssh pi@123.45.67.89
    

    The default password is raspberry.

    You will be shown raspi-config on first logging in. Press the number 2 and change the password to something better. If you want to run that screen later, you’ll need to run sudo raspi-config

    Now we update! To update everything, run sudo apt-get update — this downloads all the things you need. Next you run the upgrader – sudo apt-get dist-upgrade – to install it all. If this fails, it will have advice on how to proceed. Read carefully!

    We also want to install Git tools: sudo apt-get install git net-tools

    And you may want to change your timezone: sudo dpkg-reconfigure tzdata

    Install Pi-hole

    You can one-line this, but I prefer to use git:

    $ git clone --depth 1 https://github.com/pi-hole/pi-hole.git pi-hole
    $ sudo bash pi-hole/automated\ install/basic-install.sh
    

    This will take you through the installer. You’ll have some options to pick from:

    • Interface — I’m using eth0, which means wired Ethernet and not WiFi.
    • DNS — I picked OpenDNS but for most people Google is fine and reliable.
    • Blacklists — Accept the defaults.
    • Protocols — Most people will use IPv4. You’ll know if you use IPv6.
    • IP Address/Gateway – The system does a good job getting the IP of your Pi. The gateway needs to be the IP of your existing router.
    • Web Admin — Yes, we want the web admin.
    • Web Server – You want this if you picked yes for Web Admin.

    Whew. That was a lot, right? And the install takes a bit (don’t panic if it stalls). You’ll get your password for the web admin in this process, so please make a note of it.

    Configure Your Pi-hole

    At this point, you can visit http://pi.hole/admin to get to your admin page. If that doesn’t work, try http://123.45.67.89/admin (changing that to your Pi’s IP) and that will get you in. It does not log you in. You can log in, and you’ll want to because we have to do some extra configuration.

    Also it looks really cool:

    The pi-hole admin screen.

    If you’re lucky enough, your router lets you change the DNS servers. And if that’s the case, just change it to the IP of your Pi-hole. Done and done. However. If you’re like me and use the router that came from your ISP, you may be surprised to find out they don’t trust you in the slightest and you cannot change the DNS settings.

    After you’re done swearing at your ISP, and you’ve decided you don’t feel like shelling out a couple hundred for a router that may or may not work, it’s time to play magic with DHCP.

    DHCP (Dynamic Host Configuration Protocol) is the service that lets your router give all the devices on your local network (LAN) an internal IP address. It also handles all the traffic from your device to the rest of the Internet.

    1. Log in to your router and disable DHCP
    2. Log in to Pi-hole admin and enable DHCP
    3. Make sure the Router Gateway address is the correct IP for your router (if it’s not, nothing will work)

    If you want to switch everyone over right away, reboot your router. If not, just wait for everyone to pick up the new service.

    How Have You Pi’d Your Hole?

    What extra tricks have you spun up? Do you have a perfect blacklist? Do you use it for a VPN as well? Did you figure out how to beat the AT&T modems into submission?

    FYI. Do not reply to this post with “You should use X hardware instead” or even “You need own your own router.” Telling people they’re wrong about choices they made when the choices are perfectly valid is an 🍆 move. Be helpful and lift up. Don’t gatekeep.

    For the people who still have questions, here are some useful posts:

  • Saving Theme Data to a File

    Saving Theme Data to a File

    Your life isn’t all WordPress.

    I know, I know, I said a dirty thing, but let’s be honest, everything isn’t always all WordPress. And when that happens, you have to do some weird things to make your data shared.

    One of the things I needed one day was a way for non-WordPress files to get access to a theme setting. See, the theme let me set a top-bar and customize it. Sometimes I did that to share news, sometimes to link to latest posts. Regardless, I updated it via Customizer, and it worked great for WordPress.

    Not so much for my static HTML site, or my non-WordPress PHP site.

    I dwelled on it for a while and then thought “Wait, if the theme knows how to echo a specific setting, then there has to be a function for that. And if there’s a function, then can’t I hook into Customizer saving to trigger the creation of an HTML file with the data and call that from my other sites?”

    And guess what? You can!

    The WordPress Code

    Toss this in an MU Plugin and it’ll save a file to wp-content/top-bar.html when you save customizer settings.

    <?php
    class HELF_Top_Bar {
    
    
    	public function __construct() {
    		add_action( 'customize_save_after', array( $this, 'save_top_file' ) );
    	}
    
    
    	/**
    	 * Safe the top file
    	 * When customizer is saved, copy the get_theme_mod() data and parse it
    	 * into a file: wp-content/top-bar.html
    	 * @return n/a
    	 */
    	public function save_top_file() {
    		$default     = 'Something Default Here';
    		$banner_text = get_theme_mod( 'top-banner-text', $default );
    
    		$fp = fopen( WP_CONTENT_DIR . '/top-bar.html', 'w' );
    		fwrite( $fp, $banner_text );
    		fclose( $fp );
    
    	}
    
    }
    
    new HELF_Top_Bar();
    
    

    The Javascript

    The what?

    For my other PHP file, it’s a simple file_get_contents() call to the HTML. But for static HTML I had to resort to trickery with Javascript.

    <script>
    	var elem = document.getElementById("wpcontent");
    
    fetch('https://example.com/wp-content/top-bar.html', {
            credentials: 'include'
        })
            .then(function (response) {
                return response.text();
            })
            .then(function (text) {
                console.log('Request successful', text.length);
    			console.log('Request successful2', text);
    			$('.wpcontent').html(text);
            })
            .catch(function (error) {
                console.log('Request failed', error)
            });
    </script>
    
    <div class="wpcontent"></div>
    
    

    And magic happens.

  • Rolling Your Own Related Posts

    Rolling Your Own Related Posts

    To start out at the top, I did not write a whole ‘related posts’ plugin. As with all things, I started by asking myself “What’s the problem I’m trying to solve?”

    The answer is “I have a custom post type that needs to relate to other posts in the type, but based on my specific criteria which is currently organized into custom taxonomies and post meta.” And from the outset, that certainly sounds like a massive custom job. it was one I was dreading until I remembered that a developer I respected and trusted had once complained to me about the problems with all those other auto-related-posts plugins.

    1. They’re heavy and use a lot of ram
    2. They don’t let you customize ‘weight’ of relations
    3. They’re not extendable

    So I did the next logical thing and I looked up their plugins.

    The Plugin

    The crux of why I chose this plugin was simply that it’s extendable, but also that it started out with what I had:

    Posts with the most terms in common will display at the top!

    Perfect!

    Design The Basics

    Before you jump into coding, you need to know what you’re doing. I chose to isolate what I needed first. I made a list of everything I thought was relative:

    • Taxonomies: Tropes, Genres, Intersectionality, Tags, Stars
    • Post Meta: Worth It, Loved, Calculated Score

    Yes, it’s that site again.

    I read the plugin documentation and verified that for most of that I just needed to list the taxonomies in the shortcode like this:

    [related_posts_by_tax fields="ids" order="RAND" title="" format="thumbnails" image_size="postloop-img" link_caption="true" posts_per_page="6" columns="0" post_class="similar-shows" taxonomies="lez_tropes,lez_genres,lez_stars,lez_intersections,lez_showtagged"]
    

    Initially I didn’t list the stars because the way the code works, it would say “If you have a Gold Star, show other Gold Stars.” And that wasn’t what I wanted to see. I wanted “If you have ANY star, show other shows with a star.” That said, once we got over 12 shows in each ‘star’ category, this became much easier to match and I could add it in.

    The rest of the code, those checks for meta, needed actual code written.

    Meta Checks

    There’s a helpful filter, related_posts_by_taxonomy_posts_meta_query, that lets you filter the meta queries used by the post. Leveraging that, we can make our checks:

    1. Match the ‘worth it’ value of a show
    2. If the show is loved, list other loved show
    3. If the show isn’t loved, use the score to find show with the same relative value

    Both Worth It and Loved are post meta values. Mine happen to be added by CMB2, but the logic remains the same regardless how you add it. Worth It has four possible values (Yes, No, Maybe, TBD), and the check is either the value or false. Loved is a checkbox, a boolean exists or not, which means it’s a true/falsy. The score is a number that’s generated every time the show is saved, and it’s crazy complicated and another story.

    The code I use looks like this:

    add_filter( 'related_posts_by_taxonomy_posts_meta_query', 'MYSITE_RPBT_meta_query', 10, 4 );
    function MYSITE_RPBT_meta_query( $meta_query, $post_id, $taxonomies, $args ) {
    	$worthit = ( get_post_meta( $post_id, 'lezshows_worthit_rating', true ) ) ? get_post_meta( $post_id, 'lezshows_worthit_rating', true ) : false;
    	$loved   = ( get_post_meta( $post_id, 'lezshows_worthit_show_we_love', true ) ) ? true : false;
    	$score   = ( get_post_meta( $post_id, 'lezshows_the_score', true ) ) ? get_post_meta( $post_id, 'lezshows_the_score', true ) : 10;
    
    	// We should match up the worth-it value as well as the score.
    	// After all, some low scores have a thumbs up.
    	if ( false !== $worthit ) {
    		$meta_query[] = array(
    			'key'     => 'lezshows_worthit_rating',
    			'compare' => $worthit,
    		);
    	}
    
    	// If the show is loved, we want to include it here.
    	if ( $loved ) {
    		$meta_query[] = array(
    			'key'     => 'lezshows_worthit_show_we_love',
    			'compare' => 'EXISTS',
    		);
    	}
    
    	// If they're NOT loved, we use the scores for a value.
    	if ( ! $loved ) {
    		// Score: If the score is similar +/- 10
    		if ( $score >= 90 ) {
    			$score_range = array( 80, 100 );
    		} elseif ( $score <= 10 ) {
    			$score_range = array( 10, 30 );
    		} else {
    			$score_range = array( ( $score - 10 ), ( $score + 10 ) );
    		}
    		$meta_query[] = array(
    			'key'     => 'lezshows_the_score',
    			'value'   => $score_range,
    			'type'    => 'numeric',
    			'compare' => 'BETWEEN',
    		);
    	}
    
    	return $meta_query;
    }
    

    More Similar

    But there’s one more thing we wanted to include. When I built this out, Tracy said “There should be a way for us to pick the shows we think are similar!”

    She’s right! I built in a CMB2 repeatable field where you can pick shows from a dropdown and that saves the show post IDs as an array. That was the easy part, since we were already doing that in another place.

    Once that list exists, we grab the handpicked list, break it out into a simple array, check if the post is published and not already on the list, and combine it all:

    add_filter( 'related_posts_by_taxonomy', array( $this, 'alter_results' ), 10, 4 );
    function alter_results( $results, $post_id, $taxonomies, $args ) {
    	$add_results = array();
    
    	if ( ! empty( $results ) && empty( $args['fields'] ) ) {
    		$results = wp_list_pluck( $results, 'ID' );
    	}
    
    	$handpicked  = ( get_post_meta( $post_id, 'lezshows_similar_shows', true ) ) ? wp_parse_id_list( get_post_meta( $post_id, 'lezshows_similar_shows', true ) ) : array();
    	$reciprocity = self::reciprocity( $post_id );
    	$combo_list  = array_merge( $handpicked, $reciprocity );
    
    	if ( ! empty( $combo_list ) ) {
    		foreach ( $combo_list as $a_show ) {
    			//phpcs:ignore WordPress.PHP.StrictInArray
    			if ( 'published' == get_post_status( $a_show ) && ! in_array( $a_show, $results ) && ! in_array( $a_show, $add_results ) ) {
    				$add_results[] = $a_show;
    			}
    		}
    	}
    
    	$results = $add_results + $results;
    
    	return $results;
    }
    

    But … you may notice $reciprocity and wonder what that is.

    Well, in a perfect world if you added The Good Fight as a show similar to The Good Wife, you’d also go back and add The Good Wife to The Good Fight. The reality is humans are lazy. There were two ways to solve this reciprocity of likes issues.

    1. When a show is added as similar to a show, the code auto-adds it to the other show
    2. When the results are generated, the code checks if any other show likes the current show and adds it

    Since we’re already having saving speed issues (there’s a lot of back processing going on with the scores) and I’ve integrated caching, it was easier to pick option 2.

    function reciprocity( $post_id ) {
    	if ( ! isset( $post_id ) || 'post_type_shows' !== get_post_type( $post_id ) ) {
    		return;
    	}
    
    	$reciprocity      = array();
    	$reciprocity_loop = new WP_Query(
    		array(
    			'post_type'              => 'post_type_shows',
    			'post_status'            => array( 'publish' ),
    			'orderby'                => 'title',
    			'order'                  => 'ASC',
    			'posts_per_page'         => '100',
    			'no_found_rows'          => true,
    			'update_post_term_cache' => true,
    			'meta_query'             => array(
    				array(
    					'key'     => 'lezshows_similar_shows',
    					'value'   => $post_id,
    					'compare' => 'LIKE',
    				),
    			),
    		)
    	);
    
    	if ( $reciprocity_loop->have_posts() ) {
    		while ( $reciprocity_loop->have_posts() ) {
    			$reciprocity_loop->the_post();
    			$this_show_id = get_the_ID();
    			$shows_array  = get_post_meta( $this_show_id, 'lezshows_similar_shows', true );
    
    			if ( 'publish' === get_post_status( $this_show_id ) && isset( $shows_array ) && ! empty( $shows_array ) ) {
    				foreach ( $shows_array as $related_show ) {
    					if ( $related_show == $post_id ) {
    						$reciprocity[] = $this_show_id;
    					}
    				}
    			}
    		}
    		wp_reset_query();
    		$reciprocity = wp_parse_id_list( $reciprocity );
    	}
    
    	return $reciprocity;
    }
    

    There’s a little looseness with the checks, and because there are some cases were shows show up wrong because of the ids (ex: show 311 and 3112 would both be positive for a check on 311), we have to double up on the checks to make sure that the show is really the same.

    What’s Next?

    There are still some places I could adjust this. Like if I use more filters I can make the show stars worth ‘more’ than the genres and so on. And right now, due to the way most Anime are based on Manga (and thus get flagged as “Literary Inspired”), anything based on Sherlock Holmes ends up with a lot of recommended Anime.

    Still, this gives me a way more flexible way to list what’s similar.