SpinTax and Calling C# from PowerShell

What is SpinTax?

SpinTax is a way of “spinning” text. It’s often used by somewhat spammy marketing programs to post different words, phrase, or articles to different web or social media sites. It is designed to create somewhat readable but yet random text, based on changing synonyms for various words. There are actually programs that will take articles and turn them into SpinTax. They try to substitute appropriate synonyms for nouns, verbs, and ajectives. Then when the text is re-run, your article should be fairly unique. However, sometimes, the spun articles turn out to be unreadable. You wouldn’t use this on your “money site”, but they are often used for Tier 1, 2, or 3 support sites, where you make SEO backlinks to your main site, or other tiers.

Here’s a quick example from the sample code below:

{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Davis}!

This means to pick one of the three “greeting” words, followed always by the words/phrase “to you, “, then followed by one of the three salutations (Mr., Mrs, or Ms.) then followed by one of three last names. The program runs below runs the SpinTax in a loop 20 times, so you can see what it generates. It should be random, but occasionally a phrase will repeat. The above is a simple example; SpinTax can also be nested. When you get code from the internet, make sure it handles “nested Spintax.” You might have to run your own test to make sure.

Many programmers use RegEx to accomplish the same, for example here is a PHP code sample: PHP RegEx Spinner. I didn’t quite understand all the RegEx, especially the ?R. That code actually does a call back. I attempted in PowerShell, but had some issues. Perhaps that will be a topic of a future blog article.

I found the fast C# code below in this StackOverflow post.

But I wanted to be able to call it from PowerShell. I could have have compiled it in Visual Studio and made a .DLL, but for fun, I decided to include it “inline” in the PowerShell program. We do that by using the “Add-Type -TypeDefinition” cmdlet, which basically assembles the C# code, which was stored in a variable. The downside of this technique is that if you change the C# code, you must close and re-open PowerShell or ISE. Running Add-Type a second time will fail, not replace your old code. So I have code below that checks to see if the type already exists, before executing the Add-Type.

SampleCode

$csCode = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Spintax
{
    public class Program
    {
        public static Random rand = new Random();
        /*
        static void Main(string[] args)
        {
            string strSpinTax = "{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {{Jason|Malina|Sara}|Williams|Davis}";
            Console.WriteLine("strSpinTax=" + strSpinTax);

            for (int j=0; j < 20; j++)
                {
                   string strResult = SpinEvenMoreFaster(strSpinTax);
                   Console.WriteLine("result=" + strResult);
                }
            Console.ReadLine();
        }
        */

        static int[] partIndices = new int[100];
        static int[] depth = new int[100];
        static char[] symbolsOfTextProcessed = new char[100000];

        public static String SpinIt(String text)
        {
            int cur = SpinEvenMoreFasterInner(text, 0, text.Length, 0);
            return new String(symbolsOfTextProcessed, 0, cur);
        }

        public static int SpinEvenMoreFasterInner(String text, int start, int end, int symbolIndex)
        {
            int last = start;
            for (int i = start; i < end; i++)
            {
                if (text[i] == '{')
                {
                    int k = 1;
                    int j = i + 1;
                    int index = 0;
                    partIndices[0] = i;
                    depth[0] = 1;
                    for (; j < end && k > 0; j++)
                    {
                        if (text[j] == '{')
                            k++;
                        else if (text[j] == '}')
                            k--;
                        else if (text[j] == '|')
                        {
                            if (k == 1)
                            {
                                partIndices[++index] = j;
                                depth[index] = 1;
                            }
                            else
                                depth[index] = k;
                        }
                    }
                    if (k == 0)
                    {
                        partIndices[++index] = j - 1;
                        int part = rand.Next(index);
                        text.CopyTo(last, symbolsOfTextProcessed, symbolIndex, i - last);
                        symbolIndex += i - last;
                        if (depth[part] == 1)
                        {
                            text.CopyTo(partIndices[part] + 1, 
                                         symbolsOfTextProcessed, 
                                         symbolIndex, 
                                         partIndices[part + 1] - partIndices[part] - 1);
                            symbolIndex += partIndices[part + 1] - partIndices[part] - 1;
                        }
                        else
                        {
                            symbolIndex = SpinEvenMoreFasterInner(text, partIndices[part] + 1, 
                                          partIndices[part + 1], symbolIndex);
                        }
                        i = j - 1;
                        last = j;
                    }
                }
            }
            text.CopyTo(last, symbolsOfTextProcessed, symbolIndex, end - last);
            return symbolIndex + end - last;
        }

    }
}
"@

cls
if (-not ([System.Management.Automation.PSTypeName]'Program').Type)
{
    #include the C# code that exists in variable $csCode
    #you will get error if this already exists 
    Add-Type -TypeDefinition $csCode
}

#non-nested
#$spintax = '{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {Smith|Williams|Davis}!';

#nested:
$spintax = '{Hello|Howdy|Hola} to you, {Mr.|Mrs.|Ms.} {{Jason|Malina|Sara}|Williams|Davis}'
$spintax = '{{you|one|they|he|she} {will|may} need to|{one|you} {must|will need to}}'

Write-Host "spinTax: $spinTax"

#This code does seem to handle nested SpinTax.

for ($i=0; $i -le 20; $i++)  #loop 20 times 
    {
       $spinResult = [Spintax.Program]::SpinIt($spintax)
       Write-Host "$($i): result=$spinResult"
    }

Results:

spinTax: {{you|one|they|he|she} {will|may} need to|{one|you} {must|will need to}}
0: result=he will need to
1: result=she will need to
2: result=you will need to
3: result=you will need to
4: result=you must
5: result=they will need to
6: result=he may need to
7: result=you must
8: result=one will need to
9: result=you will need to
10: result=he may need to
11: result=they may need to
12: result=one will need to
13: result=they may need to
14: result=you must
15: result=one must
16: result=she will need to
17: result=one will need to
18: result=one will need to
19: result=you must
20: result=one must

Uncategorized  

Leave a Reply