diff --git a/README.md b/README.md
index 3bd226f5..b0d0b2d4 100644
--- a/README.md
+++ b/README.md
@@ -34,14 +34,15 @@ If you have π» Laptop + π Internet + 10 minutes, you will be generating blo
2). pip install -U -r requirements.txt
3). streamlit run alwrity.py
```
+
---
Still stuck, [Open issue here](https://github.com/AJaySi/AI-Writer/issues) & Someone will bail you out.
---
-
-
-
-
+
+
+
+

### AI Tools & Features of Alwrity:
| No. | Alwrity Tool | Description |
@@ -72,7 +73,7 @@ Still stuck, [Open issue here](https://github.com/AJaySi/AI-Writer/issues) & Som
# AI Content Generation Toolkit - Alwrity

-
+(Click to Read Details of each available features)[https://github.com/AJaySi/AI-Writer/wiki/ALwrity-Interface-first-page-explanation]
## Introduction
Alwrity automates and enhances the process of content creation, optimization, and management(Really ?).
diff --git a/lib/ai_seo_tools/TBD b/lib/ai_seo_tools/TBD
index a8515fbe..f507a6c0 100644
--- a/lib/ai_seo_tools/TBD
+++ b/lib/ai_seo_tools/TBD
@@ -1 +1,7 @@
https://pypi.org/project/textstat/
+https://github.com/greghub/website-launch-checklist
+https://github.com/marcobiedermann/search-engine-optimization
+https://developers.google.com/speed/docs/insights/v5/get-started
+https://developers.google.com/search/apis/indexing-api/v3/prereqs
+https://developer.chrome.com/docs/lighthouse/overview/#cli
+
diff --git a/lib/ai_seo_tools/content_title_generator.py b/lib/ai_seo_tools/content_title_generator.py
new file mode 100644
index 00000000..bdbadc3f
--- /dev/null
+++ b/lib/ai_seo_tools/content_title_generator.py
@@ -0,0 +1,127 @@
+import time #Iwish
+import os
+import json
+import streamlit as st
+from tenacity import (
+ retry,
+ stop_after_attempt,
+ wait_random_exponential,
+)
+import google.generativeai as genai
+
+from ..gpt_providers.text_generation.main_text_generation import llm_text_gen
+
+
+def ai_title_generator():
+ """ Use AI to personalize content title generation """
+ st.title("βοΈ Alwrity - AI Blog Title Generator")
+
+ # Input section
+ with st.expander("**PRO-TIP** - Follow the steps below for best results.", expanded=True):
+ col1, col2 = st.columns([5, 5])
+
+ with col1:
+ input_blog_keywords = st.text_input(
+ '**π Enter main keywords of your blog!**',
+ placeholder="e.g., AI tools, digital marketing, SEO",
+ help="Use 2-3 words that best describe the main topic of your blog."
+ )
+ input_blog_content = st.text_area(
+ '**π Copy/Paste your entire blog content.** (Optional)',
+ placeholder="e.g., Content about the importance of AI in digital marketing...",
+ help="Paste your full blog content here for more accurate title suggestions. This is optional."
+ )
+
+ with col2:
+ input_title_type = st.selectbox(
+ 'π Blog Type',
+ ('General', 'How-to Guides', 'Tutorials', 'Listicles', 'Newsworthy Posts', 'FAQs', 'Checklists/Cheat Sheets'),
+ index=0
+ )
+ input_title_intent = st.selectbox(
+ 'π Search Intent',
+ ('Informational Intent', 'Commercial Intent', 'Transactional Intent', 'Navigational Intent'),
+ index=0
+ )
+ language_options = ["English", "Spanish", "French", "German", "Chinese", "Japanese", "Other"]
+ input_language = st.selectbox(
+ 'π Select Language',
+ options=language_options,
+ index=0,
+ help="Choose the language for your blog title."
+ )
+ if input_language == "Other":
+ input_language = st.text_input(
+ 'Specify Language',
+ placeholder="e.g., Italian, Dutch",
+ help="Specify your preferred language."
+ )
+
+ # Generate Blog Title button
+ if st.button('**Generate Blog Titles**'):
+ with st.spinner("Generating blog titles..."):
+ if input_blog_content == 'Optional':
+ input_blog_content = None
+
+ if not input_blog_keywords and not input_blog_content:
+ st.error('**π«£ Provide Inputs to generate Blog Titles. Either Blog Keywords OR content is required!**')
+ else:
+ blog_titles = generate_blog_titles(input_blog_keywords, input_blog_content, input_title_type, input_title_intent, input_language)
+ if blog_titles:
+ st.subheader('**π©π§π¬ Go Rule search ranking with these Blog Titles!**')
+ with st.expander("**Final - Blog Titles Output πππ**", expanded=True):
+ st.markdown(blog_titles)
+ else:
+ st.error("π₯ **Failed to generate blog titles. Please try again!**")
+
+
+# Function to generate blog metadesc
+def generate_blog_titles(input_blog_keywords, input_blog_content, input_title_type, input_title_intent, input_language):
+ """ Function to call upon LLM to get the work done. """
+ # If keywords and content both are given.
+ if input_blog_content and input_blog_keywords:
+ prompt = f"""As a SEO expert, I will provide you with main 'blog keywords' and 'blog content'.
+ Your task is write 5 SEO optimised blog titles, from given blog keywords and content.
+
+ Follow the below guidelines for generating the blog titles:
+ 1). As SEO expert, follow all best practises for SEO optimised blog titles.
+ 2). Your response should be optimised around given keywords and content.
+ 3). Optimise your response for web search intent {input_title_intent}.
+ 4). Optimise your response for blog type of {input_title_type}.
+ 5). Your blog titles should in {input_language} language.\n
+
+ blog keywords: '{input_blog_keywords}'\n
+ blog content: '{input_blog_content}'
+ """
+ elif input_blog_keywords and not input_blog_content:
+ prompt = f"""As a SEO expert, I will provide you with main 'keywords' of a blog.
+ Your task is write 5 SEO optimised blog titles from given blog keywords.
+
+ Follow the below guidelines for generating the blog titles:
+ 1). As SEO expert, follow all best practises for SEO optimised blog titles.
+ 2). Your response should be optimised around given keywords and content.
+ 3). Optimise your response for web search intent {input_title_intent}.
+ 4). Optimise your response for blog type of {input_title_type}.
+ 5). Your blog titles should in {input_language} language.\n
+
+ blog keywords: '{input_blog_keywords}'\n
+ """
+ elif input_blog_content and not input_blog_keywords:
+ prompt = f"""As a SEO expert, I will provide you with a 'blog content'.
+ Your task is write 5 SEO optimised blog titles from given blog content.
+
+ Follow the below guidelines for generating the blog titles:
+ 1). As SEO expert, follow all best practises for SEO optimised blog titles.
+ 2). Your response should be optimised around given keywords and content.
+ 3). Optimise your response for web search intent {input_title_intent}.
+ 4). Optimise your response for blog type of {input_title_type}.
+ 5). Your blog titles should in {input_language} language.\n
+
+ blog content: '{input_blog_content}'\n
+ """
+
+ try:
+ response = llm_text_gen(prompt)
+ return response
+ except Exception as err:
+ st.error(f"Exit: Failed to get response from LLM: {err}")
diff --git a/lib/ai_seo_tools/meta_desc_generator.py b/lib/ai_seo_tools/meta_desc_generator.py
new file mode 100644
index 00000000..404d5efe
--- /dev/null
+++ b/lib/ai_seo_tools/meta_desc_generator.py
@@ -0,0 +1,84 @@
+import time #Iwish
+import os
+import json
+import streamlit as st
+from tenacity import (
+ retry,
+ stop_after_attempt,
+ wait_random_exponential,
+)
+import google.generativeai as genai
+from ..gpt_providers.text_generation.main_text_generation import llm_text_gen
+
+
+def metadesc_generator_main():
+
+ # Title and description
+ st.title("βοΈ Alwrity - AI Blog Meta Description Generator")
+
+ # Input section
+ with st.expander("**PRO-TIP** - Read the instructions below. π", expanded=True):
+ col1, col2, space = st.columns([5, 5, 0.5])
+ with col1:
+ keywords = st.text_input("π Target Keywords (comma-separated):",
+ placeholder="e.g., content marketing, SEO, social media, online business",
+ help="Enter your target keywords, separated by commas. π")
+
+ tone_options = ["Informative", "Engaging", "Humorous", "Intriguing", "Playful"]
+ tone = st.selectbox("π¨ Desired Tone (optional):",
+ options=["General"] + tone_options,
+ help="Choose the overall tone you want for your meta description. π")
+ with col2:
+ search_type = st.selectbox('π Search Intent:',
+ ('Informational Intent', 'Commercial Intent', 'Transactional Intent', 'Navigational Intent'),
+ index=0)
+
+ language_options = ["English", "Spanish", "French", "German", "Other"]
+ language_choice = st.selectbox("π Preferred Language:",
+ options=language_options,
+ help="Select the language for your meta description. π£οΈ")
+ if language_choice == "Other":
+ language = st.text_input("Specify Other Language:",
+ placeholder="e.g., Italian, Chinese",
+ help="Enter your preferred language. π")
+ else:
+ language = language_choice
+
+ # Generate Blog Title button
+ if st.button('**β¨ Generate Meta Description β¨**'):
+ with st.spinner("Crafting your Meta descriptions... β³"):
+
+ # Validate input fields
+ if not keywords:
+ st.error('**π«£ Blog Keywords are required!**')
+ else:
+ blog_metadesc = generate_blog_metadesc(keywords, tone, search_type, language)
+ if blog_metadesc:
+ st.subheader('**π Your SEO-Boosting Blog Meta Descriptions! π**')
+ with st.expander("**Final - Blog Meta Description Output ππ**", expanded=True):
+ st.markdown(blog_metadesc)
+ else:
+ st.error("π₯ **Failed to generate blog meta description. Please try again!**")
+
+
+# Function to generate blog metadesc
+def generate_blog_metadesc(keywords, tone, search_type, language):
+ """ Function to call upon LLM to get the work done. """
+ prompt = f"""
+ Craft 3 engaging and SEO-friendly meta descriptions for a blog post based on the following details:
+
+ Blog Post Keywords: {keywords}
+ Search Intent Type: {search_type}
+ Desired Tone: {tone}
+ Preferred Language: {language}
+
+ Output Format:
+
+ Respond with 3 compelling and concise meta descriptions, approximately 155-160 characters long, that incorporate the target keywords, reflect the blog post content, resonate with the target audience, and entice users to click through to read the full article.
+ """
+ with st.spinner("Calling Gemini to craft 3 Meta descriptions for you... π«"):
+ try:
+ response = llm_text_gen(prompt)
+ return response
+ except Exception as err:
+ st.error(f"Exit: Failed to get response from LLM: {err}")
diff --git a/lib/ai_seo_tools/twitter_opengraph_generator.py b/lib/ai_seo_tools/twitter_opengraph_generator.py
new file mode 100644
index 00000000..363d8622
--- /dev/null
+++ b/lib/ai_seo_tools/twitter_opengraph_generator.py
@@ -0,0 +1,115 @@
+import streamlit as st
+import openai
+from bs4 import BeautifulSoup
+import requests
+import os
+
+# Set up OpenAI API key
+openai.api_key = os.getenv('OPENAI_API_KEY') # Assuming you have it in a .env file
+
+# Placeholder function for web scraping the URL (to be replaced with your mechanism)
+def scrape_webpage(url):
+ try:
+ response = requests.get(url)
+ response.raise_for_status() # Raise an exception for bad status codes
+ soup = BeautifulSoup(response.content, 'html.parser')
+
+ # Extract the title
+ title = soup.find('title').text.strip() if soup.find('title') else None
+
+ # Extract the description
+ description = soup.find('meta', attrs={'name': 'description'})['content'] if soup.find('meta', attrs={'name': 'description'}) else None
+
+ # Extract the image URL
+ image_url = soup.find('meta', attrs={'property': 'og:image'})['content'] if soup.find('meta', attrs={'property': 'og:image'}) else None
+
+ return title, description, image_url
+
+ except requests.exceptions.RequestException as e:
+ st.error(f"An error occurred while scraping the webpage: {e}")
+ return None, None, None
+
+# Function to generate OpenGraph meta tags
+def generate_opengraph_meta_tags(url, html_content=None, title=None, description=None, image_url=None):
+ ai_prompt = f"""
+ Generate OpenGraph meta tags for a Twitter Card for the following webpage: {url}
+
+ Here is the HTML content of the page:
+ ```html
+ {html_content}
+ ```
+
+ Optional Customizations:
+ * Title: {title if title else "Infer from the page content"}
+ * Description: {description if description else "Infer from the page content"}
+ * Image URL: {image_url if image_url else "Infer from the page content"}
+
+ Best practices:
+ - Title: Should be concise and informative.
+ - Description: Provide a brief summary of the content.
+ - Image URL: Use a high-quality image.
+ - URL: Ensure the URL is correct.
+ - Type: Specify the content type (e.g., article, website).
+ - Site Name: Provide the name of the site.
+
+ Output the generated OpenGraph meta tags as a series of HTML meta tags in the following format:
+
+
+
+
+
+
+
+ """
+
+ response = openai.Completion.create(
+ engine="text-davinci-003",
+ prompt=ai_prompt,
+ max_tokens=150
+ )
+
+ return response.choices[0].text.strip()
+
+# Function to display the Streamlit UI
+def display_ui():
+ st.title('OpenGraph Meta Tags Generator for Twitter π¦')
+ st.write('Generate OpenGraph meta tags for Twitter cards with ease. Just provide a URL and let our tool do the rest!')
+
+ # User inputs
+ url = st.text_input('Content URL', help="π Paste the URL of the page you want to share on Twitter.", placeholder="e.g., https://example.com")
+ title = st.text_input('Title (optional)', help="π Optionally, provide a custom title for your Twitter card.", placeholder="e.g., Amazing Blog Post")
+ description = st.text_area('Description (optional)', help="βοΈ Optionally, provide a custom description for your Twitter card.", placeholder="e.g., This blog post covers...")
+ image_url = st.text_input('Image URL (optional)', help="πΈ Optionally, provide a URL to an image for your Twitter card.", placeholder="e.g., https://example.com/image.jpg")
+
+ if st.button('Generate Meta Tags'):
+ if url:
+ # Scrape the webpage for missing information if not provided
+ if not title or not description or not image_url:
+ scraped_title, scraped_description, scraped_image_url = scrape_webpage(url)
+ title = title or scraped_title
+ description = description or scraped_description
+ image_url = image_url or scraped_image_url
+
+ # Fetch HTML content
+ html_content = requests.get(url).text
+
+ # Generate OpenGraph meta tags
+ meta_tags = generate_opengraph_meta_tags(url, html_content=html_content, title=title, description=description, image_url=image_url)
+
+ st.subheader('Generated OpenGraph Meta Tags')
+ st.code(meta_tags, language='html')
+ else:
+ st.error('Please provide a content URL.')
+
+ # Instructions and preview
+ st.write('### Instructions')
+ st.write('1. Enter the URL of the content you want to share on Twitter.')
+ st.write('2. Optionally, provide a custom title, description, and image URL.')
+ st.write('3. Click "Generate Meta Tags" to see the generated OpenGraph meta tags.')
+
+ st.write('### Preview')
+ st.write('A preview of how the Twitter card will look with the generated meta tags will be shown here (Feature to be implemented).')
+
+# Run the Streamlit UI
+if __name__ == '__main__':
+ display_ui()
diff --git a/lib/utils/alwrity_utils.py b/lib/utils/alwrity_utils.py
index bf9e8e7e..fcb03af1 100644
--- a/lib/utils/alwrity_utils.py
+++ b/lib/utils/alwrity_utils.py
@@ -37,6 +37,8 @@ from lib.ai_writers.web_url_ai_writer import blog_from_url
from lib.ai_writers.image_ai_writer import blog_from_image
from lib.ai_writers.ai_essay_writer import ai_essay_generator
from lib.ai_seo_tools.seo_structured_data import ai_structured_data
+from lib.ai_seo_tools.content_title_generator import ai_title_generator
+from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main
from lib.gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image
from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_planner
@@ -115,13 +117,19 @@ def ai_seo_tools():
""" Collection SEO tools for content creators. """
options = [
"Generate Structured Data - Rich Snippet",
- "AI SEO Audit",
+ "Generate SEO optimized Blog Titles",
+ "Generate Meta Description for SEO",
"Quit"
]
choice = st.selectbox("**πSelect AI SEO Tool:**", options, index=0, format_func=lambda x: f"π {x}")
if choice == "Generate Structured Data - Rich Snippet":
ai_structured_data()
+ elif choice == "Generate Meta Description for SEO":
+ metadesc_generator_main()
+ elif choice == "Generate SEO optimized Blog Titles":
+ ai_title_generator()
+
def blog_from_keyword():
diff --git a/lib/workspace/my_content_team/ai_podcast_team/part1.py b/lib/workspace/my_content_team/ai_podcast_team/part1.py
new file mode 100644
index 00000000..04edb6a4
--- /dev/null
+++ b/lib/workspace/my_content_team/ai_podcast_team/part1.py
@@ -0,0 +1,25 @@
+{
+ "role": "Topic Intake and Specification Agent (TISA)",
+ "purpose": "To gather and clarify user inputs, create a structured brief, and identify existing content related to the chosen topic.",
+ "tasks": [
+ "Collect detailed information from the user, including the topic, target audience, desired tone, industry specifics, and format.",
+ "Formulate a comprehensive and clear brief for the Research Agent.",
+ "Search for existing content on the chosen topic to provide context and starting points for the Research Agent."
+ ],
+ "goals": [
+ "Ensure that the userβs ideas and preferences are clearly understood and documented.",
+ "Provide a well-structured brief that guides the subsequent agents in the workflow.",
+ "Inform the user and research agent about existing content to enhance the research process."
+ ],
+ "backstory": "TISA was developed to streamline the initial phase of podcast creation, recognizing that clear and detailed input is critical for high-quality output. Designed with an empathetic approach, TISA aims to bridge the gap between user ideas and technical execution by providing a structured starting point.",
+ "skills": [
+ "Effective communication for clarifying user requirements.",
+ "Structuring detailed and comprehensive briefs.",
+ "Content search and identification."
+ ],
+ "tools": [
+ "Interactive forms for user input.",
+ "Predefined templates for brief formulation.",
+ "Search engines and content discovery tools."
+ ]
+}
diff --git a/lib/workspace/my_content_team/ai_podcast_team/part2.py b/lib/workspace/my_content_team/ai_podcast_team/part2.py
new file mode 100644
index 00000000..2de7a7ef
--- /dev/null
+++ b/lib/workspace/my_content_team/ai_podcast_team/part2.py
@@ -0,0 +1,24 @@
+{
+ "role": "Research Agent (RA)",
+ "purpose": "To conduct comprehensive research based on the provided brief and organize findings into a clear summary.",
+ "tasks": [
+ "Gather information from various sources, including websites, articles, books, research papers, and social media.",
+ "Use knowledge graphs and structured data tools (e.g., Google Knowledge Graph, Wikidata) to enhance research efficiency.",
+ "Summarize research findings in a concise and organized manner, highlighting key points, statistics, and relevant insights."
+ ],
+ "goals": [
+ "Provide accurate, relevant, and comprehensive research to inform the scriptwriting process.",
+ "Present information in a format that is easy to understand and use for the scriptwriting agent."
+ ],
+ "backstory": "RA was designed to tackle the challenge of information overload by filtering and prioritizing data. Built with advanced algorithms and access to a wide range of databases, RA ensures that the podcast content is well-informed and backed by reliable sources.",
+ "skills": [
+ "Efficient information retrieval.",
+ "Summarizing and organizing data.",
+ "Knowledge graph integration (optional)."
+ ],
+ "tools": [
+ "Web scraping tools.",
+ "Industry-specific databases.",
+ "Content summarization tools."
+ ]
+}
diff --git a/lib/workspace/my_content_team/ai_podcast_team/part3.py b/lib/workspace/my_content_team/ai_podcast_team/part3.py
new file mode 100644
index 00000000..e8463529
--- /dev/null
+++ b/lib/workspace/my_content_team/ai_podcast_team/part3.py
@@ -0,0 +1,24 @@
+{
+ "role": "Script Writing Agent (SWA)",
+ "purpose": "To transform the research summary and brief into a detailed, engaging, and balanced podcast script.",
+ "tasks": [
+ "Create a script with distinct dialogues for three speakers, ensuring balanced contributions and diverse perspectives.",
+ "Maintain the specified tone and tailor the language to the target audience.",
+ "Incorporate relevant industry terminology while avoiding unnecessary jargon."
+ ],
+ "goals": [
+ "Produce a compelling and coherent podcast script that meets the userβs specifications.",
+ "Ensure that each speakerβs dialogue is engaging and well-balanced.",
+ "Adapt the script to the desired tone and audience preferences."
+ ],
+ "backstory": "SWA was created to bring creativity and structure to the podcast creation process. Leveraging natural language processing and dialogue generation capabilities, SWA ensures that the podcast script is not only informative but also engaging and well-paced.",
+ "skills": [
+ "Storytelling and dialogue generation.",
+ "Integrating research findings into a coherent script.",
+ "Character development (optional)."
+ ],
+ "tools": [
+ "AI writing tools like ChatGPT and Jasper.",
+ "Dialogue generation tools."
+ ]
+}
diff --git a/lib/workspace/my_content_team/ai_podcast_team/part4.py b/lib/workspace/my_content_team/ai_podcast_team/part4.py
new file mode 100644
index 00000000..9f3f6615
--- /dev/null
+++ b/lib/workspace/my_content_team/ai_podcast_team/part4.py
@@ -0,0 +1,24 @@
+{
+ "role": "Review and Revision Agent (RRA)",
+ "purpose": "To evaluate the script for clarity, engagement, accuracy, and adherence to the userβs requirements, and to suggest improvements.",
+ "tasks": [
+ "Thoroughly review the script for coherence, logical flow, and engagement.",
+ "Fact-check the script using advanced AI tools (e.g., Google Search Console, fact-checking APIs) to ensure accuracy.",
+ "Suggest revisions and improvements to enhance the scriptβs overall quality."
+ ],
+ "goals": [
+ "Ensure that the final script is clear, accurate, and engaging.",
+ "Make necessary edits to meet the userβs requirements and preferences.",
+ "Provide feedback that helps improve the scriptβs content and flow."
+ ],
+ "backstory": "RRA was designed to add a critical layer of quality control to the podcast creation process. With a focus on accuracy and engagement, RRA combines grammar and style checking with advanced fact-checking capabilities to refine and polish the script.",
+ "skills": [
+ "Grammar and style checking.",
+ "Fact-checking and tone analysis.",
+ "AI-assisted fact-checking (optional)."
+ ],
+ "tools": [
+ "Grammarly, QuillBot for grammar and style.",
+ "Fact-checking tools."
+ ]
+}