rsm-roguchi commited on
Commit
d24e71f
Β·
1 Parent(s): a1394be

change some sever type shi

Browse files
Files changed (3) hide show
  1. server/meta.py +32 -9
  2. server/twitter.py +121 -38
  3. ui/meta.py +18 -2
server/meta.py CHANGED
@@ -11,20 +11,43 @@ ACCESS_TOKEN = os.getenv("FB_PAGE_ACCESS_TOKEN")
11
 
12
  generated_fb_post = reactive.Value("")
13
 
14
- def generate_facebook_post(topic: str) -> str:
15
- prompt = (
 
16
  f"You are a social media manager for a hobby e-commerce company called 'Ultima Supply'.\n"
17
- f"Write a short, engaging Facebook post (max 500 characters) about: '{topic}'.\n"
18
- f"Use casual and friendly tone. Include emojis and 3-5 relevant hashtags."
19
  )
20
- return get_response(
21
- input=prompt,
22
- template=lambda x: x,
 
23
  llm="gemini",
24
  md=False,
25
  temperature=0.9,
26
- max_tokens=500
27
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  def post_to_facebook(message: str) -> str:
30
  url = f"https://graph.facebook.com/{PAGE_ID}/feed"
 
11
 
12
  generated_fb_post = reactive.Value("")
13
 
14
+ def generate_facebook_post(topic: str, max_len: int = 500) -> str:
15
+ # Step 1: Initial post generation
16
+ gen_prompt = (
17
  f"You are a social media manager for a hobby e-commerce company called 'Ultima Supply'.\n"
18
+ f"Write a short, engaging Facebook post (max {max_len} characters) about: '{topic}'.\n"
19
+ f"Use a casual and friendly tone. Include emojis and 3–5 SEO-relevant hashtags. Keep it under the character limit."
20
  )
21
+
22
+ post = get_response(
23
+ input=gen_prompt,
24
+ template=lambda x: x.strip(),
25
  llm="gemini",
26
  md=False,
27
  temperature=0.9,
28
+ max_tokens=250
29
+ ).strip()
30
+
31
+ # Step 2: If too long, ask Gemini to shorten it
32
+ if len(post) <= max_len:
33
+ return post
34
+ else:
35
+ shorten_prompt = (
36
+ f"Shorten this Facebook post to {max_len} characters or fewer, but keep the friendly tone, emojis, and all original hashtags:\n\n"
37
+ f"{post}\n\n"
38
+ f"Only return the shortened post. Do not remove hashtags or change tone."
39
+ )
40
+
41
+ shortened = get_response(
42
+ input=shorten_prompt,
43
+ template=lambda x: x.strip(),
44
+ llm="gemini",
45
+ md=False,
46
+ temperature=0.7,
47
+ max_tokens=200
48
+ ).strip()
49
+
50
+ return shortened[:max_len] # Hard cap fallback
51
 
52
  def post_to_facebook(message: str) -> str:
53
  url = f"https://graph.facebook.com/{PAGE_ID}/feed"
server/twitter.py CHANGED
@@ -2,26 +2,19 @@ from shiny import reactive, render, ui
2
  import os
3
  import requests
4
  from bs4 import BeautifulSoup
5
- import tweepy
6
  from dotenv import load_dotenv, find_dotenv
 
7
  from llm_connect import get_response
8
 
 
9
  load_dotenv(find_dotenv(), override=True)
10
 
11
- BEARER_TOKEN = os.getenv("TWITTER_BEARER_TOKEN")
12
  API_KEY = os.getenv("TWITTER_ACC_API_KEY")
13
  API_SECRET = os.getenv("TWITTER_ACC_API_SECRET")
14
  ACCESS_TOKEN = os.getenv("TWITTER_ACC_ACCESS_TOKEN")
15
  ACCESS_TOKEN_SECRET = os.getenv("TWITTER_ACC_ACCESS_TOKEN_SECRET")
16
 
17
- client = tweepy.Client(
18
- bearer_token=BEARER_TOKEN,
19
- consumer_key=API_KEY,
20
- consumer_secret=API_SECRET,
21
- access_token=ACCESS_TOKEN,
22
- access_token_secret=ACCESS_TOKEN_SECRET
23
- )
24
-
25
  generated_tweet = reactive.Value("")
26
 
27
  def scrape_shopify_blog(url: str) -> str:
@@ -36,36 +29,94 @@ def scrape_shopify_blog(url: str) -> str:
36
  except Exception as e:
37
  return f"❌ Error scraping blog: {e}"
38
 
39
- def generate_tweet_from_text(text: str) -> str:
40
- prompt = (
 
41
  "You are a social media manager for a hobby e-commerce company called 'Ultima Supply'.\n"
42
- "Write a detailed, engaging Twitter post (min 200 chars, max 280 chars) summarizing the Shopify blog content:\n"
43
- f"{text}\n"
44
- "Use casual, fun language. Include emojis and 3-5 relevant hashtags."
 
45
  )
46
- return get_response(
47
- input=prompt,
48
- template=lambda x: x,
 
 
49
  llm='gemini',
50
  md=False,
51
- temperature=0.9,
52
- max_tokens=500
53
- )
54
 
55
- def generate_tweet_from_topic(topic: str) -> str:
56
- prompt = (
57
- f"You are a social media manager for a hobby e-commerce company called 'Ultima Supply'.\n"
58
- f"Write a detailed, engaging Twitter post (min 200 chars, max 280 chars) about: '{topic}'.\n"
59
- f"Use casual, fun language. Include emojis and 3-5 SEO relevant hashtags."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  )
61
- return get_response(
62
- input=prompt,
63
- template=lambda x: x,
 
64
  llm='gemini',
65
  md=False,
66
- temperature=0.9,
67
- max_tokens=500
68
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  def tweet_input_handler(url: str, topic: str) -> str:
71
  if url.strip():
@@ -78,15 +129,42 @@ def tweet_input_handler(url: str, topic: str) -> str:
78
  else:
79
  return "⚠️ Provide either a blog URL or a topic."
80
 
81
- def post_tweet(text: str) -> str:
 
 
 
 
 
 
 
 
 
 
82
  try:
83
- response = client.create_tweet(text=text)
84
- print("βœ… Tweet response:", response.data)
85
- return f"βœ… Tweet posted (ID: {response.data['id']})"
 
 
86
  except Exception as e:
87
- print("❌ Tweet error:", e)
88
  return f"❌ Failed to post tweet: {e}"
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  # ---- EXPORT THIS FOR app.py TO CALL ----
91
  def server(input, output, session):
92
  @output
@@ -96,6 +174,7 @@ def server(input, output, session):
96
  url = input.shopify_url().strip()
97
  topic = input.tweet_topic().strip()
98
  tweet = tweet_input_handler(url, topic)
 
99
  if tweet.startswith("⚠️"):
100
  return ui.HTML(f"<p><strong>{tweet}</strong></p>")
101
  generated_tweet.set(tweet)
@@ -105,7 +184,11 @@ def server(input, output, session):
105
  @render.text
106
  @reactive.event(input.post_btn_twt)
107
  def tweet_post_status():
 
 
 
108
  tweet = generated_tweet()
 
109
  if not tweet:
110
  return "⚠️ No tweet generated yet."
111
- return post_tweet(tweet)
 
2
  import os
3
  import requests
4
  from bs4 import BeautifulSoup
 
5
  from dotenv import load_dotenv, find_dotenv
6
+ import tweepy
7
  from llm_connect import get_response
8
 
9
+ # Load environment variables
10
  load_dotenv(find_dotenv(), override=True)
11
 
12
+ # Load credentials
13
  API_KEY = os.getenv("TWITTER_ACC_API_KEY")
14
  API_SECRET = os.getenv("TWITTER_ACC_API_SECRET")
15
  ACCESS_TOKEN = os.getenv("TWITTER_ACC_ACCESS_TOKEN")
16
  ACCESS_TOKEN_SECRET = os.getenv("TWITTER_ACC_ACCESS_TOKEN_SECRET")
17
 
 
 
 
 
 
 
 
 
18
  generated_tweet = reactive.Value("")
19
 
20
  def scrape_shopify_blog(url: str) -> str:
 
29
  except Exception as e:
30
  return f"❌ Error scraping blog: {e}"
31
 
32
+ def generate_tweet_from_text(text: str, url: str='https://ultimasupply.com/blogs/news', max_len: int = 250) -> str:
33
+ # Step 1: Generate tweet from blog content
34
+ gen_prompt = (
35
  "You are a social media manager for a hobby e-commerce company called 'Ultima Supply'.\n"
36
+ f"Write a single engaging Twitter post (max {max_len} characters) summarizing the following Shopify blog content:\n\n"
37
+ f"{text}\n\n"
38
+ f"The tweet MUST include this URL: {url}\n"
39
+ "Use casual, fun language. Add emojis and 3–5 SEO-relevant hashtags. Keep it under the character limit and make it attention-grabbing."
40
  )
41
+
42
+
43
+ tweet = get_response(
44
+ input=gen_prompt,
45
+ template=lambda x: x.strip(),
46
  llm='gemini',
47
  md=False,
48
+ temperature=0.6,
49
+ max_tokens=100
50
+ ).strip()
51
 
52
+ print(f'Base Tweet: {tweet}')
53
+
54
+ # Step 2: If too long, ask Gemini to shorten it
55
+ if len(tweet) <= max_len:
56
+ return tweet
57
+ else:
58
+ shorten_prompt = (
59
+ f"You are shortening this tweet to {max_len} characters or fewer.\n"
60
+ f"YOU MUST KEEP ALL of the original SEO hashtags and the Shopify blog link intact in the final output.\n"
61
+ f"DO NOT remove or modify the link or any hashtags.\n\n"
62
+ f"Original tweet:\n{tweet}\n"
63
+ )
64
+
65
+ shortened = get_response(
66
+ input=shorten_prompt,
67
+ template=lambda x: x.strip(),
68
+ llm='gemini',
69
+ md=False,
70
+ temperature=0.5,
71
+ max_tokens=75
72
+ ).strip()
73
+
74
+ return shortened # Final hard cap just in case
75
+
76
+
77
+ def generate_tweet_from_topic(topic: str, max_len: int = 250) -> str:
78
+ # Step 1: Generate tweet from topic
79
+ gen_prompt = (
80
+ f"You are a social media manager for a hobby e-commerce brand called 'Ultima Supply'.\n"
81
+ f"Write a SHORT SINGLE SENTENCE Twitter post (max {max_len} characters) about: '{topic}'.\n"
82
+ f"Use casual, fun language. Include emojis and 3-5 SEO-relevant hashtags."
83
  )
84
+
85
+ tweet = get_response(
86
+ input=gen_prompt,
87
+ template=lambda x: x.strip(),
88
  llm='gemini',
89
  md=False,
90
+ temperature=0.6,
91
+ max_tokens=100
92
+ ).strip()
93
+
94
+ print(f'Base Tweet: {tweet}')
95
+
96
+ # Step 2: If too long, regenerate with shortening prompt
97
+ if len(tweet) <= max_len:
98
+ return tweet
99
+ else:
100
+ shorten_prompt = (
101
+ f"You are shortening this tweet to {max_len} characters or fewer.\n"
102
+ f"YOU MUST KEEP ALL of the original SEO hashtags and the Shopify blog link intact in the final output.\n"
103
+ f"DO NOT remove or modify the link or any hashtags.\n\n"
104
+ f"Original tweet:\n{tweet}\n"
105
+ )
106
+
107
+
108
+ shortened = get_response(
109
+ input=shorten_prompt,
110
+ template=lambda x: x.strip(),
111
+ llm='gemini',
112
+ md=False,
113
+ temperature=0.5,
114
+ max_tokens=75
115
+ ).strip()
116
+
117
+
118
+ return shortened # Ensure hard cutoff if still too long
119
+
120
 
121
  def tweet_input_handler(url: str, topic: str) -> str:
122
  if url.strip():
 
129
  else:
130
  return "⚠️ Provide either a blog URL or a topic."
131
 
132
+ # Create Tweepy v2 Client
133
+ def get_tweepy_client():
134
+ return tweepy.Client(
135
+ consumer_key=API_KEY,
136
+ consumer_secret=API_SECRET,
137
+ access_token=ACCESS_TOKEN,
138
+ access_token_secret=ACCESS_TOKEN_SECRET
139
+ )
140
+
141
+ # Post tweet using Tweepy v2 Client
142
+ def post_tweet_tweepy(tweet_text: str) -> str:
143
  try:
144
+ client = get_tweepy_client()
145
+ response = client.create_tweet(text=tweet_text)
146
+ tweet_id = response.data.get("id")
147
+ print("βœ… Tweet posted:", tweet_id)
148
+ return f"βœ… Tweet posted (ID: {tweet_id})"
149
  except Exception as e:
150
+ print("❌ Failed to post tweet:", e)
151
  return f"❌ Failed to post tweet: {e}"
152
 
153
+ # Check auth by attempting to fetch own user (optional)
154
+ def check_twitter_connection() -> str:
155
+ try:
156
+ client = get_tweepy_client()
157
+ user_data = client.get_me()
158
+ if user_data and user_data.data:
159
+ username = user_data.data.username
160
+ print(f"βœ… Connected to Twitter as @{username}")
161
+ return f"βœ… Connected to Twitter as @{username}"
162
+ else:
163
+ return "❌ Unable to fetch Twitter account info."
164
+ except Exception as e:
165
+ print("❌ Connection check failed:", e)
166
+ return f"❌ Twitter auth error: {e}"
167
+
168
  # ---- EXPORT THIS FOR app.py TO CALL ----
169
  def server(input, output, session):
170
  @output
 
174
  url = input.shopify_url().strip()
175
  topic = input.tweet_topic().strip()
176
  tweet = tweet_input_handler(url, topic)
177
+ print("πŸ“ Generated Tweet:\n", tweet)
178
  if tweet.startswith("⚠️"):
179
  return ui.HTML(f"<p><strong>{tweet}</strong></p>")
180
  generated_tweet.set(tweet)
 
184
  @render.text
185
  @reactive.event(input.post_btn_twt)
186
  def tweet_post_status():
187
+ status = check_twitter_connection()
188
+ if status.startswith("❌"):
189
+ return status
190
  tweet = generated_tweet()
191
+ print("πŸ“€ Attempting to post tweet:", tweet)
192
  if not tweet:
193
  return "⚠️ No tweet generated yet."
194
+ return post_tweet_tweepy(tweet)
ui/meta.py CHANGED
@@ -2,12 +2,28 @@ from shiny import ui
2
 
3
  ui = ui.nav_panel(
4
  "Facebook Poster",
 
 
5
  ui.input_text("fb_topic", "Enter Topic", placeholder="e.g. New Anime Drop This Friday"),
 
 
6
  ui.div(
7
  ui.input_action_button("gen_btn_fb", "Generate Facebook Post", class_="btn-primary"),
8
  class_="mt-3 mb-2"
9
  ),
 
 
10
  ui.output_ui("fb_post_draft"),
11
- ui.input_action_button("post_btn_fb", "Post to Facebook", class_="btn-success"),
12
- ui.output_text("fb_post_status")
 
 
 
 
 
 
 
 
 
 
13
  )
 
2
 
3
  ui = ui.nav_panel(
4
  "Facebook Poster",
5
+
6
+ # Input for post topic
7
  ui.input_text("fb_topic", "Enter Topic", placeholder="e.g. New Anime Drop This Friday"),
8
+
9
+ # Generate button
10
  ui.div(
11
  ui.input_action_button("gen_btn_fb", "Generate Facebook Post", class_="btn-primary"),
12
  class_="mt-3 mb-2"
13
  ),
14
+
15
+ # Output: generated post
16
  ui.output_ui("fb_post_draft"),
17
+
18
+ # Post button
19
+ ui.div(
20
+ ui.input_action_button("post_btn_fb", "Post to Facebook", class_="btn-success"),
21
+ class_="my-3"
22
+ ),
23
+
24
+ # Status message
25
+ ui.output_text("fb_post_status"),
26
+
27
+ # Optional: spacing/padding at bottom
28
+ class_="p-4"
29
  )