AI Post Generator

This software system represents a cutting-edge blend of artificial intelligence, web integration, and content management, primarily designed for automating and enhancing digital content creation and publishing. It’s a multi-faceted platform that leverages Python for backend processing, integrating various APIs and AI technologies to streamline the generation, manipulation, and distribution of digital content.

Core Components

  1. AI-Driven Content and Image Generation:
    • Utilizes OpenAI’s GPT model to automatically generate text content and image descriptions, showcasing capabilities in natural language processing and creative AI applications.
    • Employs image processing techniques to search, download, and manipulate images, further enhancing content with visual elements.
  2. Integration with Web Services:
    • Seamlessly integrates with WordPress for content publishing, and Instagram for social media engagement, demonstrating proficiency in RESTful API usage and social media platforms.
    • Implements Google and Unsplash API integrations for dynamic image sourcing, reflecting versatility in handling various web services.
  3. Advanced-Data Handling and Process Automation:
    • Manages content data through Excel workbooks, indicating skills in data organization and management.
    • Automates various aspects of content creation, from topic generation to post publishing, illustrating a high degree of process automation and efficiency.
  4. Modular Architecture and Security:
    • Exhibits a modular programming approach, enhancing code readability, maintainability, and scalability.
    • Emphasizes security and configuration best practices by externalizing sensitive information and using environment variables.

Technical Sophistication

The system stands out for its technical sophistication and innovative use of AI. It bridges the gap between content generation and digital publishing, making it a highly efficient tool for content creators and marketers. The integration of AI for text and image creation, coupled with automated publishing workflows, marks a significant advancement in automating and optimizing digital content strategy.

Implications for Digital Content Space

This platform is not just a technical achievement but also a strategic tool for the digital content space. It enables businesses and content creators to rapidly produce high-quality, AI-enhanced content, tailored to specific platforms like blogs and social media. This system is a testament to the power of combining AI with web technologies, paving the way for more intelligent and efficient content creation in the digital era.

This is an example of the posts generated from my Python script. A carousel or single post can be generated. The image displayed is based on an AI description generated from my specified topic. The caption is also generated from AI based on my topic. I program the watermark and shadow gradient in Python as well. Please see my scripts below for more information

import logging
from flask import Flask, request, jsonify
from flask_cors import CORS
from contentgenerator import ProcessPosts, Generatepost

# Setup logging
logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s %(levelname)s:%(message)s')

def generate_article():
    try:
        logging.info("Starting article generation")
        #generator = ProcessPosts()
        list = []
        generatordirect = Generatepost()
        processpost = ProcessPosts()
        result = processpost.process_post()
        # Additional code...
        logging.info("Article generation completed")
        #return jsonify(result)
    except Exception as e:
        logging.error("Error occurred: " + str(e))
        raise

if __name__ == '__main__':
    try:
        generate_article()
    except Exception as e:
        logging.error("Failed to generate article: " + str(e))

main.py

  • Functionality: This script acts as the entry point for a content generation application. It sets up logging to record application activities and errors, which is crucial for monitoring and debugging.
  • Complexity: The script demonstrates an understanding of exception handling, logging, and integration with external modules (contentgenerator.py), indicating familiarity with Python best practices and modular programming.
from email.mime import image
from nntplib import ArticleInfo
from pydoc_data.topics import topics
from urllib import request
from wordpress import wordPress
from config import UNSPLASH_API_KEY, WORDPRESS_PASSWORD, WORDPRESS_USERNAME,WORDPRESS_SITE_URL, GOOGLE_API_KEY, GOOGLE_CX, OPENAI_API_KEY, FONT, WATERMARK_PATH
import requests
import json
import random
import re
import pandas as pd
from aiassistant import OpenAIAssistant
from PIL import Image, ImageDraw, ImageFont, ImageOps, ImageFilter
import textwrap
import os

class Images:
    def __init__(self,openai_assistant):
        self.currentarticle = OpenAIAssistant()
        self.WpArticle = wordPress()
        self.offset = 0
        
        #self.google_api_key = google_api_key

        #self.google_cx = google_cx
        #self.unsplash_api_key = unsplash_api_key
    def search_google_images(query):

        url = "https://www.googleapis.com/customsearch/v1"
        params = {
            'key': GOOGLE_API_KEY,
            'cx': GOOGLE_CX,
            'q': query,
            'searchType': 'image',
            'imgSize': 'large',  # Set image size to large for higher quality images
            'num': 10,
        } # Number of results per request        }
        response = requests.get(url, params=params)
        if response.status_code == 200:
            results = json.loads(response.content)['items']
            if results:
                return random.choice(results)['link']
        return None    # Define a function to search for images on Unsplash based on the topic
    def search_unsplash_images(self,topics):
        headers = {
            'Authorization': f'Client-ID {UNSPLASH_API_KEY}',

            'Accept-Version': 'v1'
        }
        # Initialize empty lists to store featured image URLs and content image URLs
        featured_image_urls = []
        content_image_urls = []
        for topic in topics:
            # Search for an image for the featured image
            query = topic            
            url = f'https://api.unsplash.com/search/photos?query={query}&orientation=landscape'
            response = requests.get(url, headers=headers)

            if response.status_code == 200:
                results = json.loads(response.content)['results']

                if results:
                    # Select a random image from the results
                    featured_image_url = random.choice(results)['urls']['regular']
                    featured_image_urls.append(featured_image_url)
            else:

                featured_image_urls.append(None)
            # Search for an image to add to the content
            query = f'{topic}'
            url = f'https://api.unsplash.com/search/photos?query={query}&orientation=landscape'

            response = requests.get(url, headers=headers)
            if response.status_code == 200:
                results = json.loads(response.content)['results']

                if results:
                    # Select a random image from the results
                    content_image_url = random.choice(results)['urls']['regular']

                    content_image_urls.append(content_image_url)
                else:
                    content_image_urls.append(None)
            else:
                content_image_urls.append(None)
        return random.choice(featured_image_urls), random.choice(content_image_urls)
    def image_info_dictionary(self, article):              
        image_positions = {}
        # Adjusted regex pattern to match your image placeholders and descriptions
        image_pattern = r'\["?image\s*(\d+)"?\]\s*\["?([^"]+)"?\]'
        
        matches = list(re.finditer(image_pattern, article))
        print("Number of matches found:", len(matches))  # Debug print
        for match in matches:
            image_placeholder = match.group(1)
            image_description = match.group(2)
            position = match.start()

            image_positions[image_placeholder] = {
                "pos": position,
                "image_description": image_description,
                "matched_string": match.group(0)
            }
            print(f"Match: {match.group(0)}, Position: {position}, Description: {image_description}")  # Debug print

        return image_positions, article

    def insert_content_images(self, article, topic):
        print("Article before processing:", article)  # Debug print

        image_dictionary, updated_article = self.image_info_dictionary(article)

        print("Image Dictionary:", image_dictionary)  # Debug print

        for image_info in image_dictionary.values():
            pos = image_info["pos"] + self.offset
            matched_string = image_info["matched_string"]
            description = image_info["image_description"]

            # Debug prints
            print("Position:", pos)
            print("Description:", description)

            # Generate the image using the description
            image_url = self.currentarticle.generate_image(description)
            print("Generated Image URL:", image_url)

            # Insert the image URL into the article text at the specified position
            content_media_id, wp_content_image_url = self.WpArticle.upload_media_to_wordpress(image_url, topic)
            updated_article = updated_article[:pos] + f'<img src="{wp_content_image_url}">' + updated_article[pos + len(matched_string):]
            self.offset += len(f'<img src="{wp_content_image_url}">') - len(matched_string)

            print("Updated Article Segment:", updated_article[pos:pos+100])  # Print a segment of the updated article for verification

        return updated_article

    def insert_featured_images(self,img_keywords,topic):
        featured_image_url = self.currentarticle.generate_image(img_keywords)        
        #featured_image_url, content_image_url_notused = self.search_unsplash_images(img_keywords)
        #image_description = self.generate_image_description(article)
        #content_image_url=self.article.generate_image(image_description)
        featured_media_id, featured_image_url = self.WpArticle.upload_media_to_wordpress(featured_image_url, topic)
        return featured_media_id, featured_image_url
    

    def overlay_text_on_image(self, image_location, text, font_path=FONT, font_size=60, line_width=30, start_opacity=380, watermark_path=WATERMARK_PATH, padding=20):
        shadow_offset = 2
        if not os.path.exists(image_location):
            raise FileNotFoundError(f"Image file not found at {image_location}")

        img = Image.open(image_location).convert('RGBA')
        font = ImageFont.truetype(font_path, font_size)

        lines = textwrap.wrap(text, width=line_width)
        text_height = sum([font.getbbox(line)[3] for line in lines]) + 10 * (len(lines) - 1)

        # Add a gradual gradient to the bottom of the image
        bottom_gradient_height = text_height + 300
        img = self.add_gradient_to_image(img, bottom_gradient_height, start_opacity, gradient_color=(0, 0, 0))

        draw = ImageDraw.Draw(img)

        # Calculate the total height of the text
        text_total_height = sum([font.getbbox(line)[3] + 10 for line in lines[:-1]]) + font.getbbox(lines[-1])[3]
        # The starting y position of the text will be at the bottom minus the text height and padding
        y_text_start = img.height - text_total_height - padding
        y = y_text_start  # Adjust the vertical position as needed
        for line in lines:
            text_width = font.getbbox(line)[2]
            x = (img.width - text_width) // 2
            draw.text((x + shadow_offset, y + shadow_offset), line, font=font, fill=(0, 0, 0))  # Black shadow
            draw.text((x, y), line, font=font, fill="white")
            y += font.getbbox(line)[3] + 10

        # After drawing the text, overlay the watermark and horizontal lines
        if watermark_path:
            watermark_image = Image.open(watermark_path).convert('RGBA')
            watermark_image.thumbnail((150, 150))

            # Calculate the position of the watermark above the text
            watermark_x = (img.width - watermark_image.width) // 2
            watermark_y = y_text_start - watermark_image.height + 15
            # Draw horizontal lines around the watermark position
            line_thickness = 2
            line_color = (255, 255, 255)  # White color for the lines
            line_length = 100  # Adjust the length as needed
            line_y = watermark_y + watermark_image.height // 2

            draw.line([(padding, line_y), (watermark_x - line_length // 2, line_y)], fill=line_color, width=line_thickness)
            draw.line([(watermark_x + watermark_image.width + line_length // 2, line_y), (img.width - padding, line_y)], fill=line_color, width=line_thickness)

            # Paste the watermark image
            img.paste(watermark_image, (watermark_x, watermark_y), watermark_image)
            img = img.convert("RGB")

        return img
    
    def add_gradient_to_image(self,image, bottom_gradient_height, start_opacity, gradient_color=(0, 0, 0)):
        # Create a mask for the gradient
        mask = Image.new('L', (image.width, image.height), 0)  # Start with a fully transparent mask
        for y in range(bottom_gradient_height):
            # Calculate the opacity for the gradient
            # It starts from start_opacity at the bottom and reduces to 0 towards the top
            opacity = start_opacity * (bottom_gradient_height - y) / bottom_gradient_height
            mask.paste(int(opacity), (0, image.height - y - 1, image.width, image.height - y))

        # Create a shadow image with the specified gradient color
        shadow = Image.new('RGBA', (image.width, image.height), color=(*gradient_color, start_opacity))

        # Apply the gradient mask to the shadow
        shadow.putalpha(mask)

        # Composite the shadow and the image
        composite = Image.alpha_composite(image, shadow)
        return composite

image_handling.py

  • Core Responsibilities:
    • Image Search and Selection: Implements methods to search for images using Google and Unsplash APIs, showcasing skills in API integration and usage.
    • Image Processing: Contains functionalities for processing image information, overlaying text, and applying gradients, demonstrating an understanding of image manipulation techniques using the PIL library.
  • Complexity: The class shows complexity in handling various image sources, manipulating images, and integrating with external services. It also includes error handling and debugging prints for troubleshooting.
import pandas as pd
from wordpress import wordPress
from aiassistant import OpenAIAssistant
from image_handling import Images
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import requests
from gui import CustomDialog, show_post_dialog
import openpyxl
import json
import utils
import re
from config import DATABASE_LOCATION, POST_DATABASE, POSTS, IMAGE_LOCATION, FONT, META_ACCESS_TOKEN, INSTAGRAM_USER_ID
import os
import time
import textwrap
from instagram import Instagram

class Generatepost:

    def __init__(self):
        self.post = OpenAIAssistant()
        self.images = Images(self.post)
        self.wppost = wordPress()
        self.IGPost = Instagram()
        self.image_counter = 1  # Initialize a counter for image naming
    def generate_image_description(self, post_text):
        prompt = f"Based on the following post, provide a short description for a relevant image:\n\n{post_text}"
        return self.post.ask_gpt(prompt)

    def generate_post_img_desc(self, topic):
            prompt = f"""Provide an image description for Dalle to generate an interesting image on the topic '{topic}' in a python list format like: ["Image Description"] """
            response = self.post.ask_gpt(prompt)            
            try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
            except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []
            
                """ def generate_post_img_desc(self, topic):
                prompt = "Provide an image description for Dalle to generate an interesting image on the topic '{topic}' in a python list format like: ["Keyword1","Keyword2","Keyword3"] 
                response = self.post.ask_gpt(prompt)            
                try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
                except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []"""
                
    def generate_post_img_desc(self, topic):
            prompt = f"""Provide one short image description for Dalle to generate an interesting image on the topic '{topic}'. The description should be surrounded by brackets in a python list format like: ["an intersting desciription for featured image of post"] """
            response = self.post.ask_gpt(prompt)            
            try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
            except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []

    def generate_caption(self, topic):
        captionAI = OpenAIAssistant()
        prompt = f"Provide an inspiring and factual 150 to 1500 character paragraph on the topic, '{topic}'. This will be used as an Instagram caption. Your response should be surrounded by brackets in python list format such as: [this is the caption for instagram]."
        response = captionAI.ask_gpt(prompt)
        print(f"\nCaption is {response}\n")
        # Attempt to parse the response
        try:
            # Removing brackets and quotes, and trimming whitespace
            cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
            # Splitting the string into a list based on commas
            #category_list = cleaned_response.split(',')
            # Get the first item in the list as the category, or default to "Uncategorized"
            #category = category_list[0].strip() if category_list else "No Caption"
            cleaned_response = cleaned_response + "\nFollow us @NeuralNetNews for my Content like this!"
            return cleaned_response
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error appropriately
            return "No Caption"

    def generate_post(self,topic_list, topic):
            page_list = []
            original_image_directory, post_images_directory = self.create_folders(topic)
            if topic_list:
                page_list = topic_list
            else:
                page_list.append(topic)
            for page_item in page_list:
                image_description = self.generate_post_img_desc(topic)
                image_url = self.post.generate_image(image_description)
                #image_url = 'https://m.media-amazon.com/images/W/MEDIAX_792452-T2/images/I/71PQ8F9jyOL.jpg'

                # overlay image with txt
                # Overlay text on the image
                original_image = self.save_image(image_url,original_image_directory, topic)
                # Check if the post is less than 50 words
                image_with_text = self.images.overlay_text_on_image(original_image, page_item)
                
                # Save or store the image
                finished_image_path = self.save_image(image_with_text,post_images_directory, topic)
                
                
                # Insert images into the post
            caption = self.generate_caption(topic)
            media_id = self.IGPost.post_to_instagram(post_images_directory,topic,caption)
            return media_id
                #featured_media_id, _ = self.images.insert_featured_images(image_keywords, topic)
                #text
    def create_folders(self,topic):
        # Sanitize the topic name and limit it to 20 characters
        sanitized_topic = self.sanitize_topic_name(topic)[:20]
        # Create the top directory for the topic
        topic_directory = os.path.join(IMAGE_LOCATION, sanitized_topic).replace('\\', '/')
        os.makedirs(topic_directory, exist_ok=True)

        # Create subdirectories for both original and post images
        original_image_directory = os.path.join(topic_directory, "Original_Image").replace('\\', '/')
        post_images_directory = os.path.join(topic_directory, "Post_Images").replace('\\', '/')

        os.makedirs(original_image_directory, exist_ok=True)
        os.makedirs(post_images_directory, exist_ok=True)

        return original_image_directory, post_images_directory
    
    def overlay_text_on_image2(self, image_location, text, font_path=FONT, font_size=80, line_width=20):
        if not os.path.exists(image_location):
            raise FileNotFoundError(f"Image file not found at {image_location}")

        img = Image.open(image_location)
        draw = ImageDraw.Draw(img)
        font = ImageFont.truetype(font_path, font_size)
        shadow_offset = 2

        # Function to calculate the height of a single line of text
        def get_text_height(text_line):
            return font.getbbox(text_line)[3] - font.getbbox(text_line)[1]

        # Wrap text
        lines = textwrap.wrap(text, width=line_width)

        # Calculate text height
        text_height = sum([get_text_height(line) for line in lines])

        # Check if text height is too tall for the image, and reduce line width if necessary
        while text_height > img.height and line_width > 10:
            line_width -= 1
            lines = textwrap.wrap(text, width=line_width)
            text_height = sum([get_text_height(line) for line in lines])

        y = (img.height - text_height) / 2
        for line in lines:
            text_width = font.getlength(line)
            x = (img.width - text_width) / 2

            # Draw shadow or outline first
            draw.text((x + shadow_offset, y + shadow_offset), line, font=font, fill=(0, 0, 0))  # Black shadow
            # Draw main text
            draw.text((x, y), line, font=font, fill=(255, 255, 255))  # White text
            y += get_text_height(line)

        return img
    
    def save_image(self, image_input, image_folder, topic):
        image_name = self.generate_image_name(topic)
        image_path = os.path.join(image_folder, image_name).replace('\\', '/')

        # Check if the input is a URL or an Image object
        if isinstance(image_input, str):
            # Assuming it's a URL, download and save the image
            response = requests.get(image_input)
            if response.status_code == 200:
                image = Image.open(BytesIO(response.content))
            else:
                print(f"Failed to download image from {image_input}")
                return None
        elif isinstance(image_input, Image.Image):
            # If it's an Image object, use it directly
            image = image_input
        else:
            raise ValueError("Invalid image input type.")

        image.save(image_path)
        print(f"Image saved at {image_path}")
        return image_path
        
    def sanitize_topic_name(self, topic):
        # Replace spaces with underscores and remove special characters
        sanitized_name = ''.join(e for e in topic if e.isalnum() or e == ' ').replace(' ', '_')
        return sanitized_name

    def generate_image_name(self, topic):
        # Generate a unique image name using the topic and current time
        sanitized_topic = self.sanitize_topic_name(topic)
        timestamp = int(time.time())
        return f"{sanitized_topic}_{timestamp}.jpg"

    def _generate_title(self, topic):
        prompt = f"""Write me an interesting title for the {topic} in the form: ["title"]"""
        response = self.post.ask_gpt(prompt)
        try:
            # Removing brackets and quotes, and trimming whitespace
            cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
            # Splitting the string into a list based on commas
            title_list = cleaned_response.split(',')
            # Get the first item in the list as the category, or default to "Uncategorized"
            title = title_list[0].strip() if title_list else "No Title"
            return title
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error appropriately
            return "No Title"
        
    def _clean_post(self, post):
        # Add your cleaning logic here
        cleaned_post = post  # Replace this with actual cleaning operations
        return cleaned_post


class ProcessPosts:
    def __init__(self):
        self.topic_ai = OpenAIAssistant()
    
    def generate_topc(self, n_topics=1):
        topics = []
        for _ in range(n_topics):
            response = self.topic_ai.ask_gpt(self._topic_prompt())
            response_dict, error = utils.parse_json_string(utils.clean_response_string(response))

            if error:
                # Handle the error appropriately
                continue
            topics.append(response_dict)
            utils.append_data_to_excel(pd.DataFrame(response_dict), POSTS)
        return topics 
       

    def process_post(self):
        workbook, worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + POSTS, ["Topic"])
        database_workbook, database_worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + POST_DATABASE, ["Topic"])

        topics = utils.read_worksheet(worksheet)
        database_topics = utils.read_worksheet(database_worksheet)
        print(f"Topics: {topics}")
        print(f"Database Topics: {database_topics}")

        matching_topics = set(topics).intersection(database_topics)
        print(f"Matching set: {matching_topics}")
        utils.delete_matching_rows(worksheet, matching_topics)
        utils.save_workbook(workbook, DATABASE_LOCATION + POSTS)

        # Define delimiters for splitting topics
        delimiters = ["?"]

        # Process only the first topic
        topic_data = next(utils.iterate_rows(worksheet, ["Topic"]), None)
        if topic_data:
            original_topic = topic_data["Topic"]
            topic_list = []

            # Split the topic by defined delimiters
            split_topics = re.split('|'.join(map(re.escape, delimiters)), original_topic)

            for topic_page in split_topics:
                topic_page = topic_page.strip()  # Remove leading/trailing whitespace
                if topic_page and topic_page not in matching_topics:
                    topic_list.append(topic_page)

            if topic_list:
                # Generate and process post for the first topic
                self._generate_and_process_post(topic_list, original_topic, database_worksheet,database_workbook, worksheet,workbook)

        utils.save_workbook(database_workbook, DATABASE_LOCATION + POST_DATABASE)


    def _topic_prompt(self):
        return("""
            create a nested dictionary of interesting topics for POSTS, 
            keywords for images to represent that post, and a 
            category for that post in the format shown below:
            topic_dict = {
                "topic1": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic2": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic3": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                }
            }
            please don't write anything else but the dictionary
            """)

    def _generate_and_process_post(self, topic_list, topic, database_worksheet, database_workbook, worksheet, workbook):
        post_generator = Generatepost()
        PostID = post_generator.generate_post(topic_list, topic)
        if PostID:
            # Append the topic to the database worksheet
            database_worksheet.append([topic])
            print(f"Post for topic '{topic}' has been generated and posted with ID {PostID}.")

            # Save changes to the database workbook
            utils.save_workbook(database_workbook, DATABASE_LOCATION + POST_DATABASE)

            # Delete the row from the posttopics worksheet that was used to generate the topic and save changes
            utils.delete_topic_row(worksheet, topic)
            utils.save_workbook(workbook, DATABASE_LOCATION + POSTS)

contentgenerator.py

  • Key Components:
    • Image and Post Generation: The Generatepost class generates image descriptions using OpenAI’s GPT model, downloads images, and overlays text on them. This class is central to creating visually appealing content for posts.
    • Post Processing: ProcessPosts handles the generation and management of topics for posts. It interacts with Excel workbooks for storing and managing data, which indicates proficiency in data manipulation and storage.
  • Complexity: The script demonstrates advanced capabilities in using AI for content creation, handling media files, and automating content management processes.
from PIL import Image, ImageDraw, ImageFont
from config import FONT, INSTAGRAM_PASSWORD, INSTAGRAM_USER_ID, META_ACCESS_TOKEN
import textwrap
import requests
import json
import os
from wordpress import wordPress

class Instagram:
    def __init__(self):
        self.wp = wordPress()

    def post_to_instagram(self, image_folder,topic,caption):
        try:
            media_object_ids = []



            is_carousel = len(os.listdir(image_folder)) > 1

            for image_file in os.listdir(image_folder):
                image_path = os.path.join(image_folder, image_file)
                if os.path.isfile(image_path) and image_path.lower().endswith(('.png', '.jpg', '.jpeg')):
                    wordpress_image_id, wordpress_image_url = self.wp.upload_media_to_wordpress(image_path)
                    if wordpress_image_url:
                        media_object_id = self.create_media_object(wordpress_image_url, caption, is_carousel)
                        if media_object_id:
                            media_object_ids.append(media_object_id)

            if not media_object_ids:
                return {'error': 'No media objects created'}

            if len(media_object_ids) == 1:
                return self.publish_single_image(media_object_ids[0])
            else:
                return self.publish_carousel(media_object_ids,caption)
            
    

        except Exception as e:
            print('Exception:', str(e))
            return {'error': 'General Exception', 'details': str(e)}
    
    def publish_single_image(self, container_id):
        url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media_publish"
        payload = {
            'creation_id': container_id,
            'access_token': META_ACCESS_TOKEN
        }
        response = requests.post(url, data=payload)
        if response.status_code == 200:
            return response.json()
        else:
            print("Failed to publish single image:", response.text)
            return {'error': 'Failed to publish single image', 'details': response.json()}
    
    def publish_carousel(self, container_ids, caption):
        carousel_container_id = self.create_carousel_container(container_ids, caption)
        if not carousel_container_id:
            return {'error': 'Failed to create carousel container'}

        url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media_publish"
        payload = {
            'creation_id': carousel_container_id,
            'access_token': META_ACCESS_TOKEN
        }
        response = requests.post(url, data=payload)
        if response.status_code == 200:
            return response.json()
        else:
            print("Failed to publish carousel:", response.text)
            return {'error': 'Failed to publish carousel', 'details': response.json()}

    def create_carousel_container(self, item_container_ids, caption):
        url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media"
        payload = {
            'media_type': 'CAROUSEL',
            'children': ','.join(item_container_ids),
            'caption': caption,
            'access_token': META_ACCESS_TOKEN
        }
        response = requests.post(url, data=payload)
        if response.status_code == 200:
            return response.json()['id']
        else:
            print("Failed to create carousel container:", response.text)
            return None

    def create_media_object(self, image_url, caption, is_carousel_item):
        create_url = f"https://graph.facebook.com/v18.0/{INSTAGRAM_USER_ID}/media"
        create_payload = {
            'image_url': image_url,
            'caption': caption,
            'is_carousel_item': is_carousel_item,
            'access_token': META_ACCESS_TOKEN
        }
        create_response = requests.post(create_url, data=create_payload)
        if create_response.status_code == 200:
            return create_response.json()['id']
        else:
            print("Failed to create media object:", create_response.text)
            return None

# Example usage
#instagram = Instagram()
#instagram.post_to_instagram('C:/path/to/image/folder/', 'Your caption here')

instagram.py

  • Purpose: Manages the posting of images and carousels to Instagram. It includes functionalities to create media objects and publish them using Instagram’s Graph API.
  • Complexity: Demonstrates in-depth knowledge of social media API integration and content publishing. It also shows error handling and response management skills.
import requests
import base64
import json
from io import BytesIO
from PIL import Image
import os
from config import WORDPRESS_API_PASSWORD, WORDPRESS_PASSWORD, WORDPRESS_EMAIL, WORDPRESS_SITE_URL, WORDPRESS_USERNAME


#Word Press Integration Class
class wordPress:
    def __init__(self):
        self.username = WORDPRESS_USERNAME  # Replace with your WordPress email
        self.password = WORDPRESS_PASSWORD           # Replace with your WordPress password
        self.site_url = WORDPRESS_SITE_URL # Replace with your WordPress site URL
        self.credentials = f"{self.username}:{self.password}"
        self.token = base64.b64encode(self.credentials.encode())
        self.headers = {"Authorization": f"Basic {self.token.decode('utf-8')}"}

    def category_exists(self, category_name):
        # Check if the category already exists
        url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories?search={category_name}'
        response = requests.get(url, headers=self.headers)
        if response.status_code == 200:
            categories = json.loads(response.content)
            if categories:
                return categories[0]['id']
        return None
    def create_category(self,category_name):
        # Create a new category
        url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories'
        data = {
            'name': category_name        }
        response = requests.post(url, headers=self.headers, json=data)
        if response.status_code == 201:
            category_id = json.loads(response.content)['id']
            print(f'Category created with ID: {category_id}')
            return category_id
        return None

    def get_category_id(self,category_name):
        # Get the ID of the category
        category_id = self.category_exists(category_name)
        if category_id:
            return category_id
        else:
            return self.create_category(category_name)
        
    def upload_media_to_wordpress(self, image_source):
            try:
                # Check if the image source is a URL or a local file path
                if image_source.startswith('http://') or image_source.startswith('https://'):
                    # Download the image from the URL
                    response = requests.get(image_source)
                    response.raise_for_status()
                    image = Image.open(BytesIO(response.content))
                    buffer = BytesIO()
                    image.convert('RGB').save(buffer, format="JPEG")
                    buffer.seek(0)
                    file_data = buffer.getvalue()
                    file_name = 'image.jpg'
                else:
                    # Open the local image file
                    with open(image_source, 'rb') as image_file:
                        file_data = image_file.read()
                        file_name = os.path.basename(image_source)

                # Prepare the data and files payload for the POST request
                data = {
                    "media_type": "image/jpeg",
                }
                files = {'file': (file_name, file_data, 'image/jpeg', {'Expires': '0'})}

                # Post the image to WordPress
                upload_response = requests.post(f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/media', headers=self.headers, data=data, files=files)
                upload_response.raise_for_status()

                # Parse the response
                if upload_response.status_code == 201:
                    media_id = upload_response.json()['id']
                    media_url = upload_response.json()['guid']['rendered']
                    print(f'Featured image uploaded with ID: {media_id}')
                    return media_id, media_url
                else:
                    print(f"Failed to upload media: {upload_response.text}")
                    return None, None

            except requests.RequestException as e:
                print(f"Request failed: {e}")
                return None, None     
            
    def create_post(self, title, content, category_id, featured_media_id):
        # Create a post on the WordPress site
        url = f"{self.site_url}/wp-json/wp/v2/posts"
        data = {
            "title": title,
            "content": content,
            "status": "publish",
            "categories": [category_id],
            "featured_media": featured_media_id,
        }
        response = requests.post(url, headers=self.headers, json=data)
        if response.status_code == 201:
            print(json.loads(response.content))
            post_id = json.loads(response.content)["id"]
            post_url = json.loads(response.content)["guid"]["rendered"]
            print(f"\nPost created with ID: {post_id}")
            print(f"\nPost created with url: {post_url}")
            return post_id, post_url
        else:
            print(f"Failed to create post. Status Code: {response.status_code}")
            print(f"Response: {response.content}")
            return None
    

wordpress.py

  • Responsibilities:
    • WordPress Interaction: Handles various operations like checking for existing categories, creating new categories, uploading media, and creating posts on a WordPress site.
    • API Interaction: Utilizes WordPress REST API for these operations, showcasing skills in RESTful API interaction and authentication handling.
  • Complexity: Illustrates proficiency in CMS (Content Management System) integration and automation of content publishing processes.

More Projects