diff --git a/.gitignore b/.gitignore index 80319ff0..ea2c618d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.pyc __pycache__ pseo-experiemnts/ +*.swp diff --git a/README.md b/README.md index e69de29b..a34df8cf 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,51 @@ +## Introduction + +Given high level domain keywords like "Fishing baits online" Or any 2-3 main key words that describe, broadly, your business. +This tool will produce a SEO optimized blogs. This tool will suggest most popular blog topics, divide them in sub topics and write content for each sub topic. For each of the paragraphs, we summarise it and pass the line for text to image. +Thus, the generated blog will have text and relevant images. + +(TBD) Provide the blog output as plain text, markdown Or HTML. + +Presently, wordpress and WIX integration is present for uploading the generated blog, but needs testing. + + +This is based on openai gpt models for content generation, google bard for keyword research and some basic tools for plagiarism checker, SEO audit and suggestions to improve the generated content. +As prompts are the important ingredients to get the best result, they are stored in prompts folder. Edit these prompts to produce results as per your likings. + +API based blog generation are much cheaper, almost 10x, but difficult to use for everyone. We use bard for search related prompts and chatgpt for generative requirements. + +Check TBD for features currently under development. + +---------------------------------- + +## How to use this tool + +This is in active development and needs ironing out. The main concern is make it general purpose, for all. +Usuability and extendibility are major concerns. This section will be updated soon. + +---------------------------------- + +# The detailed SEO checks are as follows: + +- Keyword Density +- Keyword Presence in Title +- Keyword Presence in Image Alt Text +- Headings Text +- Internal Links +- External Links +- Readability Score +- Spelling Errors +- Grammar Errors +- SEO Score +- SEO Suggestions to improve generated content + +----------------------------------- + +#What to write on ? + +This is basically keyword research for a specific domain, narrowed down by blog topics. +We can craft prompts to get an idea on what to generate blogs on. Divide them in topic and write for most searched ones, as below: + +#[Prompts] +For more details on prompts used to get blog topics and SEO keyword research, check file blog_ideas.prompts in prompts folder. + diff --git a/TBD b/TBD index ea142857..b552bd49 100644 --- a/TBD +++ b/TBD @@ -1,2 +1,9 @@ -https://github.com/hardikvasa/google-images-download +1). https://github.com/hardikvasa/google-images-download +2). imagen from google +3). dalle-3 +4). Bing images +5). Include gpt researcher : https://python.langchain.com/docs/use_cases/web_scraping + +6). We need memory to store blogs posts and not repeat them. +Have a database, or query from web hosting, of all the blogs present. diff --git a/lib/.get_text_response.py.swp b/lib/.get_text_response.py.swp deleted file mode 100644 index 0ac235ad..00000000 Binary files a/lib/.get_text_response.py.swp and /dev/null differ diff --git a/lib/get_text_response.py b/lib/get_text_response.py index e99031a3..d05b4e6e 100644 --- a/lib/get_text_response.py +++ b/lib/get_text_response.py @@ -90,7 +90,7 @@ def generate_detailed_blog(blog_keywords): while('' in blog_topic_arr): blog_topic_arr.remove('') - print(f"Generated Blog Topics: {type(blog_topic_arr)}---- {blog_topic_arr}") + print(f"Generated Blog Topics:---- {blog_topic_arr}") # For each of blog topic, generate content. for a_blog_topic in blog_topic_arr: @@ -102,7 +102,7 @@ def generate_detailed_blog(blog_keywords): a_topic = re.sub(r"^\W*\D*", "", a_blog_topic) tpc_cnt = generate_topic_content(a_topic) - #print(f"{a_topic} ------ {tpc_cnt}") + print(f"{a_topic} ------ {tpc_cnt}") # We now need to concatenate all the sections and sew it into blog content. tmp_blog_markdown_str = blog_markdown_str + " " + a_blog_topic + " " + f"{tpc_cnt}" @@ -111,7 +111,12 @@ def generate_detailed_blog(blog_keywords): # print/check the final blog content. print(f"Final blog content: {blog_markdown_str}") # Save the blog content as a .md file. Markdown or HTML ? - # Best to name the file + # Use chatgpt to convert the text into HTML or markdown. + + # Now, we need perform some *basic checks on the blog content, such as: + # is_content_ai_generated.py, plagiarism_checker_from_known_sources.py + # seo_analyzer.py . These are present in the lib folder. + # prompt: Rewrite, improve and paraphrase [text] and use headings and subheadings to break up the content and make it easier to read using the keyword [keyword]. @@ -195,6 +200,3 @@ def get_long_tailed_keywords(blog_article): """ # want you to generate a list of long-tail keywords that are related to the following blog post [Enter blog post text here] pass - - - diff --git a/lib/seo_module/prompt b/lib/seo_module/prompt new file mode 100644 index 00000000..e2448734 --- /dev/null +++ b/lib/seo_module/prompt @@ -0,0 +1,3 @@ +Act as an SEO specialist, analyze [website URL], and make improvement suggestions regarding technical SEO with the ways to make those improvements listed in a table. + + diff --git a/lib/wordpress_api_integration/README.md b/lib/webhosting_integrations/README.md similarity index 100% rename from lib/wordpress_api_integration/README.md rename to lib/webhosting_integrations/README.md diff --git a/lib/webhosting_integrations/wix_integration.py b/lib/webhosting_integrations/wix_integration.py new file mode 100644 index 00000000..5061f6c4 --- /dev/null +++ b/lib/webhosting_integrations/wix_integration.py @@ -0,0 +1,48 @@ +import requests +import json + +def upload_blog_post(wix_site_id, wix_api_key, blog_post_title, blog_post_content): + """ Uploads a blog post to a Wix website. + + Args: + wix_site_id: The ID of the Wix website. + wix_api_key: The API key for the Wix website. + blog_post_title: The title of the blog post. + blog_post_content: The content of the blog post. + + Returns: + None. + """ + + # Create the request body. + request_body = { + "title": blog_post_title, + "content": blog_post_content + } + + # Make the request to the Wix API. + response = requests.post( + f"https://{wix_site_id}.wixsite.com/api/v1/blog/posts", + headers={"Authorization": f"Bearer {wix_api_key}"}, + json=request_body + ) + + # Check the response status code. + if response.status_code != 200: + raise Exception(f"Failed to upload blog post: {response.status_code}") + + # Print a success message. + print("Blog post uploaded successfully!") + + + +########################################################################################### +# Example usage: +wix_site_id = "1234567890" +wix_api_key = "YOUR_WIX_API_KEY" +blog_post_title = "My first blog post" +blog_post_content = "This is my first blog post." + + +upload_blog_post(wix_site_id, wix_api_key, blog_post_title, blog_post_content) + diff --git a/lib/wordpress_api_integration/V2/wordpress_api_integration.py b/lib/webhosting_integrations/wordpress_api_integration.py similarity index 100% rename from lib/wordpress_api_integration/V2/wordpress_api_integration.py rename to lib/webhosting_integrations/wordpress_api_integration.py diff --git a/lib/wordpress_api_integration/V1/main.py b/lib/wordpress_api_integration/V1/main.py deleted file mode 100644 index 6fe8d090..00000000 --- a/lib/wordpress_api_integration/V1/main.py +++ /dev/null @@ -1,21 +0,0 @@ -## main.py - -from wordpress_api import WordpressAPI - - -def main(): - """ - Main entry point of the program. - """ - # Create WordpressAPI instance - wp_api = WordpressAPI(base_url="https://example.com", username="admin", password="password") - - # Authenticate - wp_api.authenticate() - - # Upload content - content = "This is a test content" - wp_api.upload_content(content) - -if __name__ == "__main__": - main() diff --git a/lib/wordpress_api_integration/V1/test_wordpress_api.py b/lib/wordpress_api_integration/V1/test_wordpress_api.py deleted file mode 100644 index 67f56a85..00000000 --- a/lib/wordpress_api_integration/V1/test_wordpress_api.py +++ /dev/null @@ -1,30 +0,0 @@ -## test_wordpress_api.py - -import pytest -from wordpress_api import WordpressAPI - - -class TestWordpressAPI: - @pytest.fixture - def wp_api(self): - return WordpressAPI(base_url="https://example.com", username="admin", password="password") - - def test_authenticate_success(self, wp_api): - wp_api.authenticate() - assert wp_api.authentication.token is not None - - def test_authenticate_failure(self, wp_api): - wp_api.authentication.password = "wrong_password" - with pytest.raises(Exception): - wp_api.authenticate() - - def test_upload_content_success(self, wp_api): - content = "This is a test content" - wp_api.upload_content(content) - # Add assertions here to verify the success of content upload - - def test_upload_content_failure(self, wp_api): - content = "This is a test content" - wp_api.content_upload.base_url = "https://wrong_url.com" - with pytest.raises(Exception): - wp_api.upload_content(content) diff --git a/lib/wordpress_api_integration/V1/wordpress_api.py b/lib/wordpress_api_integration/V1/wordpress_api.py deleted file mode 100644 index e8c2af69..00000000 --- a/lib/wordpress_api_integration/V1/wordpress_api.py +++ /dev/null @@ -1,75 +0,0 @@ -## wordpress_api.py - -import requests -import json - -class Authentication: - def __init__(self, base_url, username, password): - self.base_url = base_url - self.username = username - self.password = password - self.token = None - - def authenticate(self): - """ - Authenticates the user with the Wordpress API. - """ - url = f"{self.base_url}/authenticate" - payload = { - "username": self.username, - "password": self.password - } - headers = { - "Content-Type": "application/json" - } - - response = requests.post(url, json=payload, headers=headers) - - if response.status_code == 200: - self.token = response.json()["token"] - else: - raise Exception("Authentication failed") - - -class ContentUpload: - def __init__(self, base_url, token): - self.base_url = base_url - self.token = token - - def upload_content(self, content): - """ - Uploads the given content to the Wordpress API. - """ - url = f"{self.base_url}/upload" - payload = { - "content": content, - "token": self.token - } - headers = { - "Content-Type": "application/json" - } - - response = requests.post(url, json=payload, headers=headers) - - if response.status_code != 200: - raise Exception("Content upload failed") - -class WordpressAPI: - def __init__(self, base_url, username, password): - self.base_url = base_url - self.username = username - self.password = password - self.authentication = Authentication(base_url, username, password) - self.content_upload = ContentUpload(base_url, self.authentication.token) - - def authenticate(self): - """ - Authenticates the user with the Wordpress API. - """ - self.authentication.authenticate() - - def upload_content(self, content): - """ - Uploads the given content to the Wordpress API. - """ - self.content_upload.upload_content(content) diff --git a/lib/wordpress_api_integration/V2/main.py b/lib/wordpress_api_integration/V2/main.py deleted file mode 100644 index 3b898946..00000000 --- a/lib/wordpress_api_integration/V2/main.py +++ /dev/null @@ -1,54 +0,0 @@ -## main.py - -import os -import requests -import json - - -class WordPressAPIIntegration: - def __init__(self, credentials: dict): - self.credentials = credentials - - def upload_file(self, file_path: str) -> bool: - if not self._check_file(file_path): - return False - - if not self._authenticate(): - return False - - if not self._upload_file_to_api(file_path): - return False - - return True - - def _check_file(self, file_path: str) -> bool: - max_file_size = 10 * 1024 * 1024 # 10MB - file_size = os.path.getsize(file_path) - if file_size > max_file_size: - return False - - valid_file_types = ['.jpg', '.jpeg', '.png', '.gif'] - file_extension = os.path.splitext(file_path)[1] - if file_extension not in valid_file_types: - return False - - return True - - def _authenticate(self) -> bool: - url = "https://wordpress-api.com/authenticate" - headers = {'Content-Type': 'application/json'} - data = json.dumps(self.credentials) - response = requests.post(url, headers=headers, data=data) - if response.status_code == 200: - return True - - return False - - def _upload_file_to_api(self, file_path: str) -> bool: - url = "https://wordpress-api.com/upload" - files = {'file': open(file_path, 'rb')} - response = requests.post(url, files=files) - if response.status_code == 200: - return True - - return False diff --git a/lib/wordpress_api_integration/V2/test_wordpress_api_integration.py b/lib/wordpress_api_integration/V2/test_wordpress_api_integration.py deleted file mode 100644 index 31fea1e0..00000000 --- a/lib/wordpress_api_integration/V2/test_wordpress_api_integration.py +++ /dev/null @@ -1,86 +0,0 @@ -## test_wordpress_api_integration.py - -import os -import pytest -from wordpress_api_integration import WordPressAPIIntegration - - -class TestWordPressAPIIntegration: - @pytest.fixture - def credentials(self): - return { - "username": "test_user", - "password": "test_password" - } - - @pytest.fixture - def valid_file_path(self): - return "path/to/valid/file.jpg" - - @pytest.fixture - def invalid_file_path(self): - return "path/to/invalid/file.txt" - - def test_upload_file_valid_file(self, credentials, valid_file_path, monkeypatch): - def mock_check_file(file_path): - return True - - def mock_authenticate(): - return True - - def mock_upload_file_to_api(file_path): - return True - - monkeypatch.setattr(WordPressAPIIntegration, "_check_file", mock_check_file) - monkeypatch.setattr(WordPressAPIIntegration, "_authenticate", mock_authenticate) - monkeypatch.setattr(WordPressAPIIntegration, "_upload_file_to_api", mock_upload_file_to_api) - - api_integration = WordPressAPIIntegration(credentials) - result = api_integration.upload_file(valid_file_path) - - assert result is True - - def test_upload_file_invalid_file(self, credentials, invalid_file_path, monkeypatch): - def mock_check_file(file_path): - return False - - monkeypatch.setattr(WordPressAPIIntegration, "_check_file", mock_check_file) - - api_integration = WordPressAPIIntegration(credentials) - result = api_integration.upload_file(invalid_file_path) - - assert result is False - - def test_upload_file_authentication_failed(self, credentials, valid_file_path, monkeypatch): - def mock_check_file(file_path): - return True - - def mock_authenticate(): - return False - - monkeypatch.setattr(WordPressAPIIntegration, "_check_file", mock_check_file) - monkeypatch.setattr(WordPressAPIIntegration, "_authenticate", mock_authenticate) - - api_integration = WordPressAPIIntegration(credentials) - result = api_integration.upload_file(valid_file_path) - - assert result is False - - def test_upload_file_upload_failed(self, credentials, valid_file_path, monkeypatch): - def mock_check_file(file_path): - return True - - def mock_authenticate(): - return True - - def mock_upload_file_to_api(file_path): - return False - - monkeypatch.setattr(WordPressAPIIntegration, "_check_file", mock_check_file) - monkeypatch.setattr(WordPressAPIIntegration, "_authenticate", mock_authenticate) - monkeypatch.setattr(WordPressAPIIntegration, "_upload_file_to_api", mock_upload_file_to_api) - - api_integration = WordPressAPIIntegration(credentials) - result = api_integration.upload_file(valid_file_path) - - assert result is False diff --git a/prompt b/prompt deleted file mode 100644 index b7f4b816..00000000 --- a/prompt +++ /dev/null @@ -1,4 +0,0 @@ -Move all hard coded values from the modules and put the them in a config file. -Suggest functions that be improved upon on readibility, polymorphism and remove redundany. -Make the code conform to PEP standards. -Include try and except. Include exception at possible places. Include detailed excpetions and error messages. diff --git a/prompts/blog_ideas_prompts.md b/prompts/blog_ideas_prompts.md new file mode 100644 index 00000000..7d002413 --- /dev/null +++ b/prompts/blog_ideas_prompts.md @@ -0,0 +1,40 @@ +##################################################### +# +# Use below prompts to generate Idea or topics, titles to write on. +# +##################################################### + +# This is basically keyword research for a specific domain, narrowed down by blog topics. +# We can craft prompts to get an idea on what to generate blogs on. +# Divide them in topic and write for most searched ones, as below: + +------------------------------------------------------------------- + +--- Write seven subheadings for the blog article with the title [title]; the titles should be catchy and 60 characters max. + +--- List the top 5 most popular long tail keywords for the topic [YOUR TOPIC] + +--- What Are The {X} Most Popular Sub-topics Related To {Topic}? + +--- What Are The {X} Most Popular Sub-topics Related To {Sub-topic}? + +--- List Without Description The Top {X} Most Popular Keywords For The Topic Of {X} + +--- List Without Description The Top {X} Most Popular Long-tail Keywords For The Topic “{X}” + +--- List Without Description The Top Semantically Related Keywords And Entities For The Topic {X} + +--- Give me five popular keywords that include “SEO” in the word, and the following letter starts with a. Once the answer has been done, move on to giving five more popular keywords that include “SEO” for each letter of the alphabet b to z. + +--- For the topic of “{Topic}” list 10 keywords each for the different types of user personas + +--- Generate 50 keywords for the topic “[Topic]” that contain “vs” + +--- Perform the following steps in a consecutive order Step 1, Step 2, Step 3, Step 4. +Step 1 – Generate the 5 most popular keywords related to the topic of “keyword" with their search intent. +Step 2 – For each keyword provide 2 long-tail keywords. +Step 3 – Generate the 5 most popular questions that include those keywords. +Step 4 – Generate 5 blog article titles based on the keywords from Step 1 and Step 2. + +--- As a technical writer experienced in SEO, please create a detailed blog post outline that provides a step-by-step guide +for using [X], targeting beginners with a friendly and helpful tone and a desired length of 800-1000 words.