s''
This commit is contained in:
5
.env
Normal file
5
.env
Normal file
@@ -0,0 +1,5 @@
|
||||
TWITTER_CONSUMER_KEY="nQ4HjC7oofnO809VSjvHn5v42"
|
||||
TWITTER_CONSUMER_SECRET="LFvQGjUsSxeBwwBFAWvrLyWpVxyGCykcDrLfDZjICewmRlB4AZ"
|
||||
TWITTER_ACCESS_TOKEN="2516769889-Sv8W6R1q9rV7a2GQBk2z3gpcI8jf8Iec96d2d4P"
|
||||
TWITTER_ACCESS_TOKEN_SECRET="g0mCTwArcMEInFqwA2fLoBORBjvNWdqfptbNn4tuNkkZP"
|
||||
TWITTER_BEARER_TOKEN="g0mCTwArcMEInFqwA2fLoBORBjvNWdqfptbNn4tuNkkZP"
|
||||
125
main.py
Normal file
125
main.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv() # 加载 .env 文件中的环境变量
|
||||
import tweepy
|
||||
import schedule
|
||||
import time
|
||||
import os
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 从环境变量读取密钥(安全实践)
|
||||
consumer_key = os.environ.get('TWITTER_CONSUMER_KEY')
|
||||
consumer_secret = os.environ.get('TWITTER_CONSUMER_SECRET')
|
||||
access_token = os.environ.get('TWITTER_ACCESS_TOKEN')
|
||||
access_token_secret = os.environ.get('TWITTER_ACCESS_TOKEN_SECRET')
|
||||
bearer_token = os.environ.get('TWITTER_BEARER_TOKEN')
|
||||
|
||||
if not all([consumer_key, consumer_secret, access_token, access_token_secret, bearer_token]):
|
||||
raise ValueError("Missing Twitter API keys in environment variables.")
|
||||
|
||||
# 创建 OAuth1 用户认证(用于 v1.1 端点,如媒体上传)
|
||||
auth = tweepy.OAuth1UserHandler(consumer_key, consumer_secret, access_token, access_token_secret)
|
||||
api_v1 = tweepy.API(auth, wait_on_rate_limit=True)
|
||||
|
||||
# 创建 Client(用于 v2 端点,如发帖)
|
||||
client = tweepy.Client(
|
||||
bearer_token=bearer_token,
|
||||
consumer_key=consumer_key,
|
||||
consumer_secret=consumer_secret,
|
||||
access_token=access_token,
|
||||
access_token_secret=access_token_secret,
|
||||
wait_on_rate_limit=True
|
||||
)
|
||||
|
||||
def translate_text(text, target_lang='en'):
|
||||
"""
|
||||
简单文本自动翻译模拟(从中文到英语)。
|
||||
这里使用字符串替换作为示例;实际可替换为 API 调用(如 Google Translate)或库(如 transformers)。
|
||||
"""
|
||||
# 示例翻译映射(扩展这个字典以支持更多)
|
||||
translations = {
|
||||
'每日自动问候!当前时间:': 'Daily auto greeting! Current time:',
|
||||
'每日自动带图问候!当前时间:': 'Daily auto greeting with image! Current time:',
|
||||
'这是一条自动发帖测试。': 'This is an auto-post test.'
|
||||
}
|
||||
for cn, en in translations.items():
|
||||
if cn in text:
|
||||
text = text.replace(cn, en)
|
||||
return text
|
||||
|
||||
def upload_media(image_paths, max_retries=3):
|
||||
"""上传媒体文件,支持重试"""
|
||||
media_ids = []
|
||||
for image_path in image_paths[:4]: # 限制最多4张
|
||||
if not os.path.exists(image_path):
|
||||
logger.error(f"图片文件不存在: {image_path}")
|
||||
continue
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
media = api_v1.media_upload(filename=image_path)
|
||||
media_ids.append(media.media_id)
|
||||
logger.info(f"图片上传成功: {image_path} (Media ID: {media.media_id})")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning(f"图片上传失败 {image_path} (尝试 {attempt+1}/{max_retries}): {e}")
|
||||
if attempt == max_retries - 1:
|
||||
return [] # 失败后返回空列表
|
||||
return media_ids
|
||||
|
||||
def post_tweet(text, image_paths=None):
|
||||
"""
|
||||
通用发帖函数:支持纯文本或带图片。
|
||||
自动翻译文本。
|
||||
"""
|
||||
text = translate_text(text) # 自动翻译
|
||||
media_ids = []
|
||||
|
||||
if image_paths:
|
||||
media_ids = upload_media(image_paths)
|
||||
if not media_ids:
|
||||
logger.error("没有图片上传成功,无法发帖")
|
||||
return
|
||||
|
||||
try:
|
||||
response = client.create_tweet(text=text, media_ids=media_ids if media_ids else None)
|
||||
tweet_id = response.data['id']
|
||||
logger.info(f"发帖成功!帖子 ID: {tweet_id}")
|
||||
logger.info(f"链接: https://x.com/user/status/{tweet_id}")
|
||||
except tweepy.TweepyException as e:
|
||||
logger.error(f"发帖失败: {e}")
|
||||
|
||||
def scheduled_post(with_images=False):
|
||||
"""定时任务函数"""
|
||||
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
if with_images:
|
||||
message = f"每日自动带图问候!当前时间: {timestamp}"
|
||||
image_paths = ["path/to/your/daily_image.jpg"] # 可动态替换(如从文件夹随机选)
|
||||
post_tweet(message, image_paths)
|
||||
else:
|
||||
message = f"每日自动问候!当前时间: {timestamp}"
|
||||
post_tweet(message)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Twitter Auto Poster")
|
||||
parser.add_argument('--schedule', action='store_true', help="Run in scheduled mode")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.schedule:
|
||||
# 设置定时任务:每天9:00发纯文本帖(可添加带图任务)
|
||||
schedule.every().day.at("09:00").do(scheduled_post, with_images=False)
|
||||
# 示例:每天10:00发带图帖
|
||||
# schedule.every().day.at("10:00").do(scheduled_post, with_images=True)
|
||||
|
||||
logger.info("定时发帖程序已启动...")
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(60) # 每分钟检查一次
|
||||
else:
|
||||
# 立即执行示例
|
||||
message = "Hello from Tweepy! 😊 date: 2026-01-05"
|
||||
image_paths = ["path/to/your/image1.jpg", "path/to/your/image2.png"]
|
||||
post_tweet(message, image_paths)
|
||||
13
requirements.txt
Normal file
13
requirements.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# Twitter/X 自动发帖项目依赖
|
||||
tweepy>=4.14.0
|
||||
schedule>=1.2.2
|
||||
python-dotenv>=1.0.1
|
||||
# 可选:如果需要更高级的日志或重试机制
|
||||
requests>=2.32.3
|
||||
requests-oauthlib>=2.0.0
|
||||
|
||||
# 如果你未来想添加真实翻译功能(例如使用 Google Translate 非官方 API)
|
||||
# googletrans==4.0.0-rc1 # 注意:非官方,易被封
|
||||
|
||||
# 如果需要更强大的日志记录
|
||||
# loguru>=0.7.2
|
||||
Reference in New Issue
Block a user