upload files
This commit is contained in:
196
.gitignore
vendored
Normal file
196
.gitignore
vendored
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Environment variables and secrets
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
cookies.json
|
||||||
|
|
||||||
|
# Data and posts tracking
|
||||||
|
posts.json
|
||||||
|
|
||||||
|
# Temporary video processing files
|
||||||
|
temp/
|
||||||
|
*.mp4
|
||||||
|
*.webm
|
||||||
|
*.mov
|
||||||
|
*.mpeg
|
||||||
|
|
||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
4
cookies.json
Normal file
4
cookies.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"ct0": "",
|
||||||
|
"auth_token": ""
|
||||||
|
}
|
||||||
10
example.env
Normal file
10
example.env
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Bluesky Credentials
|
||||||
|
# Use an app password from https://bsky.app/settings/app-passwords
|
||||||
|
BSKY_USERNAME=example.bsky.social
|
||||||
|
BSKY_PASSWORD=
|
||||||
|
|
||||||
|
# Twitter User to Monitor
|
||||||
|
TWITTER_USER=exampleuser
|
||||||
|
|
||||||
|
# Check interval in milliseconds (60000 = 1 minute)
|
||||||
|
CHECK_INTERVAL=60000
|
||||||
89
get_tweets.py
Normal file
89
get_tweets.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import asyncio
|
||||||
|
from twikit import Client
|
||||||
|
|
||||||
|
async def get_user_tweets(username, count=10):
|
||||||
|
client = Client('en-US')
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.load_cookies('cookies.json')
|
||||||
|
except:
|
||||||
|
print(json.dumps({"error": "Not logged in. Run login script first."}))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = await client.get_user_by_screen_name(username)
|
||||||
|
user_id = user.id
|
||||||
|
|
||||||
|
tweets_data = []
|
||||||
|
tweets = await client.get_user_tweets(user_id, 'Tweets', count=count)
|
||||||
|
|
||||||
|
for tweet in tweets:
|
||||||
|
if tweet.text.startswith('RT @'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get full text - twikit truncates long tweets at 280 chars
|
||||||
|
# Full text is available on the tweet object under different attributes
|
||||||
|
full_text = None
|
||||||
|
|
||||||
|
# Try note_tweet first (X Premium long posts are stored as "note tweets")
|
||||||
|
if hasattr(tweet, 'note_tweet') and tweet.note_tweet:
|
||||||
|
note = tweet.note_tweet
|
||||||
|
if hasattr(note, 'text'):
|
||||||
|
full_text = note.text
|
||||||
|
elif isinstance(note, dict):
|
||||||
|
full_text = note.get('text') or note.get('note_tweet_results', {}).get('result', {}).get('text')
|
||||||
|
|
||||||
|
# Fall back to full_text attribute if available
|
||||||
|
if not full_text and hasattr(tweet, 'full_text') and tweet.full_text:
|
||||||
|
full_text = tweet.full_text
|
||||||
|
|
||||||
|
# Fall back to regular text
|
||||||
|
if not full_text:
|
||||||
|
full_text = tweet.text
|
||||||
|
|
||||||
|
# Strip trailing ellipsis if still truncated (shouldn't happen but just in case)
|
||||||
|
if full_text.endswith('…'):
|
||||||
|
full_text = full_text[:-1]
|
||||||
|
|
||||||
|
media = []
|
||||||
|
if tweet.media:
|
||||||
|
for m in tweet.media:
|
||||||
|
media_url = None
|
||||||
|
media_type = m.type
|
||||||
|
|
||||||
|
if hasattr(m, 'media_url'):
|
||||||
|
media_url = m.media_url
|
||||||
|
|
||||||
|
if media_type == 'video' and hasattr(m, 'streams') and m.streams:
|
||||||
|
best_stream = max(m.streams, key=lambda s: s.bitrate if hasattr(s, 'bitrate') and s.bitrate else 0)
|
||||||
|
if hasattr(best_stream, 'url'):
|
||||||
|
media_url = best_stream.url
|
||||||
|
|
||||||
|
if media_url:
|
||||||
|
media.append({
|
||||||
|
'type': media_type,
|
||||||
|
'url': media_url
|
||||||
|
})
|
||||||
|
|
||||||
|
tweets_data.append({
|
||||||
|
'id': tweet.id,
|
||||||
|
'text': full_text,
|
||||||
|
'media': media,
|
||||||
|
'created_at': tweet.created_at
|
||||||
|
})
|
||||||
|
|
||||||
|
print(json.dumps(tweets_data))
|
||||||
|
except Exception as e:
|
||||||
|
print(json.dumps({"error": str(e)}))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
username = sys.argv[1] if len(sys.argv) > 1 else None
|
||||||
|
count = int(sys.argv[2]) if len(sys.argv) > 2 else 10
|
||||||
|
|
||||||
|
if not username:
|
||||||
|
print(json.dumps({"error": "Username required"}))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
asyncio.run(get_user_tweets(username, count))
|
||||||
Reference in New Issue
Block a user