I help out as a community advisor on a XenForo forum, and since the owner of the forum is also a friend of mine, I sometimes end up looking at things a little more closely than a regular member would. That is how I came across a moderation problem that was easy to miss if you were not looking for it. A member had quoted another person’s post (me), but inside that quote was a link that had never appeared in the original post. And worst of all, it was a spammy link promoting a spammy SEO company!
The quote on the forum showed my name as the originator of the quoted text, so to anyone reading the thread, it looked pretty legit. The problem was that the URL where I was being quoted in had never been posted by me. It had been added later by the new person replying.
That creates a pretty obvious moderation problem. Most people reading a forum assume quoted content actually came from the person being quoted. If someone inserts a URL into that quote, it can make it look like the original member recommended, endorsed, or posted a website they had nothing to do with.
So, the first question was obvious: how do we find out whether this happened anywhere else? And that was quickly followed up with: how do we keep moderators from having to manually check every quote forever?
That is how the BRS Quote Integrity add-on for XenForo 2.3 was created. If you run a XenForo forum, the add-on is free to use. You can review the source on GitHub or download the latest release.
TL;DR – What We Built
- We built a XenForo 2.3 add-on that compares URLs inside quoted content against the original source post.
- Moderators only see a warning when a URL appears in the quote that was not in the source.
- The add-on checks new and edited posts and can also scan older forum content.
- AI helped with development, troubleshooting, code review, documentation, and working through different approaches.
- The code still had to be tested, questioned, and changed when the first implementation did not fully account for how moderators would actually use it.
The Problem Wasn’t That Someone Edited a Quote
XenForo uses BBCode for quoted content and allows users to edit quoted text before submitting a reply. I’ve used that plenty of times before. Sometimes instead of quoting an entire thread, maybe I only need a few sentences. In those cases, I’ll edit the quote and keep the part that makes my case. Some people edit the quotes to shorten them; others remove images or add bold to a section that is being questioned. It’s all fairly normal.
Trying to detect every difference between the original post and the quote would create a moderation nightmare. You would end up flagging normal behavior all day long, and moderators would quickly stop paying attention to the warnings. So we deliberately did not build a general quote comparison tool.
We narrowed the problem down to something much more specific: does a quote contain a URL that does not exist in the original source post XenForo says was quoted?
If the link was in the original post, nothing happens. If it was not, the moderator gets a warning on the screen, in the post directly. No searching logs or other dashboards.
This became one of the basic principles behind the add-on: show nothing unless there is a problem. There’s nothing worse than a tool used for moderation that flags everything and creates a bunch of noise. Those aren’t useful tools, even if what they are saying is technically accurate.
How the XenForo Add-On Works
When a user quotes another message on XenForo, the quoted text stores a link that identifies the original post. It’s not stored in the database; it’s stored in the actual forum post itself. That gives us a solid starting point to work from because we are not trying to guess where the quote came from.
When someone submits or edits a post, BRS Quote Integrity looks for quote blocks that also contain URL-like content. If it finds one, the add-on gets the original XenForo post ID, loads that source post, extracts the URLs from both versions, normalizes them, and compares the results.
If the URL was already present in the source post, the quote is left alone. If a URL exists in the quoted version but not in the source, moderators receive an inline warning, and the finding is recorded in the XenForo Admin Control Panel.

That sounds relatively straightforward when it is condensed into a few paragraphs. The fun started when we moved away from clean test data and started thinking about what actual forum posts look like.
Quotes can be nested. Users can manually alter BBCode. URLs can be written in several different formats. Tracking parameters can make two versions of the same address look different even when they ultimately point to the same place. Older posts may also have been edited after somebody originally quoted them.
Real-world data has a habit of making the simple version of a programming problem disappear pretty quickly.
AI Helped Build This, But It Didn’t Get to Make the Decisions
I use AI for development work, and I do not see much point in pretending otherwise. It can be extremely useful when I am working through an unfamiliar API, reviewing a class, brainstorming different approaches, writing repetitive code, tracing an error, or trying to figure out why something that looks correct refuses to behave as expected.
AI has helped us move this project along much faster than we would have without it.
The problem starts when people confuse “AI helped write the code” with “AI decided what the software should do.” Those are not the same thing. AI should also be a “helper,” not a “senior programmer.”
AI is very good at producing something that looks finished. The PHP looks right, the methods have sensible names, the database query works, and the result appears where you expected it. That can make it very easy to stop reviewing too early.
But software is not always valid and correct just because it runs.
Detecting the Problem Wasn’t Enough
The first version of the findings system was focused on detection. If it found a suspicious URL, it recorded the finding. From a programming standpoint, that made perfect sense because the add-on was doing exactly what we had asked it to do.
Then we started thinking about what happens after a moderator sees the warning.
Imagine the moderator contacts the member and the member edits the post to remove the inserted link, or the moderator removes the link themselves. The original detection was valid, but the problem is no longer there. If the software only knows how to insert findings into a database, that warning can effectively remain actionable forever unless somebody manually removes it.
That means the software is technically remembering something that really happened while simultaneously showing the moderator information that is no longer current. We needed a solution to handle this situation.
The answer was not to delete the history either, because there may still be value in knowing that the issue occurred, who generated it, and whether it’s a recurring trend for the user. Other actions can be taken on that user at that point. So instead of leaving potential flagged posts, we changed the way findings work.
BRS Quote Integrity now uses states such as Open, Ignored, and Resolved. Open means the issue currently exists. Ignored means an administrator reviewed it and intentionally decided no action was needed. Resolved means the condition existed previously, but a later analysis confirmed that it is no longer present. In most cases, the resolved flag wouldn’t be used, as once the issue is corrected, it’s removed from the list.

So now, not only are we detecting issues, but the add-on can also reconcile findings instead of simply adding new ones. If the suspicious URL disappears after a post is edited, the finding can automatically become resolved. If the same condition comes back later, it will reopen. If an administrator deliberately ignores a legitimate exception, a future scan respects that decision rather than recreating the same problem over and over again.
That is a much better system, but it only became obvious once we stopped asking, “Does the code detect the URL?” and started asking, “How will this behave after people actually use it?”
We see the same thing with AI and WordPress commenting on SEO. AI might give you a technically valid noindex directive, redirect rule, schema block, PHP snippet, or database query. The syntax can be perfect, and the recommendation can still be completely wrong for that website.
Context matters.
Why We Didn’t Use One Big Regular Expression
Analyzing the quote structure created another interesting problem. The obvious answer would have been to build a single regular expression to identify the quote blocks. AI can produce some very impressive-looking regular expressions, and they are often perfectly fine when the data is predictable. But forum posts are not always predictable.
Quotes can be nested inside other quotes. Somebody can manually edit the BBCode. An opening and closing tag may not be included the way you expect. The more you try to cram all of that into one pattern, the more difficult the code becomes to understand and maintain.
The finished analyzer uses a stack-based approach to track quote boundaries rather than relying on a single recursive regular expression to handle everything.
The easiest comparison is matching parentheses. If you open one set of parentheses and then open another set inside it, you need to keep track of both before you know which closing parenthesis belongs to which opening one. Nested forum quotes create a very similar problem.
What we ended up with was not necessarily the shortest approach, but it was the one we trusted the most once real forum content was reviewed.
Then We Needed to Check the Old Posts
Catching future posts only solved half the problem, because we built the add-on in the first place after already finding one example. Once you find one, the uncomfortable question is whether that was an isolated incident or whether someone had been doing it for weeks, months or even years.
The forum we were working with was from 2000 and had hundreds of thousands of posts. Manually reviewing those quotes was never going to happen. So we built a historical scanner.

The admin or moderators can scan by username, date range, forum category, or a combination of those filters. That was intentional because there is no reason to hit the entire database on the first run if you already have a specific user you are investigating.
Start with the known user. See what comes back. If the results suggest a pattern, widen the search.
With hundreds of thousands of posts, we needed to ensure we didn’t crash the script by timing out the PHP process. So we process the posts in batches as needed. The timeout wasn’t an issue on our development stack, but once we moved to the real-world environment, we saw potential for a problem.
Historical scanning has an important limitation. The scanner compares an old quote against the current stored version of its source post. If the source post itself was edited years after the quote was created, the current version may not perfectly represent what existed at the exact moment the quote was made.
We also found that since this forum has been moved between different forum software, the post IDs are also different today than they were in 2000. This is an ongoing issue that needs to be updated either manually in the forum or ignored in the admin scan. We can only report on the data that the system is given.
False Positives Matter More Than Fancy Features
Any detection system is only useful if the people using it trust the warnings. If moderators see ten false alarms for every real problem, they will eventually stop paying attention, and at that point the add-on may as well not exist.
URLs make this harder because the same destination can appear slightly differently. Tracking parameters are a good example. One URL version may contain analytics parameters, while another does not, even though both ultimately lead to the same page.
The analyzer normalizes URLs and removes common campaign tracking parameters or affiliate codes before comparing them.
There are still edge cases, and we document those instead of pretending they do not exist. Shortened URLs can create questions. Redirected URLs can create questions. This is not the end of the add-on; it still needs tweaks, but only when we run into situations that require them.
How This Add-on Actually Started
BRS Quote Integrity is a XenForo add-on, but the part of the project I found more interesting was how it started.
Nobody asked me to build a huge moderation platform. I found something strange, figured out what was actually happening, looked at how XenForo stored the data, narrowed the problem down to the part that mattered, and then wrote something to detect it. I then contacted the forum owner and said: “Hey, here’s what I’ve done; do you want to test it out?” There wasn’t much for him to lose, so he ran it, and it highlighted several posts where this had happened. It’s now cleared up a spam problem that turned out to be bigger than the one post I noticed.
Once that first version worked, testing exposed the next set of questions. What happens when the offending post is corrected? What happens with nested quotes? How do we scan years’ worth of old posts without putting an unnecessary load on the server?
Every answer exposed yet another question.
The Finished BRS Quote Integrity Add-On
The current XenForo 2.3 add-on includes live checking for new and edited posts, moderator-only warnings, a findings area in the Admin Control Panel, Open, Ignored, and Resolved states, automatic reconciliation after edits, and an on-demand historical scanner that can be filtered by user, date, and forum.

The source code is public on GitHub: https://github.com/bigredseo/brs-quote-integrity
That was deliberate too. If I am going to talk about reviewing AI-assisted code rather than blindly trusting it, then hiding our own code would be a little ironic. Anyone interested in the technical side can review the source code, read the documentation, browse the release history, or download the current version.
AI Is a Tool. Treat It Like One.
I am not in the “AI is going to destroy programming” camp, and I am definitely not in the “AI can build everything now, so programmers are unnecessary” camp either.
AI gives developers another tool, and it is a very powerful one. It can save a huge amount of time on research, repetitive code, documentation, troubleshooting, and exploring different approaches.
But somebody still needs to understand what the code is doing. Somebody needs to know when a proposed solution does not fit the platform. Somebody has to ask what happens after the first successful demo. Somebody still needs to test the weird forum post with nested quotes, manually edited BBCode, and a URL covered in tracking parameters.
And sometimes somebody has to look at the output and say, “No, we are not doing it that way.”
That is still programming. The tools have changed, but the responsibility has not.
Have a WordPress Problem That Doesn’t Have an Obvious Answer?
Most of our work at Big Red SEO is WordPress web design, technical SEO, and troubleshooting WordPress websites. The XenForo project was a little outside our normal lane, but the process was exactly the same one we use when a WordPress site starts doing something it shouldn’t.
First, we figure out what is actually happening. Then we trace the cause, test the assumptions, and build or fix what is needed instead of throwing random plugins and code at the problem.
Sometimes that means fixing a plugin conflict. Sometimes it is custom PHP, JavaScript, or database work. Sometimes it is an SEO problem that turns out to have nothing to do with SEO at all.
If your WordPress website has one of those problems where everyone keeps telling you what should be happening, but nobody can explain what is actually happening, that is usually where we can help.
Call Kim and Conor at (402) 522-6468 or contact Big Red SEO and let us take a look.




