From 8891c275cf643fcf6bf40462220bc684552f49de Mon Sep 17 00:00:00 2001 From: Wire Date: Mon, 13 Jul 2020 16:36:45 +0200 Subject: [PATCH] New version 1.2 --- app/models.py | 1 - app/routes.py | 99 ++++++++++++++++++++-------------- app/static/img/empty.png | Bin 0 -> 23221 bytes app/templates/_post.html | 13 ++++- app/templates/_post_nort.html | 11 +++- app/templates/base.html | 1 + app/templates/search.html | 15 ++++++ app/templates/user.html | 31 +++++++---- 8 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 app/static/img/empty.png diff --git a/app/models.py b/app/models.py index 749a696..7f8b12a 100644 --- a/app/models.py +++ b/app/models.py @@ -62,7 +62,6 @@ class twitterPost(): validPost = True content = "El gato siguiĆ³ a la liebre. Esto es un texto de ejemplo." profilePic = "url" - twitterAt = "@error" twitterName = "Error Name" timeStamp = "error" diff --git a/app/routes.py b/app/routes.py index 6dca8cb..f8d6712 100644 --- a/app/routes.py +++ b/app/routes.py @@ -3,11 +3,16 @@ from flask import render_template, flash, redirect, url_for, request from app.forms import LoginForm, RegistrationForm, EmptyForm, SearchForm from app.models import User, twitterPost from werkzeug.urls import url_parse +from bs4 import BeautifulSoup from flask import Markup from app import app, db import time, datetime import random, string import feedparser +import requests + +nitterInstance = "https://nitter.net/" +nitterInstanceII = "https://nitter.mastodont.cat" @app.route('/') @app.route('/index') @@ -17,21 +22,20 @@ def index(): followed = current_user.followed.count() posts = [] avatarPath = "img/avatars/1.png" + form = EmptyForm() for fwd in following: avatarPath = "img/avatars/{}.png".format(str(random.randint(1,12))) #Gather profile info. - rssFeed = feedparser.parse('https://nitter.net/{}/rss'.format(fwd.username)) + rssFeed = feedparser.parse('{instance}{user}/rss'.format(instance=nitterInstance, user=fwd.username)) + twitterAt = rssFeed.feed.title.split("/")[1].replace(" ", "") + twitterName = rssFeed.feed.title.split("/")[0] #Gather posts if rssFeed.entries != []: for post in rssFeed.entries: newPost = twitterPost() - newPost.profilePic = rssFeed.channel.image.url - newPost.twitterAt = rssFeed.feed.title.split("/")[1].replace(" ", "") - newPost.twitterName = rssFeed.feed.title.split("/")[0] - newPost.username = rssFeed.feed.title.split("/")[0] newPost.date = getTimeDiff(post.published_parsed) newPost.timeStamp = datetime.datetime(*post.published_parsed[:6]) @@ -41,11 +45,16 @@ def index(): if "RT by" in post.title: newPost.isRT = True + newPost.profilePic = "" else: newPost.isRT = False + try: + newPost.profilePic = rssFeed.channel.image.url + except: + newPost.profilePic = avatarPath posts.append(newPost) - posts.sort(key=lambda x: x.timeStamp, reverse=True) - return render_template('index.html', title='Home', posts=posts, avatar=avatarPath, followedCount = followed) + posts.sort(key=lambda x: x.timeStamp, reverse=True) + return render_template('index.html', title='Home', posts=posts, avatar=avatarPath, followedCount=followed, twitterAt=twitterAt, twitterName=twitterName, form=form) @app.route('/login', methods=['GET', 'POST']) @@ -76,32 +85,37 @@ def logout(): def register(): if current_user.is_authenticated: return redirect(url_for('index')) + form = RegistrationForm() if form.validate_on_submit(): - rssFeed = feedparser.parse('https://nitter.net/{}/rss'.format(form.username.data)) - - if rssFeed.entries == []: + if isTwitterUser(form.username.data): + flash('This is username is taken! Choose a different one.') + else: user = User(username=form.username.data, email=form.email.data) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash('Congratulations, you are now a registered user!') return redirect(url_for('login')) - else: - flash('This is username is taken! Choose a different one.') return render_template('register.html', title='Register', form=form) +@app.route('/savePost/', methods=['POST']) +@login_required +def savePost(url): + print("SAVEPOST") + print("Saved {}.".format(url.replace('~', '/'))) + return redirect(url_for('index')) + @app.route('/follow/', methods=['POST']) @login_required def follow(username): form = EmptyForm() if form.validate_on_submit(): user = User.query.filter_by(username=username).first() - isTwitter = True + isTwitter = isTwitterUser(username) if user is None and isTwitter: x = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)) newUser = User(username=username, email="{}@person.is".format(x)) - print(x) db.session.add(newUser) db.session.commit() flash('You are now following {}!'.format(username)) @@ -168,13 +182,20 @@ def following(): @login_required def search(): form = SearchForm() + parsedResults = [] if form.validate_on_submit(): user = form.username.data if isTwitterUser(user): - return redirect(url_for('user', username=user)) + r = requests.get("{instance}search?f=users&q={usern}".format(instance=nitterInstance, usern=user)) + html = BeautifulSoup(str(r.content), features="lxml") + results = html.body.find_all('a', attrs={'class':'tweet-link'}) + + parsedResults = [s['href'].replace("/", "") for s in results] + + return render_template('search.html', form = form, results = parsedResults) else: flash("User {} does not exist!".format(user)) - return render_template('search.html', form = form) + return render_template('search.html', form = form, results = parsedResults) else: return render_template('search.html', form = form) @@ -188,32 +209,30 @@ def notfound(): @login_required def user(username): user = User.query.filter_by(username=username).first() - isTwitter = True - rssFeed = feedparser.parse('https://nitter.net/{}/rss'.format(username)) - if rssFeed.entries == []: - isTwitter = False - if user is None and isTwitter: + isTwitter = isTwitterUser(username) + + if isTwitter and user is None: x = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)) newUser = User(username=username, email="{}@person.is".format(x)) db.session.add(newUser) db.session.commit() - #flash('User {} not found.'.format(username)) elif not isTwitter and user is None: return redirect(url_for('notfound')) #Gather profile info. - rssFeed = feedparser.parse('https://nitter.net/{}/rss'.format(username)) + rssFeed = feedparser.parse('{instance}{user}/rss'.format(instance=nitterInstance,user=username)) + try: + profilePicture = rssFeed.channel.image.url + except: + profilePicture = "" + twitterAt = rssFeed.feed.title.split("/")[1].replace(" ", "") + twitterName = rssFeed.feed.title.split("/")[0] #Gather posts posts = [] for post in rssFeed.entries: newPost = twitterPost() - - newPost.profilePic = rssFeed.channel.image.url - newPost.twitterAt = rssFeed.feed.title.split("/")[1].replace(" ", "") - newPost.twitterName = rssFeed.feed.title.split("/")[0] - newPost.username = rssFeed.feed.title.split("/")[0] newPost.date = getTimeDiff(post.published_parsed) newPost.timeStamp = datetime.datetime(*post.published_parsed[:6]) @@ -223,35 +242,37 @@ def user(username): if "RT by" in post.title: newPost.isRT = True + newPost.profilePic = "" else: newPost.isRT = False + try: + newPost.profilePic = rssFeed.channel.image.url + except: + newPost.profilePic = avatarPath #validPost = True posts.append(newPost) form = EmptyForm() user = User.query.filter_by(username=username).first() - return render_template('user.html', user=user, posts=posts, form=form, profilePic=rssFeed.channel.image.url) + return render_template('user.html', user=user, posts=posts, form=form, profilePic=profilePicture, twitterAt=twitterAt, twitterName=twitterName) def getTimeDiff(t): - today = datetime.datetime.now() tweetTime = datetime.datetime(*t[:6]) - diff = today - tweetTime - timeString = "0m" + diff = datetime.datetime.now() - tweetTime if diff.days == 0: - minutes = diff.seconds/60 - if minutes > 60: - hours = minutes/60 - timeString = "{}h".format(hours) + if diff.seconds > 3599: + timeString = "{}h".format(int((diff.seconds/60)/60)) else: - timeString = "{}m".format(minutes) + timeString = "{}m".format(int(diff.seconds/60)) else: timeString = "{}d".format(diff.days) return timeString def isTwitterUser(username): - rssFeed = feedparser.parse('https://nitter.net/{}/rss'.format(username)) - if rssFeed.entries == []: + request = requests.get('https://nitter.net/{}'.format(username), timeout=1) + print("User {name} is {boo} twitter.".format(name=username, boo=request.status_code == 404)) + if request.status_code == 404: return False return True \ No newline at end of file diff --git a/app/static/img/empty.png b/app/static/img/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..e1634827f44b906e615b0dd05844528c191224a1 GIT binary patch literal 23221 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 I=Frr?0iGeZQ~&?~ literal 0 HcmV?d00001 diff --git a/app/templates/_post.html b/app/templates/_post.html index eea25ac..3a8250f 100644 --- a/app/templates/_post.html +++ b/app/templates/_post.html @@ -6,14 +6,23 @@ - +
{{post.date}} - {{post.twitterAt}} retwitted + {{twitterAt}} retwitted

{{post.content}}

+
+

+

+ +
+

+