├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── LICENSE ├── README.md ├── wp-content └── plugins │ └── superez-ai-seo │ ├── _inc.css │ └── styles.css │ ├── _inc.htm │ └── tpl_edit.htm │ ├── _inc.js │ ├── script.js │ ├── superez-gpt-builder.editor.js │ └── superez-gpt-builder.gptapi.js │ ├── _inc.php │ └── sections.php │ └── superez-ai-seo.php └── wp-superez-ai-seo-page.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, g023 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperEZ AI SEO Wordpress Plugin 2 | 3 | A Wordpress plugin that utilizes the power of OpenAI GPT-3/GPT-4 API to generate SEO content for your blog or page posts. This Wordpress plugin serves as a personal AI assistant to help you with content ideas and creating content. It also allows you to add Gutenberg blocks to the editor after the assistant generates the content. 4 | 5 | ## Features 6 | 7 | - Early alpha release, use at your own risk. 8 | - Personal AI assistant to assist with content ideas and creation. 9 | - Ability to add Gutenberg blocks to the editor. 10 | - AI-powered revision and update of existing titles. 11 | - Mainly running in JavaScript with PHP handling on page load to eliminate page reloads while using the plugin. 12 | - Utilizes jQuery to access the API and provide functionality. 13 | - Various fields for SEO generation, including meta title, meta description, meta keywords, meta categories, and meta tags. 14 | - Supports the use of short tags in editable prompts, such as {PAGE_TITLE}. 15 | - Easy to add more fields using a simple array, although their interactivity will need to be connected via jQuery. 16 | - Temperature can be adjusted, with 0.0 being very conservative and 1.2 being very creative. 17 | - Max tokens control the limit of tokens for each request. 18 | - Click on the title of the AI assistant to minimize or maximize its window. 19 | 20 | ## Installation 21 | 22 | 1. Download the plugin and place it in the `wp-content/plugins/` folder. 23 | - Create a folder called `superez-ai-seo` inside the `plugins` folder. 24 | - Place the plugin files inside the `superez-ai-seo` folder. 25 | - Example path: `wp-content/plugins/superez-ai-seo/superez-ai-seo.php` 26 | - Write permissions are not required for any of the folders at the moment. 27 | 2. Activate the plugin. 28 | - Go to the WordPress plugins section and activate the plugin. 29 | 3. Access the plugin in a blog post or page. 30 | - You should see a section called "AI SEO and AI Content Generation". 31 | - If the section is collapsed, click on it to show the AI assistants. 32 | 4. Set your API key. 33 | - Get an API key from [https://openai.com/blog/openai-api](https://openai.com/blog/openai-api). 34 | - Add your API key to the API key field and click "Set API Key". 35 | 5. Start using the plugin. 36 | - Choose an assistant and start exploring its features. 37 | - You can start by entering a simple title on your page and then use the AI to generate a different title. Click "AI Revise Main Title" and then "Update Main Title" to update the main title with the revised title. 38 | 6. Enjoy! 39 | 40 | ## Requires 41 | - OpenAI API Key 42 | - Tested on clean install of Wordpress 6.3.2 43 | 44 | [![screenshot](https://raw.githubusercontent.com/g023/SuperEZ-AI-SEO-Wordpress-Plugin/main/wp-superez-ai-seo-page.png)](#screenshot) 45 | 46 | 47 | ## Notes 48 | v1.0.1a 49 | - Code cleanup. Release .zip should install as a wordpress zipped plugin. 50 | 51 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | .ezseo .row { 3 | display:block; 4 | } 5 | 6 | .ezseo .col { 7 | display:inline-block; 8 | } 9 | 10 | .ezseo .button { 11 | margin-left:5px; 12 | margin-right:5px; 13 | } 14 | 15 | .d-footer input, 16 | .ezseo input.output { 17 | margin-bottom:4px; 18 | } 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | .d-ai-assistant { 30 | position:fixed; 31 | border:1px solid #222; 32 | bottom:40px; 33 | right:30px; 34 | text-align:center; 35 | /* 36 | margin:2px; 37 | padding:10px; 38 | */ 39 | padding:0px; 40 | z-index:999998999; 41 | background-color:white; 42 | background-color:rgba(255,255,255,0.6); 43 | /* blur effect */ 44 | backdrop-filter: blur(5px); 45 | -webkit-backdrop-filter: blur(5px); 46 | border-radius:2px 2px 2px 2px; 47 | 48 | padding:20px; 49 | 50 | min-width:220px; 51 | } 52 | 53 | /* now style interior */ 54 | 55 | .d-ai-assistant .row { 56 | position:relative; 57 | width:100%; 58 | padding:2px; 59 | } 60 | 61 | .d-ai-assistant button, 62 | .d-ai-assistant input, 63 | .d-ai-assistant textarea { 64 | position:relative; 65 | width:100%; 66 | padding:2px;margin:0px; 67 | outline:none; 68 | left:0px;right:0px; 69 | /* prevent overlapping */ 70 | 71 | border:1px solid #ccc; 72 | border-radius:2px 2px 2px 2px; 73 | 74 | 75 | } 76 | 77 | .d-ai-assistant .title { 78 | cursor:pointer; 79 | /* no select */ 80 | user-select:none; 81 | -webkit-user-select:none; 82 | -moz-user-select:none; 83 | -ms-user-select:none; 84 | -o-user-select:none; 85 | /* background-color:#ccc; */ 86 | padding:2px; 87 | border-radius:2px 2px 0px 0px; 88 | border-bottom:1px solid #ccc; 89 | 90 | } 91 | 92 | .d-ai-assistant .title:hover { 93 | background-color:#ccc; 94 | } 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | .config-container { 103 | } 104 | .config-container textarea { 105 | width: 100%; 106 | } 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.htm/tpl_edit.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 |
33 |
34 |
35 |
36 |

37 |
38 |

39 | 40 |
41 | 42 |
43 | 44 | 47 | 48 |

49 | 50 | max tokens: 51 |
52 |

53 |
output:
54 | 55 |

56 |
57 |
58 |
59 |
60 |

61 | 62 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.js/script.js: -------------------------------------------------------------------------------- 1 | console.log('Hello World! admin section'); 2 | 3 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.js/superez-gpt-builder.editor.js: -------------------------------------------------------------------------------- 1 | // BEGIN :: UPDATE API KEY // 2 | // when we click the the-aikey-btn we set the api key for current window. (at moment just storing in a global variable) 3 | // begin. 4 | g_apiKey = ''; 5 | g_GPT = new ChatGPTClient(g_apiKey); 6 | 7 | 8 | // when we click the the-aikey-btn we set the api key for current window. (at moment just storing in a global variable) 9 | jQuery(document).ready(function($) { 10 | jQuery('#the-aikey-btn').click(function() { 11 | var key = $('#the-aikey').val(); 12 | g_apiKey = key; 13 | g_GPT = new ChatGPTClient(g_apiKey); 14 | alert('API key set.'); 15 | // blank field 16 | $('#the-aikey').val(''); 17 | }); 18 | }); 19 | // END :: UPDATE API KEY // 20 | 21 | // BEGIN :: HANDLE UPDATE TITLE FROM REVISED // 22 | /* 23 | // Get the current post title. 24 | const currentTitle = wp.data.select('core/editor').getCurrentPost().title; 25 | 26 | // Define the new title. 27 | const newTitle = 'New Page Title'; 28 | 29 | // Update the post title using the `wp.data.dispatch` method. 30 | wp.data.dispatch('core/editor').editPost({ title: newTitle }); 31 | 32 | // You can also console log the old and new titles for verification. 33 | console.log('Old Title:', currentTitle); 34 | console.log('New Title:', newTitle); 35 | */ 36 | // when we click the update-main-title button we update the main title with the revised title 37 | jQuery(document).ready(function($) { 38 | jQuery('.update-main-title').click(function() { 39 | var revised_title = $('#ez-base-title').val(); 40 | 41 | // update the title 42 | wp.data.dispatch('core/editor').editPost({ title: revised_title }); 43 | }); 44 | }); 45 | // END :: HANDLE UPDATE TITLE FROM REVISED // 46 | 47 | 48 | // -- end: 1 49 | 50 | 51 | // begin: 2 52 | 53 | // when slider changes, update the number 54 | jQuery(document).ready(function($) { 55 | $('#the-ai-assistant-temperature').on('input', function() { 56 | $('#the-ai-assistant-temperature-2').val($(this).val()); 57 | }); 58 | }); 59 | // when number changes, update the slider 60 | jQuery(document).ready(function($) { 61 | $('#the-ai-assistant-temperature-2').on('input', function() { 62 | $('#the-ai-assistant-temperature').val($(this).val()); 63 | }); 64 | }); 65 | // -- end: 2 66 | 67 | 68 | 69 | 70 | // begin: 3 71 | 72 | // find first {ai} tag in document and return the result after processing with the prompt 73 | // return error if prompt empty 74 | // if {PAGE_TITLE} in prompt, use the page title in place of that tag 75 | jQuery(document).ready(function($) { 76 | 77 | // when + block clicked, take assistant output and insert it as a block in wp editor 78 | // BEGIN :: HOW TO INSERT A BLOCK INTO THE EDITOR 79 | /* 80 | // create a block 81 | var block = wp.blocks.createBlock('core/paragraph', { 82 | content: 'Hello World!', 83 | }); 84 | // add the block to the editor 85 | wp.data.dispatch('core/editor').insertBlocks(block); 86 | */ 87 | // END :: HOW TO INSERT A BLOCK INTO THE EDITOR 88 | jQuery('#the-ai-assistant-add-block').on('click', function() { 89 | // get the output 90 | var output = $('#assistant-output').val(); 91 | // if it is empty, error 92 | if (output == '') { 93 | alert('No content to add.'); 94 | return; 95 | } 96 | 97 | // create a block 98 | var block = wp.blocks.createBlock('core/paragraph', { 99 | content: output, 100 | }); 101 | 102 | // add the block to the editor 103 | // wp.data.dispatch('core/editor').insertBlocks(block); // deprecated 104 | // new: wp.data.dispatch( 'core/block-editor' ).insertBlocks` 105 | wp.data.dispatch( 'core/block-editor' ).insertBlocks( block ); 106 | }); 107 | 108 | // when title clicked, slidetoggle 109 | jQuery('.d-ai-assistant .title').on('click', function() { 110 | jQuery('.d-ai-assistant .ai-assistant').slideToggle(10); 111 | }); 112 | 113 | // when assistant button clicked 114 | jQuery('#the-ai-assistant-btn').click(function() { 115 | // createBlock from wordpress 116 | 117 | var prompt = $('#the-prompt').val(); 118 | if (prompt == '') { 119 | alert('Please enter a prompt.'); 120 | return; 121 | } 122 | // get the page title 123 | var title = $('h1.editor-post-title').html(); 124 | // replace {PAGE_TITLE} with title 125 | prompt = prompt.replace('{PAGE_TITLE}', title); 126 | // get the content 127 | var content = $('.editor-post-text-editor').val(); 128 | // replace {PAGE_CONTENT} with content 129 | prompt = prompt.replace('{PAGE_CONTENT}', content); 130 | // get the model 131 | var model = 'gpt-3.5-turbo-16k'; 132 | // get the temperature 133 | // var temperature = 0.7; 134 | var temperature = $('#the-ai-assistant-temperature').val(); 135 | console.log('temperature',temperature); 136 | // get the max tokens 137 | // var max_tokens = 60; 138 | var max_tokens = $('#the-ai-assistant-max-tokens').val(); 139 | console.log('max_tokens',max_tokens); 140 | // make temperate a float 141 | temperature = parseFloat(temperature); 142 | // make max tokens an int 143 | max_tokens = parseInt(max_tokens); 144 | // set the prompt up 145 | messages = [{ role: 'user', content: prompt }]; 146 | // send the request, and await a response 147 | get_response(model,messages,temperature,max_tokens, '.d-ai-assistant #assistant-output'); 148 | 149 | }); 150 | }); 151 | 152 | 153 | // BEGIN :: 154 | // create a function based off the code in .my-seo-fetch-revise-title 155 | async function update_output(parent_class) 156 | { 157 | // theButton is $(this) 158 | // title = jQuery('h1.editor-post-title').html(); 159 | // use wp. javascript functions to get title 160 | const title = wp.data.select('core/editor').getCurrentPost().title; 161 | content = jQuery('.editor-post-text-editor').val(); // need a better option 162 | // get the parent div 163 | var parent_div = jQuery(parent_class); 164 | // get the target to send the output to 165 | var update_class = parent_class + ' .output'; 166 | 167 | console.log('parent_class',parent_class); 168 | console.log('update_class',update_class); 169 | 170 | console.log('current_output',jQuery(update_class).val()); 171 | 172 | // get the prompt // parent gpt-field // child prompt 173 | // var prompt = parent_div.find('.prompt').val(); 174 | var prompt = parent_div.find('.the-preprompt').val(); 175 | 176 | console.log('prompt',prompt); 177 | 178 | // replace {POST_TITLE} with title 179 | prompt = prompt.replace('{POST_TITLE}', title); 180 | 181 | // replace {POST_CONTENT} with content 182 | prompt = prompt.replace('{POST_CONTENT}', content); 183 | 184 | console.log('prompt',prompt); 185 | // get the model 186 | // var model = parent_div.find('.ai-model').val(); 187 | var model = parent_div.find('.the-model').val(); 188 | console.log('model',model); 189 | // get the temperature 190 | // var temperature = $('.gpt-field .ai-temperature').val(); 191 | // var temperature = parent_div.find('.ai-temperature').val(); 192 | var temperature = parent_div.find('.the-temp').val(); 193 | console.log('temperature',temperature); 194 | // get the max tokens 195 | // var max_tokens = $('.gpt-field .ai-max-tokens').val(); 196 | // var max_tokens = parent_div.find('.ai-max-tokens').val(); 197 | var max_tokens = parent_div.find('.the-maxtokens').val(); 198 | console.log('max_tokens',max_tokens); 199 | // make temperate a float 200 | temperature = parseFloat(temperature); 201 | // make max tokens an int 202 | max_tokens = parseInt(max_tokens); 203 | // set the prompt up 204 | messages = [{ role: 'user', content: prompt }]; 205 | 206 | // send the request, and await a response 207 | await get_response(model,messages,temperature,max_tokens, update_class); 208 | } 209 | 210 | // make an array of updateables 211 | var updateables = [ 212 | '.my-seo-fetch-revise-title', 213 | '.my-seo-fetch-title', 214 | '.my-seo-fetch-ai-description', 215 | '.my-seo-fetch-ai-keywords', 216 | '.my-seo-fetch-ai-categories', 217 | '.my-seo-fetch-ai-tags', 218 | ]; 219 | 220 | // attach click handlers to each updateable 221 | jQuery(document).ready(function($) { 222 | updateables.forEach(function(updateable) { 223 | jQuery(updateable).on('click', async function( theButton ) { 224 | var parent_class = '.gpt-field[fld='+jQuery(this).attr('fld')+']'; 225 | await update_output(parent_class); 226 | }); 227 | }); 228 | }); 229 | 230 | // when we click my-seo-fetch-revise-title 231 | 232 | // END :: 233 | 234 | 235 | // -- end: 3 236 | 237 | 238 | // begin: 4 239 | 240 | // handle sliders for gpt-fields 241 | jQuery(document).ready(function($) { 242 | $('.gpt-field input[type=range]').on('input', function() { 243 | $(this).next().val($(this).val()); 244 | }); 245 | $('.gpt-field input[type=number]').on('input', function() { 246 | $(this).prev().val($(this).val()); 247 | }); 248 | }); 249 | 250 | // default hide config 251 | jQuery(document).ready(function($) { 252 | $('.gpt-field .config-container').hide(); 253 | }); 254 | // when click show config 255 | jQuery(document).ready(function($) { 256 | $('.gpt-field .show-hide-config').on('click', function() { 257 | $(this).parent().find('.config-container').slideToggle(10); 258 | }); 259 | }); 260 | 261 | // -- end: 4 -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.js/superez-gpt-builder.gptapi.js: -------------------------------------------------------------------------------- 1 | 2 | // get_response requires global: g_GPT which is a ChatGPTClient object 3 | 4 | class ChatGPTClient { 5 | constructor(apiKey) { 6 | this.apiKey = apiKey; 7 | this.baseUrl = 'https://api.openai.com/v1/chat/completions'; 8 | } 9 | 10 | sendRequest(model, messages, temperature = 0.7, max_tokens=150) { 11 | 12 | const headers = { 13 | 'Content-Type': 'application/json', 14 | 'Authorization': `Bearer ${this.apiKey}`, 15 | }; 16 | 17 | const requestData = { 18 | model, 19 | messages, 20 | temperature, 21 | max_tokens 22 | }; 23 | 24 | // return $.ajax({ 25 | // use jQuery. syntax to make it easier to use in a web browser 26 | return jQuery.ajax({ 27 | type: 'POST', 28 | url: this.baseUrl, 29 | headers, 30 | data: JSON.stringify(requestData), 31 | success: (response) => response, 32 | error: (error) => error, 33 | }); // end -> ajax 34 | 35 | } // end -> sendRequest 36 | } // end -> class ChatGPTClient 37 | 38 | // requires a global: g_GPT 39 | async function get_response(model,messages,temperature, max_tokens=40, update_class='#error') 40 | { 41 | // check for blank g_GPT.apiKey 42 | if(g_GPT.apiKey == '') 43 | { 44 | // $(update_class).val('Error: No API key set.'); 45 | alert('Error: No API key set.'); 46 | return; 47 | } 48 | 49 | try { 50 | const response = await g_GPT.sendRequest(model, messages, temperature, max_tokens); 51 | console.log(response); 52 | assistantResponse = response.choices[0].message.content; 53 | 54 | // remove double quotes from beginning and end of string 55 | if(assistantResponse.startsWith('"') && assistantResponse.endsWith('"')) 56 | assistantResponse = assistantResponse.substring(1, assistantResponse.length - 1); 57 | 58 | jQuery(update_class).val(assistantResponse); 59 | } catch (error) { 60 | // $(update_class).val(`Error: ${error.statusText}`); 61 | // use jquery syntax to make it easier to use in a web browser 62 | // jQuery(update_class).val(`Error: ${error.statusText}`); 63 | console.log('ERROR:', error.statusText); 64 | alert('ERROR:', error.statusText); 65 | } 66 | 67 | /* sleep for 5 seconds to help prevent overloading */ 68 | await new Promise(r => setTimeout(r, 5000)); 69 | 70 | return; 71 | } 72 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/_inc.php/sections.php: -------------------------------------------------------------------------------- 1 | 'ez-base-title', 5 | 'label' => 'Revise Base Title', 6 | 'description' => 'The base title for the page.', 7 | 'prompt' => 'Just show the title. You will take the title [{POST_TITLE}] and generate a better title. If the title looks like it contains things that do not seem in place, remove those elements.', 8 | 'template-admin' => '', 9 | 'sanitize-type' => 'sanitize_text_field', 10 | 'escape-type' => 'esc_attr', 11 | 'button' => 'AI Revise Main Title', 12 | 'button-id' => 'my-seo-fetch-revise-title', 13 | 'ai-max-tokens' => 60, 14 | 'ai-temperature' => 0.7, 15 | 'ai-model' => 'gpt-3.5-turbo-16k', // 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-instruct', 'gpt-4', 'gpt-4-32k' 16 | 'more-buttons' => [[ 'id'=>'update-main-title', 'label'=>'Update Main Title' ]], 17 | ); 18 | 19 | $g_fields[] = array( 20 | 'id' => 'ez-meta-title', 21 | 'label' => 'Meta Title', 22 | 'description' => 'The meta title for the page.', 23 | 'prompt' => 'Show me a catchy social media title for an og:title tag based on the page title for a blog called {POST_TITLE}.', 24 | 'template-admin' => '', 25 | 'sanitize-type' => 'sanitize_text_field', 26 | 'escape-type' => 'esc_attr', 27 | 'button' => 'Fetch AI Title', 28 | 'button-id' => 'my-seo-fetch-title', 29 | 'ai-max-tokens' => 60, 30 | 'ai-temperature' => 0.7, 31 | 'ai-model' => 'gpt-3.5-turbo-16k', // 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-instruct', 'gpt-4', 'gpt-4-32k' 32 | ); 33 | 34 | 35 | $g_fields[] = array( 36 | 'id' => 'ez-meta-desc', 37 | 'label' => 'Meta Description', 38 | 'description' => 'The meta description for the page.', 39 | 'prompt' => 'Just show the description. Show me a catchy social media description for an og:description tag based on the page content for a blog called {POST_TITLE}.', 40 | 'template-admin' => '', 41 | 'sanitize-type' => 'sanitize_text_field', 42 | 'escape-type' => 'esc_attr', 43 | 'button' => 'Fetch AI Description', 44 | 'button-id' => 'my-seo-fetch-ai-description', 45 | 'ai-max-tokens' => 250, 46 | 'ai-temperature' => 0.7, 47 | 'ai-model' => 'gpt-3.5-turbo-16k', 48 | ); 49 | 50 | $g_fields[] = array( 51 | 'id' => 'ez-meta-keywords', 52 | 'label' => 'Meta Keywords', 53 | 'description' => 'The meta keywords for the page.', 54 | 'prompt' => 'Just show the keywords with each keyword separated by a comma. No numbers. Show me a list of keywords for a blog called {POST_TITLE}. Order by most relevant to least relevant.', 55 | 'template-admin' => '', 56 | 'sanitize-type' => 'sanitize_text_field', 57 | 'escape-type' => 'esc_attr', 58 | 'button' => 'Fetch AI Keywords', 59 | 'button-id' => 'my-seo-fetch-ai-keywords', 60 | 'ai-max-tokens' => 200, 61 | 'ai-temperature' => 0.7, 62 | 'ai-model' => 'gpt-3.5-turbo-16k', 63 | ); 64 | 65 | $g_fields[] = array( 66 | 'id' => 'ez-meta-categories', 67 | 'label' => 'Meta Categories', 68 | 'description' => 'The meta categories for the page.', 69 | 'prompt' => 'Just show the categories, with each category separated by a comma. No numbers. Show me a list of categories for a blog called {POST_TITLE}. Order by most relevant to least relevant.', 70 | 'template-admin' => '', 71 | 'sanitize-type' => 'sanitize_text_field', 72 | 'escape-type' => 'esc_attr', 73 | 'button' => 'Fetch AI Categories', 74 | 'button-id' => 'my-seo-fetch-ai-categories', 75 | 'ai-max-tokens' => 200, 76 | 'ai-temperature' => 0.7, 77 | 'ai-model' => 'gpt-3.5-turbo-16k', 78 | ); 79 | 80 | $g_fields[] = array( 81 | 'id' => 'ez-meta-tags', 82 | 'label' => 'Meta Tags', 83 | 'description' => 'The meta tags for the page.', 84 | 'prompt' => 'Just show me the tags, with each tag separated by a comma. No numbers. Show me a list of tags for a blog called {POST_TITLE}. Order by most relevant to least relevant.', 85 | 'template-admin' => '', 86 | 'sanitize-type' => 'sanitize_text_field', 87 | 'escape-type' => 'esc_attr', 88 | 'button' => 'Fetch AI Tags', 89 | 'button-id' => 'my-seo-fetch-ai-tags', 90 | 'ai-max-tokens' => 200, 91 | 'ai-temperature' => 0.7, 92 | 'ai-model' => 'gpt-3.5-turbo-16k', 93 | ); 94 | 95 | -------------------------------------------------------------------------------- /wp-content/plugins/superez-ai-seo/superez-ai-seo.php: -------------------------------------------------------------------------------- 1 | https://github.com/g023 7 | License: 3-clause BSD license (https://opensource.org/licenses/BSD-3-Clause) 8 | */ 9 | 10 | 11 | /* 12 | Features: 13 | Use the power of OpenAI GPT-3/GPT-4 API to generate content for your blog or page post in your wordpress site. 14 | - early alpha release, so use at your own risk. 15 | - personal ai assistant to help you with content ideas/creating content 16 | - ability to add gutenberg blocks to the editor after the assistant generates. 17 | - existing title revise/update using AI 18 | - mainly running in Javascript with PHP just handled on the page load. 19 | - jQuery used to access api and provide functionality. 20 | - various fields for SEO generation. 21 | - meta title, meta description, meta keywords, meta categories, meta tags... 22 | - can use short tags in editable prompts such as {PAGE_TITLE} 23 | - easy to add more fields using a simple array. 24 | - their interactivity will still need to be connected via jquery somewhere in here. 25 | - temperature can be adjusted. (0.0 = very conservative, 1.2 = very creative) 26 | - max tokens controls how many tokens you want to limit the request to 27 | - click on title of ai assistant to minimize/maximize its window 28 | 29 | 30 | INSTALL: 31 | 1) install plugin to your wp-content\plugins\ folder 32 | - inside the plugins folder, you would make a folder called superez-ai-seo and then place the files in there. 33 | eg) wp-content\plugins\superez-ai-seo\superez-ai-seo.php 34 | - write permissions not required on any of the folders at the moment 35 | 2) activate plugin 36 | - activate plugin in wordpress plugins section 37 | 3) go into a blog post or a page and you should see it as a section called 'AI SEO and AI Content Generation' 38 | - if the section is collapsed, you may need to click it to show the AI assistants. 39 | 4) add your api key to the api key field and click 'set api key' 40 | - you can get an api key from https://openai.com/blog/openai-api 41 | 5) once key is loaded you simply pick an assistant and start playing around. 42 | - you might want to throw a simple title in on the page title, and then get ai to generate a different title first. 43 | (AI Revise Main Title) and then click (Update Main Title) to update the main title with the revised title. 44 | 6) enjoy :) 45 | 46 | TODO: 47 | - maybe add a key storage at some point. Right now you have to enter it whenever you enter the page 48 | and the key is just stored in memory, which is released when you leave the page. 49 | */ 50 | 51 | // This is a wordpress plugin for the admin section that helps build content using AI 52 | 53 | // add a input and button to top bar 54 | function my_seo_plugin_admin_bar() { 55 | global $wp_admin_bar; 56 | 57 | $wp_admin_bar->add_menu(array( 58 | 'id' => 'my-seo-plugin', 59 | 'title' => 'SuperEZ AI SEO', 60 | 'href' => '#', 61 | 'meta' => array( 62 | 'title' => __('SuperEZ AI SEO'), 63 | ), 64 | )); 65 | 66 | $wp_admin_bar->add_menu(array( 67 | 'id' => 'my-seo-plugin-settings', 68 | 'parent' => 'my-seo-plugin', 69 | 'title' => 'Settings', 70 | 'href' => admin_url('options-general.php?page=my-seo-plugin-settings'), 71 | 'meta' => array( 72 | 'title' => __('Settings'), 73 | ), 74 | )); 75 | /* 76 | $wp_admin_bar->add_menu(array( 77 | 'id' => 'my-seo-plugin-help', 78 | 'parent' => 'my-seo-plugin', 79 | 'title' => 'Help', 80 | 'href' => admin_url('options-general.php?page=my-seo-plugin-help'), 81 | 'meta' => array( 82 | 'title' => __('Help'), 83 | ), 84 | )); 85 | */ 86 | } 87 | 88 | add_action('admin_bar_menu', 'my_seo_plugin_admin_bar', 100); 89 | 90 | // add a settings page 91 | function my_seo_plugin_settings_page() { 92 | ?> 93 |
94 |

SuperEZ AI SEO Settings

95 |

Here are the settings for the SuperEZ AI SEO plugin.

96 | 97 | 98 | 99 | " size="50"> 102 | 103 | */ 104 | ?> 105 | 106 | 107 |
108 | 115 |
116 |

SuperEZ AI SEO Help

117 |

Here is some help for the SuperEZ AI SEO plugin.

118 |
119 | 'ez-base-title', 169 | // 'label' => 'Revise Base Title', 170 | // 'description' => 'The base title for the page.', 171 | // 'prompt' => 'Just show the title. You will take the title [{POST_TITLE}] and generate a better title.', 172 | // 'template-admin' => '', 173 | // 'sanitize-type' => 'sanitize_text_field', 174 | // 'escape-type' => 'esc_attr', 175 | // 'button' => 'AI Revise Main Title', 176 | // 'button-id' => 'my-seo-fetch-revise-title', 177 | // 'ai-max-tokens' => 60, 178 | // 'ai-temperature' => 0.7, 179 | // 'ai-model' => 'gpt-3.5-turbo-16k', // 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-instruct', 'gpt-4', 'gpt-4-32k' 180 | // 'more-buttons' => [[ 'id'=>'update-main-title', 'label'=>'Update Main Title' ]], 181 | // ); 182 | 183 | 184 | function gpt_dropdown_html($select='') 185 | { 186 | $html = ""; 192 | return $html; 193 | } 194 | 195 | // TODO: MOVE TO TEMPLATE WITH TEMPLATE TAGS: 196 | // {{{PLUGIN_DIR}}} 197 | // Callback function to render the meta box 198 | function my_seo_meta_box_callback($post) { 199 | global $g_fields; 200 | // Get the current meta values 201 | $meta_description = get_post_meta($post->ID, '_meta_description', true); 202 | $site_id = get_current_blog_id(); 203 | 204 | $plugin_dir = plugin_dir_url(__FILE__); 205 | 206 | $tags["{{{PLUGIN_DIR}}}"] = $plugin_dir; // 207 | 208 | // Output the HTML for the meta box 209 | $template = file_get_contents($plugin_dir.'_inc.htm/tpl_edit.htm'); 210 | // replace tags 211 | foreach ($tags as $tag => $value) { 212 | $template = str_replace($tag, $value, $template); 213 | } 214 | echo $template; 215 | 216 | // -----> continue 217 | 218 | // now add the fields 219 | foreach ($g_fields as $field) { 220 | echo "
"; 221 | echo '
'; 222 | 223 | // show prompt as hidden field (probably redundant) 224 | // echo ''; 225 | /* 226 | 'ai-max-tokens' => 60, 227 | 'ai-temperature' => 0.7, 228 | 'ai-model' => 'gpt-3.5-turbo-16k', 229 | */ 230 | echo ''; 231 | echo ''; 232 | echo ''; 233 | 234 | 235 | $escape_type = $field['escape-type']; 236 | $sanitize_type = $field['sanitize-type']; 237 | $template = $field['template-admin']; 238 | $gpt_selected = $field['ai-model']; 239 | 240 | $content = $escape_type($sanitize_type(get_post_meta($post->ID, $field['id'], true))); 241 | $template = str_replace('{CONTENT}', $content, $template); 242 | 243 | 244 | $template .= "
"; 245 | // BEGIN :: slider for temperature with number input as well 246 | $template .= "
"; 247 | // preprompt 248 | $template .= "
"; 249 | 250 | $template .= 'temp: '; 251 | $template .= ''; 252 | $template .= ''; 253 | $template .= "  "; 254 | $template .= '
'; 255 | // END :: slider 256 | 257 | // max tokens 258 | $template .= "
"; 259 | $template .= 'max tokens: '; 260 | $template .= ''; 261 | $template .= '
'; 262 | 263 | // dropdown for model (select default) 264 | $template .= "

"; 265 | $template .= gpt_dropdown_html($gpt_selected); 266 | $template .= "
"; 267 | 268 | $template .= "
"; 269 | 270 | 271 | 272 | // show hide configurations 273 | $template .= "
"; 274 | 275 | $button_id = $field['button-id']; 276 | $button = $field['button']; 277 | $template .= ''; 278 | 279 | 280 | 281 | // check for more buttons and add 282 | if (isset($field['more-buttons'])) 283 | foreach ($field['more-buttons'] as $more_button) 284 | $template .= '
'; 285 | 286 | // $template .= "

"; 287 | 288 | echo $template; 289 | echo "

"; 290 | 291 | //echo '
'; 292 | } 293 | ?> 294 | 295 | 296 |
297 | 298 | 299 | ID, '_meta_title', true); 335 | if (!empty($meta_title)) { 336 | echo '' . esc_html($meta_title) . ''; 337 | } 338 | */ 339 | 340 | $meta_description = get_post_meta($post->ID, 'ez-meta-desc', true); 341 | if (!empty($meta_description)) 342 | echo ''; 343 | 344 | 345 | $meta_keywords = get_post_meta($post->ID, 'ez-meta-keywords', true); 346 | if (!empty($meta_keywords)) 347 | echo ''; 348 | 349 | 350 | $meta_categories = get_post_meta($post->ID, 'ez-meta-categories', true); 351 | if (!empty($meta_categories)) 352 | echo ''; 353 | 354 | 355 | $meta_tags = get_post_meta($post->ID, 'ez-meta-tags', true); 356 | if (!empty($meta_tags)) 357 | echo ''; 358 | 359 | // handle social twitter/opengraph/facebook/whatever 360 | $social_title = get_post_meta($post->ID, 'ez-meta-title', true); 361 | $social_desc = get_post_meta($post->ID, 'ez-meta-desc', true); 362 | 363 | if (!empty($social_title)) 364 | echo ''; 365 | else 366 | echo ''; 367 | 368 | if (!empty($social_desc)) 369 | echo ''; 370 | else 371 | echo ''; 372 | 373 | // handle twitter 374 | if (!empty($social_title)) 375 | echo ''; 376 | else 377 | echo ''; 378 | 379 | if (!empty($social_desc)) 380 | echo ''; 381 | else 382 | echo ''; 383 | 384 | /* 385 | // handle image (use wp. to get image) 386 | 387 | if (!empty($social_image)) { 388 | echo ''; 389 | echo ''; 390 | } 391 | 392 | // handle url 393 | $social_url = wp.get_permalink($post->ID); 394 | if (!empty($social_url)) { 395 | echo ''; 396 | echo ''; 397 | } 398 | 399 | // handle site name 400 | 401 | // $social_site_name = get_post_meta($post->ID, 'ez-meta-site-name', true); 402 | $social_site_name = get_bloginfo('name'); 403 | if (!empty($social_site_name)) { 404 | echo ''; 405 | } 406 | 407 | // handle type 408 | $social_type = wp.get_post_type($post->ID); 409 | if (!empty($social_type)) { 410 | echo ''; 411 | } 412 | 413 | */ 414 | 415 | 416 | } 417 | } 418 | 419 | add_action('wp_head', 'my_seo_plugin_head'); 420 | 421 | 422 | 423 | 424 | // add stylesheet from plugin 425 | // function my_seo_plugin_styles() { 426 | // wp_enqueue_style('my-seo-styles', plugin_dir_url(__FILE__) . '_inc.css/styles.css'); 427 | // } 428 | 429 | 430 | 431 | 432 | // 433 | 434 | -------------------------------------------------------------------------------- /wp-superez-ai-seo-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/g023/SuperEZ-AI-SEO-Wordpress-Plugin/4902f04b472ba585e5f708674d5815c30da61539/wp-superez-ai-seo-page.png --------------------------------------------------------------------------------