-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdaily_ai_blog.py
351 lines (304 loc) · 12.8 KB
/
daily_ai_blog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
import os
import sys
import openai
import keyring
import logging
import subprocess
import requests
import json
from datetime import datetime
from dotenv import load_dotenv
from gnews import GNews
from wordpress_xmlrpc import Client, WordPressPost
from wordpress_xmlrpc.methods.posts import NewPost
# Load environment variables
load_dotenv()
# Set up logging
logging.basicConfig(
filename='daily_ai_blog.log',
level=logging.INFO,
format='%(asctime)s %(levelname)s:%(message)s'
)
# Retrieve API keys and credentials securely
OPENAI_API_KEY = keyring.get_password('openai', 'api_key')
WORDPRESS_URL = os.getenv('WORDPRESS_URL')
WORDPRESS_USERNAME = os.getenv('WORDPRESS_USERNAME')
WORDPRESS_PASSWORD = keyring.get_password('wordpress', 'application_password')
# Validate credentials
if not all([OPENAI_API_KEY, WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_PASSWORD]):
logging.error("Missing credentials. Please ensure all credentials are set.")
sys.exit("Missing credentials. Please ensure all credentials are set.")
def get_trending_ai_topics(num_topics=5):
"""Fetch trending AI topics using GNews."""
try:
google_news = GNews(language='en', country='US', max_results=num_topics)
news = google_news.get_news('artificial intelligence')
topics = [item for item in news]
logging.info(f"Fetched {len(topics)} trending topics.")
return topics
except Exception as e:
logging.error(f"Failed to fetch trending topics: {e}")
return []
def rank_and_filter_topics(topics):
"""Rank topics based on keyword matching scores without strict filtering."""
# Keywords to identify relevant topics
relevant_keywords = [
'tool', 'launch', 'release', 'announce', 'develop', 'create',
'research', 'breakthrough', 'innovation', 'discover', 'introduce',
'platform', 'software', 'application', 'system', 'technology',
'open source', 'model', 'algorithm', 'neural', 'deep learning',
'machine learning', 'transformer', 'llm', 'ai model', 'small business',
'chatgpt', 'openai', 'microsoft', 'google', 'anthropic', 'meta'
]
# Keywords to exclude (with lower negative impact)
exclude_keywords = [
'stock', 'market', 'invest', 'price', 'share', 'trading',
'nasdaq', 'nyse', 'profit', 'revenue', 'earnings', 'dividend',
'etf', 'fund', 'portfolio', 'buy', 'sell', 'investor',
'financial', 'finance', 'bank', 'investment'
]
ranked_topics = []
for topic in topics:
title = topic['title'].lower()
description = topic.get('description', '').lower()
combined_text = f"{title} {description}"
# Calculate score based on keyword matches
score = 0
matched_relevant = []
matched_excluded = []
# Count relevant keyword matches (weight: +1)
for keyword in relevant_keywords:
if keyword in combined_text:
score += 1
matched_relevant.append(keyword)
# Count excluded keyword matches (weight: -0.5 to reduce impact)
for keyword in exclude_keywords:
if keyword in combined_text:
score -= 0.5
matched_excluded.append(keyword)
# Add all topics with their scores
ranked_topics.append({
'topic': topic,
'score': score,
'matched_relevant': matched_relevant,
'matched_excluded': matched_excluded
})
logging.info(f"Topic: {topic['title']}")
logging.info(f"Score: {score}")
logging.info(f"Matched relevant keywords: {matched_relevant}")
logging.info(f"Matched excluded keywords: {matched_excluded}")
logging.info("---")
# Sort topics by score in descending order
ranked_topics.sort(key=lambda x: x['score'], reverse=True)
return ranked_topics
def select_topic(topics):
"""Select the highest-ranked topic from the list."""
if not topics:
logging.warning("No topics available to select.")
return None
# Rank the topics
ranked_topics = rank_and_filter_topics(topics)
if not ranked_topics:
logging.warning("No topics found after ranking.")
return None
# Select the highest-ranked topic
selected = ranked_topics[0]
logging.info(f"Selected highest ranked topic:")
logging.info(f"Title: {selected['topic']['title']}")
logging.info(f"Score: {selected['score']}")
logging.info(f"Matched relevant keywords: {selected['matched_relevant']}")
logging.info(f"Matched excluded keywords: {selected['matched_excluded']}")
return selected['topic']['title']
def generate_title(topic):
"""Generate an engaging title using OpenAI."""
title_prompt = (
f"Create a compelling, SEO-friendly blog title about '{topic}' that would appeal to small business owners "
"and entrepreneurs interested in AI solutions. The title should be concise, engaging, and professional. "
"Do not include any markdown formatting or special characters."
)
try:
client = openai.OpenAI(api_key=OPENAI_API_KEY)
response = client.chat.completions.create(
model='gpt-3.5-turbo',
messages=[{"role": "user", "content": title_prompt}],
max_tokens=50,
temperature=0.7
)
return response.choices[0].message.content.strip()
except Exception as e:
logging.error(f"Error generating title: {str(e)}")
return None
def generate_blog_post(topic):
"""Generate a blog post using OpenAI's ChatGPT model."""
# First generate the title
title = generate_title(topic)
if not title:
return None, None
prompt = (
f"Write an engaging and informative blog post about '{topic}' that demonstrates expertise in AI solutions "
"while appealing to small business owners and entrepreneurs. "
"The post should:\n"
"1. Begin with a hook that relates the topic to practical business benefits\n"
"2. Break down complex AI concepts into simple, actionable insights\n"
"3. Include real-world applications and examples for small businesses\n"
"4. Highlight cost-effective ways to implement AI solutions\n"
"5. Address common concerns and misconceptions\n"
"6. End with a clear call-to-action for businesses seeking AI consultation\n\n"
"Format the post using proper HTML tags (<h2> for headings, <p> for paragraphs, <ul> or <ol> for lists). "
"Make it morally and ethically sound, focusing on sustainable and responsible AI use. "
"The tone should be professional yet approachable, positioning the author as a trusted AI solutions provider. "
"Aim for approximately 1000 words. Do not include a title at the beginning of the post."
)
try:
client = openai.OpenAI(api_key=OPENAI_API_KEY)
response = client.chat.completions.create(
model='gpt-3.5-turbo',
messages=[{"role": "user", "content": prompt}],
max_tokens=1500,
temperature=0.7
)
content = response.choices[0].message.content.strip()
# Content filtering (basic example)
disallowed_keywords = ['disallowed content', 'violence', 'illegal']
if any(word in content.lower() for word in disallowed_keywords):
logging.warning("Generated content contains disallowed content.")
return None, None
logging.info("Generated blog post successfully")
return title, content
except Exception as e:
logging.error(f"Error generating blog post: {str(e)}")
return None, None
def save_for_review(title, content):
"""Save the blog post locally for review."""
filename = f"blog_post_{datetime.now().strftime('%Y%m%d')}.md"
try:
with open(filename, 'w', encoding='utf-8') as file:
file.write(f"# {title}\n\n{content}")
logging.info(f"Blog post saved for review: {filename}")
# Open the file in the default text editor
try:
if sys.platform.startswith('darwin'): # macOS
subprocess.call(('open', filename))
elif os.name == 'nt': # Windows
os.startfile(filename)
elif os.name == 'posix': # Linux
subprocess.call(('xdg-open', filename))
except Exception as e:
logging.error(f"Could not open the file automatically: {e}")
return filename
except Exception as e:
logging.error(f"Failed to save or open the blog post: {e}")
return None
def post_to_wordpress(title, content):
"""Post the blog to WordPress using REST API."""
try:
# Set up authentication
auth = (WORDPRESS_USERNAME, WORDPRESS_PASSWORD)
base_url = f"{WORDPRESS_URL}wp-json/wp/v2"
# First, get or create tags
tag_ids = []
tag_names = ['AI', 'Artificial Intelligence', 'Technology']
for tag_name in tag_names:
# Check if tag exists
response = requests.get(
f"{base_url}/tags",
params={'search': tag_name},
auth=auth
)
if response.status_code == 200:
tags = response.json()
if tags:
# Tag exists, use its ID
tag_ids.append(tags[0]['id'])
else:
# Create new tag
response = requests.post(
f"{base_url}/tags",
json={'name': tag_name},
auth=auth
)
if response.status_code == 201:
tag_ids.append(response.json()['id'])
# Prepare the post data
post_data = {
'title': title,
'content': content,
'status': 'publish',
'categories': [1], # Default category ID
'tags': tag_ids
}
# Make the POST request to create a new post
response = requests.post(
f"{base_url}/posts",
json=post_data,
auth=auth,
headers={'Content-Type': 'application/json'}
)
if response.status_code == 201:
post_id = response.json().get('id')
logging.info(f"Blog post published successfully with ID: {post_id}")
else:
logging.error(f"Failed to publish blog post. Status code: {response.status_code}")
logging.error(f"Response: {response.text}")
except Exception as e:
logging.error(f"Failed to publish blog post: {e}")
def test_wordpress_auth():
"""Test WordPress authentication and API connectivity."""
try:
auth = (WORDPRESS_USERNAME, WORDPRESS_PASSWORD)
base_url = f"{WORDPRESS_URL}wp-json/wp/v2"
# Test authentication by getting users
response = requests.get(
f"{base_url}/users/me",
auth=auth
)
logging.info(f"Auth Test - Status Code: {response.status_code}")
logging.info(f"Auth Test - Response: {response.text}")
if response.status_code == 200:
user_data = response.json()
logging.info(f"Successfully authenticated as: {user_data.get('name')} (Role: {user_data.get('roles', [])})")
return True
else:
logging.error("Authentication test failed")
return False
except Exception as e:
logging.error(f"Auth test failed with error: {e}")
return False
def main():
# Test WordPress authentication first
if not test_wordpress_auth():
logging.error("WordPress authentication failed. Please check credentials.")
return
# Step 1: Get trending AI topics
topics = get_trending_ai_topics()
if not topics:
logging.error("No topics fetched. Exiting.")
return
# Step 2: Select a topic
topic = select_topic(topics)
if not topic:
logging.error("No topic selected. Exiting.")
return
# Step 3: Generate a blog post
title, blog_post = generate_blog_post(topic)
if not blog_post:
logging.error("Failed to generate blog post. Exiting.")
return
# Step 4: Save for review
filename = save_for_review(title, blog_post)
if not filename:
logging.error("Failed to save blog post for review. Exiting.")
return
# Step 5: Prompt user to review and edit
input("Please review and edit the blog post. Press Enter when you're ready to publish.")
# Step 6: Read the edited blog post
try:
with open(filename, 'r', encoding='utf-8') as file:
edited_content = file.read()
except Exception as e:
logging.error(f"Failed to read the edited blog post: {e}")
return
# Step 7: Post to WordPress
post_to_wordpress(title, edited_content)
if __name__ == '__main__':
main()