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
- 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.
- 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.
- 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.
- 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.
- Image and Post Generation: The
- 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.