Compare commits

...

15 Commits

@ -1,22 +1,8 @@
import json from flask import Flask
import click
import arrow
from math import ceil
from datetime import datetime, timedelta
from random import choice
from os import makedirs, path, remove
from flask import Flask, request, session, redirect
from flask import render_template, flash, url_for
from flask_session import Session from flask_session import Session
from suchwow import config
from suchwow.models import Post, Profile, Comment, Notification, db, Moderator from suchwow.routes import auth, post, profile, leaderboard, api, mod, main
from suchwow.routes import auth, comment, post, profile, leaderboard, api from suchwow import filters, cli
from suchwow.utils.decorators import login_required, moderator_required
from suchwow.utils.helpers import post_webhook, get_latest_tipped_posts
from suchwow.utils.helpers import get_top_posters, get_top_posts
from suchwow.reddit import make_post
from suchwow.discord import post_discord_webhook
from suchwow import wownero, filters
app = Flask(__name__) app = Flask(__name__)
@ -27,153 +13,12 @@ Session(app)
app.register_blueprint(post.bp) app.register_blueprint(post.bp)
app.register_blueprint(auth.bp) app.register_blueprint(auth.bp)
app.register_blueprint(profile.bp) app.register_blueprint(profile.bp)
app.register_blueprint(comment.bp)
app.register_blueprint(leaderboard.bp) app.register_blueprint(leaderboard.bp)
app.register_blueprint(api.bp) app.register_blueprint(api.bp)
app.register_blueprint(mod.bp)
app.register_blueprint(main.bp)
app.register_blueprint(filters.bp) app.register_blueprint(filters.bp)
app.register_blueprint(cli.bp)
@app.route("/")
def index():
itp = 15
page = request.args.get("page", 1)
submitter = request.args.get("submitter", None)
content = request.args.get("content", None)
if content == 'latest_tipped':
posts = get_latest_tipped_posts()
return render_template(
"index.html",
posts=posts[0:30],
title="Latest Tipped Memes"
)
try:
page = int(page)
except:
flash("Wow, wtf hackerman. Cool it.")
page = 1
posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc())
if submitter:
posts = posts.where(Post.submitter==submitter)
paginated_posts = posts.paginate(page, itp)
total_pages = ceil(posts.count() / itp)
return render_template(
"index.html",
posts=paginated_posts,
page=page,
total_pages=total_pages,
title="Latest Memes"
)
@app.route("/mod")
@moderator_required
def mod_queue():
posts = Post.select().where(Post.approved==False).order_by(Post.timestamp.asc())
return render_template("index.html", posts=posts)
@app.route("/about")
def about():
return render_template("about.html")
@app.cli.command("init")
def init():
# create subdirs
for i in ["uploads", "db", "wallet"]:
makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True)
# init db
db.create_tables([Post, Profile, Comment, Notification, Moderator])
@app.cli.command("post_reddit")
@click.argument('last_hours')
def post_reddit(last_hours):
posts = Post.select().where(
Post.approved==True,
Post.to_reddit==False
).order_by(Post.timestamp.asc())
for p in posts:
if p.hours_elapsed() < int(last_hours):
if not p.to_reddit:
_p = make_post(p)
if _p:
p.to_reddit = True
p.save()
return
@app.cli.command("create_accounts")
def create_accounts():
wallet = wownero.Wallet()
for post in Post.select():
if post.account_index not in wallet.accounts():
account = wallet.new_account()
print(f"Created account {account}")
@app.cli.command("payout_users")
def payout_users():
wallet = wownero.Wallet()
_fa = wownero.from_atomic
_aw = wownero.as_wownero
for post in Post.select():
submitter = Profile.get(username=post.submitter)
balances = wallet.balances(post.account_index)
url = url_for('post.read', id=post.id, _external=True)
if balances[1] > 0.1:
print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
print(sweep)
if "tx_hash_list" in sweep:
amount = 0
for amt in sweep["amount_list"]:
amount += int(amt)
# post_webhook(f"Paid out :moneybag: {_aw(_fa(amount))} WOW to `{post.submitter}` for post [{post.id}]({url})")
# god damn you eimernase, why'd you ruin this for me? :P
@app.cli.command("add_admin")
@click.argument("username")
def add_admin(username):
if not Moderator.filter(username=username):
m = Moderator(username=username)
m.save()
print(f"Added {username}")
post_webhook(f"Moderator `{username}` added :ship_it_parrot: by console :black_flag:")
else:
print("That moderator already exists")
@app.cli.command("remove_admin")
@click.argument("username")
def remove_admin(username):
m = Moderator.filter(username=username).first()
if m:
m.delete_instance()
print(f"Deleted {username}")
post_webhook(f"Moderator `{username}` removed :excuseme: by console :black_flag:")
else:
print("That moderator doesn't exist")
@app.cli.command("show")
@click.argument("post_id")
def post_id(post_id):
p = Post.filter(id=post_id).first()
if p:
print(p.show())
else:
print("That post doesn't exist")
@app.cli.command("load_cache")
def load_cache():
app.logger.info('loading top posters into cache')
get_top_posters()
app.logger.info('done')
app.logger.info('loading latest tipped into cache')
get_latest_tipped_posts()
app.logger.info('done')
for i in [1, 3, 7, 30]:
app.logger.info(f'loading top posts last {i} days into cache')
get_top_posts(i)
app.logger.info('done')
if __name__ == "__main__": if __name__ == "__main__":

@ -0,0 +1,95 @@
from os import makedirs
import click
from flask import Blueprint, url_for, current_app
from suchwow.models import Post, Profile, Comment, Notification, db, Moderator, Ban, AuditEvent
from suchwow.utils.helpers import get_latest_tipped_posts
from suchwow.utils.helpers import get_top_posters, get_top_posts
from suchwow.reddit import make_post
from suchwow import wownero
from suchwow import config
bp = Blueprint('cli', 'cli', cli_group=None)
@bp.cli.command("init")
def init():
# create subdirs
for i in ["uploads", "db", "wallet"]:
makedirs(f"{config.DATA_FOLDER}/{i}", exist_ok=True)
# init db
db.create_tables([Post, Profile, Comment, Notification, Moderator, Ban, AuditEvent])
@bp.cli.command("post_reddit")
@click.argument('last_hours')
def post_reddit(last_hours):
posts = Post.select().where(
Post.approved==True,
Post.to_reddit==False
).order_by(Post.timestamp.asc())
for p in posts:
if p.hours_elapsed() < int(last_hours):
if not p.to_reddit:
_p = make_post(p)
if _p:
p.to_reddit = True
p.save()
return
@bp.cli.command("create_accounts")
def create_accounts():
wallet = wownero.Wallet()
for post in Post.select():
if post.account_index not in wallet.accounts():
account = wallet.new_account()
print(f"Created account {account}")
@bp.cli.command("payout_users")
def payout_users():
wallet = wownero.Wallet()
_fa = wownero.from_atomic
_aw = wownero.as_wownero
for post in Post.select():
try:
submitter = Profile.get(username=post.submitter)
balances = wallet.balances(post.account_index)
url = url_for('post.read', id=post.id, _external=True)
if balances[1] > 0.05:
print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
print(sweep)
if "tx_hash_list" in sweep:
amount = 0
for amt in sweep["amount_list"]:
amount += int(amt)
except Exception as e:
print(f"Failed because: {e}")
@bp.cli.command("show")
@click.argument("post_id")
def post_id(post_id):
p = Post.filter(id=post_id).first()
if p:
print(p.show())
else:
print("That post doesn't exist")
@bp.cli.command("load_cache")
def load_cache():
current_app.logger.info('loading top posters into cache')
get_top_posters()
current_app.logger.info('done')
current_app.logger.info('loading latest tipped into cache')
get_latest_tipped_posts()
current_app.logger.info('done')
for i in [1, 3, 7, 30, 9999]:
current_app.logger.info(f'loading top posts last {i} days into cache')
get_top_posts(i)
current_app.logger.info('done')

@ -14,6 +14,7 @@ OIDC_REDIRECT_URL = getenv('OIDC_REDIRECT_URL', 'http://localhost:5000/auth')
SECRET_KEY = getenv('SECRET_KEY', 'yyyyyyyyyyyyy') # whatever you want it to be SECRET_KEY = getenv('SECRET_KEY', 'yyyyyyyyyyyyy') # whatever you want it to be
DATA_FOLDER = getenv('DATA_FOLDER', '/path/to/uploads') # some stable storage path DATA_FOLDER = getenv('DATA_FOLDER', '/path/to/uploads') # some stable storage path
SERVER_NAME = getenv('SERVER_NAME', 'localhost') # name of your DNS resolvable site (.com) SERVER_NAME = getenv('SERVER_NAME', 'localhost') # name of your DNS resolvable site (.com)
SUPER_ADMIN = getenv('SUPER_ADMIN', 'lza_menace') # top dawg you cannot delete
WALLET_HOST = getenv('WALLET_HOST', 'localhost') # WALLET_HOST = getenv('WALLET_HOST', 'localhost') #
WALLET_PORT = int(getenv('WALLET_PORT', 8888)) # WALLET_PORT = int(getenv('WALLET_PORT', 8888)) #
WALLET_PROTO = getenv('WALLET_PROTO', 'http') # WALLET_PROTO = getenv('WALLET_PROTO', 'http') #
@ -21,9 +22,6 @@ WALLET_RPC_USER = getenv('WALLET_RPC_USER', 'suchwow') #
WALLET_RPC_PASS = getenv('WALLET_RPC_PASS', 'suchwow') # WALLET_RPC_PASS = getenv('WALLET_RPC_PASS', 'suchwow') #
WALLET_PASS = getenv('WALLET_PASS', 'zzzzzzz') # You specify all these wallet details in .env WALLET_PASS = getenv('WALLET_PASS', 'zzzzzzz') # You specify all these wallet details in .env
# Optional for banning users who post crazy shit (they do)
BANNED_USERS = {'username': 'reason for the ban'}
# Optional for posting to Reddit # Optional for posting to Reddit
PRAW_CLIENT_SECRET = getenv('PRAW_CLIENT_SECRET', None) PRAW_CLIENT_SECRET = getenv('PRAW_CLIENT_SECRET', None)
PRAW_CLIENT_ID = getenv('PRAW_CLIENT_ID', None) PRAW_CLIENT_ID = getenv('PRAW_CLIENT_ID', None)
@ -42,5 +40,6 @@ MM_ENDPOINT = getenv('MM_ENDPOINT', 'ppppppppppppppppppppppppp')
# defaults # defaults
SESSION_TYPE = 'filesystem' SESSION_TYPE = 'filesystem'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'svg', 'mp4'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 MAX_CONTENT_LENGTH = 32 * 1024 * 1024
TEMPLATES_AUTO_RELOAD = getenv('TEMPLATES_AUTO_RELOAD', True)

@ -1,6 +1,8 @@
from flask import Blueprint, current_app from flask import Blueprint
from arrow import get as arrow_get from arrow import get as arrow_get
from suchwow.models import Moderator
bp = Blueprint('filters', 'filters') bp = Blueprint('filters', 'filters')
@ -13,3 +15,10 @@ def shorten_address(a):
@bp.app_template_filter('humanize') @bp.app_template_filter('humanize')
def humanize(d): def humanize(d):
return arrow_get(d).humanize() return arrow_get(d).humanize()
@bp.app_template_filter('is_moderator')
def is_moderator(s):
m = Moderator.select().where(Moderator.username == s)
if m:
return True
return False

@ -1,13 +1,26 @@
from peewee import * from random import choice
from os import path from os import path
from datetime import datetime from datetime import datetime
from peewee import *
from PIL import Image from PIL import Image
from suchwow import wownero from suchwow import wownero
from suchwow import config from suchwow import config
db = SqliteDatabase(f"{config.DATA_FOLDER}/sqlite.db") db = SqliteDatabase(f"{config.DATA_FOLDER}/sqlite.db")
ban_reasons = [
'you smell bad',
'didnt pass the vibe check, homie',
'your memes are bad and you should feel bad',
'i just dont like you'
]
def get_ban_reason():
return choice(ban_reasons)
class Post(Model): class Post(Model):
id = AutoField() id = AutoField()
title = CharField() title = CharField()
@ -123,3 +136,21 @@ class Notification(Model):
class Meta: class Meta:
database = db database = db
class Ban(Model):
id = AutoField()
user = ForeignKeyField(Profile)
reason = TextField()
timestamp = DateTimeField(default=datetime.now)
class Meta:
database = db
class AuditEvent(Model):
id = AutoField()
user = ForeignKeyField(Profile)
timestamp = DateTimeField(default=datetime.now)
action = CharField()
class Meta:
database = db

@ -1,4 +1,5 @@
from flask import jsonify, Blueprint, url_for, request, abort from flask import jsonify, Blueprint, url_for, request, abort
from suchwow.models import Post from suchwow.models import Post
from suchwow import wownero from suchwow import wownero

@ -1,6 +1,8 @@
import requests
from uuid import uuid4 from uuid import uuid4
import requests
from flask import session, redirect, url_for, request, Blueprint from flask import session, redirect, url_for, request, Blueprint
from suchwow import config from suchwow import config
@ -21,7 +23,7 @@ def login():
@bp.route("/logout") @bp.route("/logout")
def logout(): def logout():
session["auth"] = None session["auth"] = None
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/auth/") @bp.route("/auth/")
def auth(): def auth():
@ -66,4 +68,4 @@ def auth():
# user can now visit /secret # user can now visit /secret
session["auth"] = user_profile session["auth"] = user_profile
return redirect(url_for("index")) return redirect(url_for("main.index"))

@ -1,30 +0,0 @@
from flask import render_template, Blueprint, flash
from flask import request, redirect, url_for, session
from suchwow.models import Post, Comment, Profile
from suchwow.utils.decorators import login_required
bp = Blueprint("comment", "comment")
@bp.route("/comment/create/post/<post_id>", methods=["GET", "POST"])
@login_required
def create(post_id):
if not Post.filter(id=post_id):
flash("WTF, that post doesn't exist. Stop it, hackerman.")
return redirect(url_for("index"))
if request.method == "POST":
comment_text = request.form.get("comment")
if len(comment_text) > 300:
flash("WTF, too many characters to post, asshole.")
return redirect(request.url)
commenter = Profile.get(username=session["auth"]["preferred_username"])
post = Post.get(id=post_id)
c = Comment(
comment=comment_text,
commenter=commenter,
post=post,
)
c.save()
return redirect(url_for("post.read", id=post_id))
return render_template("comment/create.html")

@ -1,11 +1,6 @@
from datetime import datetime, timedelta from flask import render_template, Blueprint, request
from os import path
from flask import render_template, Blueprint, request, session, flash from suchwow.utils.helpers import get_top_posters, get_top_posts
from flask import send_from_directory, redirect, url_for, current_app
from werkzeug.utils import secure_filename
from suchwow import wownero
from suchwow.models import Post
from suchwow.utils.helpers import rw_cache, get_top_posters, get_top_posts
bp = Blueprint("leaderboard", "leaderboard") bp = Blueprint("leaderboard", "leaderboard")
@ -23,7 +18,7 @@ def top_posts():
except: except:
days = 1 days = 1
if days not in [1, 3, 7, 30]: if days not in [1, 3, 7, 30, 9999]:
days = 7 days = 7
posts = get_top_posts(days) posts = get_top_posts(days)

@ -0,0 +1,50 @@
from math import ceil
from flask import Blueprint, request, render_template, flash
from suchwow.models import Post, Profile, Moderator
from suchwow.utils.helpers import get_latest_tipped_posts
bp = Blueprint('main', 'main')
@bp.route("/")
def index():
itp = 15
page = request.args.get("page", 1)
submitter = request.args.get("submitter", None)
content = request.args.get("content", None)
if content == 'latest_tipped':
posts = get_latest_tipped_posts()
return render_template(
"index.html",
posts=posts[0:30],
title="Latest Tipped Memes"
)
try:
page = int(page)
except:
flash("Wow, wtf hackerman. Cool it.", "is-danger")
page = 1
posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc())
if submitter:
posts = posts.where(Post.submitter==submitter)
paginated_posts = posts.paginate(page, itp)
total_pages = ceil(posts.count() / itp)
return render_template(
"index.html",
posts=paginated_posts,
page=page,
total_pages=total_pages,
title="Latest Memes"
)
@bp.route("/about")
def about():
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template("about.html", mods=mods)

@ -0,0 +1,114 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request
from suchwow.models import AuditEvent, Post, Profile, Moderator, Ban, get_ban_reason
from suchwow.utils.decorators import moderator_required
from suchwow.utils.helpers import get_session_user, audit_event
from suchwow import config
bp = Blueprint("mod", "mod")
@bp.route('/mods')
@moderator_required
def main():
live_posts = Post.select().where(Post.approved == True).count()
pending_posts = Post.select().where(Post.approved == False).count()
active_posters = Profile.select().join(Post, on=Post.submitter == Profile.username).distinct().count()
mods = Moderator.select().count()
return render_template(
'mod/main.html',
live_posts=live_posts,
pending_posts=pending_posts,
active_posters=active_posters,
mods=mods
)
@bp.route('/mods/pending')
@moderator_required
def pending_posts():
posts = Post.select().where(Post.approved == False).order_by(Post.timestamp.asc())
if not posts:
flash('no posts pending', 'is-warning')
return redirect(url_for('mod.main'))
return render_template('mod/posts.html', posts=posts)
@bp.route('/mods/manage', methods=['GET', 'POST'])
@moderator_required
def manage_mods():
to_delete = request.args.get('delete')
if to_delete:
m = Moderator.select().where(Moderator.username == to_delete).first()
if not m:
flash('No moderator exists with that name', 'is-danger')
elif m.username == get_session_user():
flash('Cannot remove yourself.', 'is-danger')
elif m.username == config.SUPER_ADMIN:
flash('Cannot delete super admin you son-of-a-bitch.', 'is-danger')
else:
m.delete_instance()
audit_event(f'Deleted {to_delete} from mods')
flash(f'Removed {to_delete} from mods!', 'is-success')
return redirect(url_for('mod.manage_mods'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Moderator.select().where(Moderator.username == to_add).first():
flash(f'{to_add} is already a mod, ya dingus.', 'is-warning')
else:
m = Moderator(username=to_add)
m.save()
audit_event(f'Added {to_add} to mods')
flash(f'Added {to_add} to mods!', 'is-success')
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
return render_template('mod/manage.html', mods=mods)
@bp.route('/mods/bans', methods=['GET', 'POST'])
@moderator_required
def manage_bans():
to_delete = request.args.get('delete')
if to_delete:
ban = Ban.select().join(Profile).where(Profile.username == to_delete).first()
if not ban:
flash('No ban exists for that user', 'is-danger')
elif ban.user == get_session_user():
flash('Cannot ban yourself.', 'is-danger')
elif ban.user == config.SUPER_ADMIN:
flash('Cannot ban super admin you son-of-a-bitch.', 'is-danger')
else:
ban.delete_instance()
audit_event(f'Removed ban on {to_delete}')
flash(f'Unbanned {to_delete}!', 'is-success')
return redirect(url_for('mod.manage_bans'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Ban.select().join(Profile).where(Profile.username == to_add).first():
flash(f'{to_add} is already banned, ya dingus.', 'is-warning')
elif to_add == config.SUPER_ADMIN:
flash('Cannot ban the super admin you son-of-a-bitch.', 'is-danger')
else:
reason = request.form.get('reason')
if not reason:
reason = get_ban_reason()
ban = Ban(user=u, reason=reason)
ban.save()
audit_event(f'Banned {to_add} ({reason})')
flash(f'Banned {to_add}!', 'is-success')
bans = Ban.select()
return render_template('mod/bans.html', bans=bans)
@bp.route('/mods/logs')
@moderator_required
def view_logs():
events = AuditEvent.select().order_by(AuditEvent.timestamp.desc()).limit(50)
return render_template('mod/logs.html', logs=events)

@ -1,19 +1,18 @@
from datetime import datetime, timedelta
from os import path, remove from os import path, remove
from io import BytesIO from io import BytesIO
from base64 import b64encode from base64 import b64encode
from secrets import token_urlsafe
from qrcode import make as qrcode_make from qrcode import make as qrcode_make
from flask import render_template, Blueprint, request, session, flash from flask import render_template, Blueprint, request, flash
from flask import send_from_directory, redirect, url_for, current_app from flask import send_from_directory, redirect, url_for, current_app
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from secrets import token_urlsafe
from suchwow import wownero from suchwow import wownero
from suchwow import config from suchwow.models import Post, Profile, Comment, Ban
from suchwow.models import Post, Comment
from suchwow.utils.decorators import login_required, profile_required, moderator_required from suchwow.utils.decorators import login_required, profile_required, moderator_required
from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user
from suchwow.utils.helpers import rw_cache, post_webhook from suchwow.utils.helpers import audit_event
from suchwow.reddit import make_post
from suchwow.discord import post_discord_webhook from suchwow.discord import post_discord_webhook
@ -28,7 +27,7 @@ def read(id):
post = Post.get(id=id) post = Post.get(id=id)
if not post.approved: if not post.approved:
if not is_moderator(get_session_user()): if not is_moderator(get_session_user()):
flash("That post has not been approved.") flash("That post has not been approved.", "is-warning")
return redirect("/") return redirect("/")
if wallet.connected: if wallet.connected:
address = wallet.get_address(account=post.account_index) address = wallet.get_address(account=post.account_index)
@ -47,32 +46,33 @@ def read(id):
qr_code=qr_code qr_code=qr_code
) )
else: else:
flash("No meme there, brah") flash("No meme there, brah", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/post/create", methods=["GET", "POST"]) @bp.route("/post/create", methods=["GET", "POST"])
@login_required @login_required
@profile_required @profile_required
def create(): def create():
submitter = get_session_user()
u = Profile.filter(username=submitter)
banned = Ban.filter(user=u).first()
if banned:
flash(f"You can't post: {banned.reason}", "is-danger")
return redirect("/")
if request.method == "POST": if request.method == "POST":
submitter = get_session_user()
if submitter in config.BANNED_USERS:
reason = config.BANNED_USERS[submitter]
flash(f"You can't post for now: {reason}")
return redirect("/")
post_title = request.form.get("title") post_title = request.form.get("title")
# check if the post request has the file part # check if the post request has the file part
if "file" not in request.files: if "file" not in request.files:
flash("You didn't upload a caliente meme, bro! You're fuckin up!") flash("You didn't upload a caliente meme, bro! You're fuckin up!", "is-danger")
return redirect(request.url) return redirect(request.url)
file = request.files["file"] file = request.files["file"]
# if user does not select file, browser also # if user does not select file, browser also
# submit an empty part without filename # submit an empty part without filename
if file.filename == "": if file.filename == "":
flash("You didn't upload a caliente meme, bro! You're fuckin up!") flash("You didn't upload a caliente meme, bro! You're fuckin up!", "is-danger")
return redirect(request.url) return redirect(request.url)
if post_title == "": if post_title == "":
flash("You didn't give your meme a spicy title, bro! You're fuckin up!") flash("You didn't give your meme a spicy title, bro! You're fuckin up!", "is-danger")
return redirect(request.url) return redirect(request.url)
if file and allowed_file(file.filename): if file and allowed_file(file.filename):
filename = "{}-{}".format( filename = "{}-{}".format(
@ -85,8 +85,12 @@ def create():
try: try:
wallet = wownero.Wallet() wallet = wownero.Wallet()
account_index = wallet.new_account() account_index = wallet.new_account()
in_use = Post.select().where(Post.account_index == account_index).first()
if in_use:
flash("Suchwow wallet is fucked up! Try again later.", "is-danger")
return redirect(request.url)
except: except:
flash("Suchwow wallet is fucked up! Try again later.") flash("Suchwow wallet is fucked up! Try again later.", "is-danger")
return redirect(request.url) return redirect(request.url)
post = Post( post = Post(
title=post_title, title=post_title,
@ -99,9 +103,9 @@ def create():
post.save() post.save()
post.save_thumbnail() post.save_thumbnail()
url = url_for('post.read', id=post.id, _external=True) url = url_for('post.read', id=post.id, _external=True)
post_webhook(f"New post :doge2: [{post.id}]({url}) by `{submitter}` :neckbeard:") audit_event(f'Created new post {post.id}')
flash("New post created and pending approval!") flash("New post created and pending approval!", "is-success")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return render_template("post/create.html") return render_template("post/create.html")
@bp.route("/post/<id>/approve") @bp.route("/post/<id>/approve")
@ -113,25 +117,14 @@ def approve(id):
if not post.approved: if not post.approved:
post.approved = True post.approved = True
post.save() post.save()
post_webhook(f"Post [{post.id}]({url}) approved :white_check_mark: by `{get_session_user()}` :fieri_parrot:") flash("Approved", "is-success")
flash("Approved") audit_event(f'Approved post {post.id}')
if current_app.config["DEBUG"] is False: if current_app.config["DEBUG"] is False:
# _r = None
# _d = None
# if not post.to_reddit:
# _r = make_post(post)
# if not post.to_discord:
# _d = post_discord_webhook(post)
# if _r and _d:
# post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Reddit and Discord.")
# else:
# post_webhook(f"Post [{post.id}]({url}) failed :this-is-fine-fire: to post to socials...Reddit: {_r} - Discord: {_d}")
post_discord_webhook(post) post_discord_webhook(post)
post_webhook(f"Post [{post.id}]({url}) submitted :dab_parrot: to Discord.") return redirect(url_for("mod.pending_posts"))
return redirect(url_for("mod_queue"))
else: else:
flash("You can't approve this") flash("You can't approve this", "is-success")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/post/<id>/delete") @bp.route("/post/<id>/delete")
@login_required @login_required
@ -144,20 +137,23 @@ def delete(id):
if user == post.submitter or is_mod: if user == post.submitter or is_mod:
save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads") save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads")
save_path = path.join(save_path_base, post.image_name) save_path = path.join(save_path_base, post.image_name)
remove(save_path) try:
remove(save_path)
except:
pass
audit_event(f'Deleted post {post.id}')
post.delete_instance() post.delete_instance()
post_webhook(f"Post {post.id} deleted :dumpsterfire: by `{user}` :godmode:") flash("Deleted that shit, brah!", "is-success")
flash("Deleted that shit, brah!")
if is_mod: if is_mod:
return redirect(url_for("mod_queue")) return redirect(url_for("mod.pending_posts"))
else: else:
return redirect(url_for("index")) return redirect(url_for("main.index"))
else: else:
flash("You can't delete a meme you don't own, brah") flash("You can't delete a meme you don't own, brah", "is-warning")
return redirect(url_for("post.read", id=post.id)) return redirect(url_for("post.read", id=post.id))
else: else:
flash("No meme there, brah") flash("No meme there, brah", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
@bp.route("/uploads/<path:filename>") @bp.route("/uploads/<path:filename>")
def uploaded_file(filename): def uploaded_file(filename):

@ -26,7 +26,7 @@ def edit():
profile.save() profile.save()
return redirect(request.args.get("redirect", "/")) return redirect(request.args.get("redirect", "/"))
else: else:
flash("WTF bro, that's not a valid Wownero address") flash("WTF bro, that's not a valid Wownero address", "is-warning")
return redirect(request.url) return redirect(request.url)
if profile_exists: if profile_exists:
profile = Profile.get(username=un) profile = Profile.get(username=un)

@ -12,8 +12,9 @@
<br /> <br />
<br /> <br />
<p>If you've made WOW from memes, please consider donating to our mod team - we work hard to ensure only the finest quality memes are shown for your viewing pleasure:</p> <p>If you've made WOW from memes, please consider donating to our mod team - we work hard to ensure only the finest quality memes are shown for your viewing pleasure:</p>
<p><strong>qvqc (top mod)</strong>: WW3rVRuwdB5Bb95mZuGpunguz7Ers3Gao6EGLYZ6z2uEA9RsYzbZN1rfT74gzEDxyfNSVcpnMvnUhZTLfjiE73eu29ErXTDi4</p> {% for mod in mods %}
<p><strong>lza_menace (dev)</strong>: WW3CRUnpWnAQmXyr8rgd5qVneg3tTKbRrZu2qbTzjwMNEtvddodV2inPbRSGjcdRyHKVNsNkwWccjN6iKu1FAGr32hqKzikQP</p> <p><strong>{{ mod.username }}</strong> - {{ mod.address or '? (update your profile, bub!)' }}</p>
{% endfor %}
<br /> <br />
<br /> <br />
<br /> <br />

@ -55,10 +55,10 @@
{% include 'navbar.html' %} {% include 'navbar.html' %}
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for category, message in messages %}
<div class="notification is-danger container is-light"> <div class="notification {{ category | default('is-danger') }} container is-light">
{{ message }} {{ message }}
</div> </div>
{% endfor %} {% endfor %}
@ -70,13 +70,17 @@
<!-- Page Content --> <!-- Page Content -->
{% block content %} {% endblock %} {% block content %} {% endblock %}
<div style="padding-bottom:10em;"></div>
<!-- Footer --> <!-- Footer -->
{% block footer %}
<footer class="footer"> <footer class="footer">
<div class="content has-text-centered"> <div class="content has-text-centered">
<p><strong>SuchWow</strong> by <a href="https://lzahq.tech" target="_blank">lza_menace</a></p> <p><strong>SuchWow</strong> by <a href="https://lzahq.tech" target="_blank">lza_menace</a></p>
<p><a href="https://wownero.org/" target="_blank">Learn more about the infamous shitcoin, Wownero</a>.</p> <p><a href="https://wownero.org/" target="_blank">Learn more about the infamous shitcoin, Wownero</a>.</p>
</div> </div>
</footer> </footer>
{% endblock %}
<!-- JS --> <!-- JS -->
<script> <script>

@ -1,19 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="width:40%;">
<div class="submit">
<h1>Leave a Comment</h1>
<form method=post enctype=multipart/form-data class="form-horizontal">
<div class="form-group">
<label class="sr-only" for="inlineFormInput">Text</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Comment text (max 300 chars)" name="comment">
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</form>
</div>
</div>
{% endblock %}

@ -16,13 +16,20 @@
{% if posts %} {% if posts %}
{% for row in posts | batch(4) %} {% for row in posts | batch(4) %}
<div class="columns"> <div class="columns">
{% for post in row %} {% for p in row %}
{% set post = post.show() %} {% set post = p.show() %}
<div class="column"> <div class="column">
<div class="card"> <div class="card">
<div class="card-image"> <div class="card-image">
<a href="{{ url_for('post.read', id=post.id) }}"> <a href="{{ url_for('post.read', id=post.id) }}">
<img src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" alt="Placeholder image"> {% if p.get_image_path().endswith('mp4') %}
<video style="max-height: 100vh!important;" controls>
<source src="{{ url_for('post.uploaded_file', filename=p.image_name) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" />
{% endif %}
</a> </a>
</div> </div>
<div class="card-content"> <div class="card-content">

@ -26,7 +26,7 @@
{% endif %} {% endif %}
<hr> <hr>
<a href="{{ url_for('index') }}"><button class="btn btn-warning">Go Home</button></a> <a href="{{ url_for('main.index') }}"><button class="btn btn-warning">Go Home</button></a>
</div> </div>

@ -0,0 +1,51 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Manage Mods</h1>
<section class="section">
{% for ban in bans %}
<article class="message" style="width: 30%; margin: 2em auto;">
<div class="message-header">
<p>{{ ban.user.username }}</p>
<a href="?delete={{ ban.user.username }}" class="delete"></a>
</div>
<div class="message-body">
{{ ban.reason }}
</div>
</article>
{% endfor %}
</section>
<div class="container" style="text-align:left; width: 30%;">
<form method="post">
<div class="field">
<label class="label">Ban User</label>
<div class="control">
<input class="input" type="text" placeholder="Username" name="username">
</div>
</div>
<div class="field">
<label class="label">Reason</label>
<div class="control">
<textarea class="textarea" placeholder="Reason for ban" name="reason"></textarea>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
<div class="control">
<button class="button is-link is-light">Cancel</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">View Logs</h1>
<section class="section">
{% for log in logs %}
<article class="message" style="width: 40%; margin: 1em auto;">
<div class="message-header">
<p>{{ log.user.username }} - {{ log.timestamp | humanize }} </p>
</div>
<div class="message-body">
{{ log.action }}
</div>
</article>
{% endfor %}
</section>
</div>
{% endblock %}
{% block footer %}{% endblock %}

@ -0,0 +1,72 @@
{% extends 'base.html' %}
{% block content %}
<div class="container content" style="padding-top: 4em;">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Live Memes</p>
<p class="title">{{ live_posts }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Pending Memes</p>
<p class="title">{{ pending_posts }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Active Posters</p>
<p class="title">{{ active_posters }}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Mods</p>
<p class="title">{{ mods }}</p>
</div>
</div>
</nav>
<div class="tabs is-toggle is-fullwidth">
<ul>
<li>
<a href="{{ url_for('mod.pending_posts') }}">
<span class="icon is-small"></span>
<span>Manage Queue</span>
</a>
</li>
<li>
<a href="{{ url_for('mod.manage_bans') }}">
<span class="icon is-small"></span>
<span>Manage Bans</span>
</a>
</li>
<li>
<a href="{{ url_for('mod.manage_mods') }}">
<span class="icon is-small"></span>
<span>Manage Mods</span>
</a>
</li>
<li>
<a href="{{ url_for('mod.view_logs') }}">
<span class="icon is-small"></span>
<span>View Logs</span>
</a>
</li>
</ul>
</div>
<figure>
<img src="https://suchwow.xyz/uploads/pg1VwHJWeKT5dWXy-wowcomfysemifinal.gif">
</figure>
<div style="padding-bottom:10em;"></div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

@ -0,0 +1,42 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Manage Mods</h1>
<section class="section">
{% for mod in mods %}
<div class="block">
<span class="tag is-large">
{{ mod.username }}
<a href="?delete={{ mod.username }}" class="delete"></a>
</span>
</div>
{% endfor %}
</section>
<div class="container" style="text-align:left; width: 30%;">
<form method="post">
<div class="field">
<label class="label">Add New Mod</label>
<div class="control">
<input class="input" type="text" placeholder="Username" name="username">
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
<div class="control">
<button class="button is-link is-light">Cancel</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block footer %}{% endblock %}

@ -0,0 +1,61 @@
{% extends 'base.html' %}
{% block content %}
<div class="container" style="text-align:center;">
<h1 class="title">Pending Posts</h1>
<section class="section">
{% if posts %}
{% for row in posts | batch(4) %}
<div class="columns">
{% for p in row %}
{% set post = p.show() %}
<div class="column">
<div class="card">
<div class="card-image">
<a href="{{ url_for('post.read', id=p.id) }}">
{% if p.get_image_path().endswith('mp4') %}
<video style="max-height: 100vh!important;" controls>
<source src="{{ url_for('post.uploaded_file', filename=p.image_name) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" />
{% endif %}
</a>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">
<a href="{{ url_for('post.read', id=p.id) }}">{{ post.title }}</a>
</p>
<p class="subtitle is-6"><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></p>
</div>
</div>
<div class="content">
{{ post.text | truncate(60) }}
<time datetime="2016-1-1">{{ post.timestamp.year }}-{{ post.timestamp.month }}-{{ post.timestamp.day }} {{ post.timestamp.hour }}:{{ post.timestamp.minute }} UTC</time>
<p>({{ post.timestamp | humanize }})</p>
</div>
</div>
<footer class="card-footer">
<a href="{{ url_for('post.approve', id=post.id) }}" class="card-footer-item" style="color:green;"><strong>Approve</strong></a>
<a href="{{ url_for('post.delete', id=post.id) }}" class="card-footer-item" style="color:red;"><strong>Deny</strong></a>
</footer>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{% else %}
<p>No posts pending!</p>
{% endif %}
</section>
</div>
{% endblock %}
{% block footer %}{% endblock %}

@ -13,7 +13,7 @@
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="{{ url_for('index') }}">Home</a> <a class="navbar-item" href="{{ url_for('main.index') }}">Home</a>
<div class="navbar-item has-dropdown is-hoverable"> <div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Leaderboards</a> <a class="navbar-link">Leaderboards</a>
@ -23,12 +23,15 @@
<a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=3">Top Memes Last 3 Days</a> <a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=3">Top Memes Last 3 Days</a>
<a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=7">Top Memes Last Week</a> <a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=7">Top Memes Last Week</a>
<a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=30">Top Memes Last Month</a> <a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=30">Top Memes Last Month</a>
<a class="navbar-item" href="{{ url_for('leaderboard.top_posts') }}?days=9999">Top Memes All Time</a>
</div> </div>
</div> </div>
<a class="navbar-item" href="{{ url_for('about') }}">About</a> <a class="navbar-item" href="{{ url_for('main.about') }}">About</a>
<a class="navbar-item" href="{{ url_for('post.create') }}">Submit</a> <a class="navbar-item" href="{{ url_for('post.create') }}">Submit</a>
{% if session.auth %} {% if 'auth' in session %}
<a class="navbar-item" style="color: orange;" href="{{ url_for('mod_queue') }}">Mods!</a> {% if session.auth.preferred_username | is_moderator %}
<a class="navbar-item" style="color: orange;" href="{{ url_for('mod.main') }}">Mods!</a>
{% endif %}
{% endif %} {% endif %}
</div> </div>
@ -69,7 +72,7 @@
<a class="nav-link" href="{{ url_for('post.create') }}">Submit</a> <a class="nav-link" href="{{ url_for('post.create') }}">Submit</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">About</a> <a class="nav-link" href="{{ url_for('main.about') }}">About</a>
</li> </li>
{% if session.auth == None %} {% if session.auth == None %}
<li class="nav-item"> <li class="nav-item">

@ -9,25 +9,63 @@
<ol> <ol>
<li>no super low effort memes</li> <li>no super low effort memes</li>
<li>no nsfl and nsfw</li> <li>no nsfl and nsfw</li>
<li>must pertain to wownero or crypto in general</li> <!-- <li>must pertain to wownero or crypto in general</li> -->
</ol> </ol>
<form method=post enctype=multipart/form-data class="form-horizontal"> <form method=post enctype=multipart/form-data class="form-horizontal">
<div class="form-group"> <div class="field">
<label class="sr-only" for="inlineFormInput">Title</label> <label class="label">Title</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Title" name="title"> <div class="control">
<input class="input" type="text" placeholder="So there I was..." name="title">
</div>
</div> </div>
<div class="form-group"> <div class="field">
<label class="sr-only" for="inlineFormInput">Text</label> <label class="label">Text</label>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="inlineFormInput" placeholder="Text (optional)" name="text"> <div class="control">
<textarea class="textarea" placeholder="balls deep in a jar of mayonnaise" name="text"></textarea>
</div>
</div> </div>
<div class="form-group"> <div id="file-js-example" class="file has-name">
<input type=file name=file> <label class="file-label">
<input class="file-input" type="file" name="file">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Upload a spicy meme
</span>
</span>
<span class="file-name">
No file uploaded
</span>
</label>
</div> </div>
<div class="form-group"> <div class="field mt-4">
<button type="submit" class="btn btn-success">Submit</button> <div class="control">
<label class="checkbox">
<input type="checkbox">
I agree to be hella based
</label>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<script>
const fileInput = document.querySelector('#file-js-example input[type=file]');
fileInput.onchange = () => {
if (fileInput.files.length > 0) {
const fileName = document.querySelector('#file-js-example .file-name');
fileName.textContent = fileInput.files[0].name;
}
}
</script>
{% endblock %} {% endblock %}

@ -21,11 +21,19 @@
<h1>{{ post.title }}</h1> <h1>{{ post.title }}</h1>
<p>{{ post.text }}</p> <p>{{ post.text }}</p>
{% if not post.approved %} {% if not post.approved %}
<a href="{{ url_for('post.approve', id=post.id) }}"><button type="button" name="button">Approve</button></a> <a href="{{ url_for('post.approve', id=post.id) }}" class="button is-success">Approve</a>
<a href="{{ url_for('post.delete', id=post.id) }}"><button type="button" name="button">Reject</button></a> <a href="{{ url_for('post.delete', id=post.id) }}" class="button is-danger">Reject</a>
{% endif %}
<p class="mt-2">Submitted by <i><u><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></u></i> at <i>{{ post.timestamp }}</i></p>
<!-- <img src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" width=600/ style="border-radius:4px;"> -->
{% if post.get_image_path().endswith('mp4') %}
<video style="max-height: 100vh!important;" controls>
<source src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" />
{% endif %} {% endif %}
<p>Submitted by <i><u><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></u></i> at <i>{{ post.timestamp }}</i></p>
<img src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" width=600/ style="border-radius:4px;">
</div> </div>
</section> </section>

@ -10,6 +10,7 @@
{% if posts %} {% if posts %}
{% for row in posts | sort(attribute='received_wow', reverse=True) | batch(4) %} {% for row in posts | sort(attribute='received_wow', reverse=True) | batch(4) %}
<div class="columns"> <div class="columns">
{% if loop.index < 15 %}
{% for post in row %} {% for post in row %}
<div class="column"> <div class="column">
<div class="card"> <div class="card">
@ -37,6 +38,7 @@
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
{% else %} {% else %}

@ -20,8 +20,8 @@ def moderator_required(f):
if m: if m:
return f(*args, **kwargs) return f(*args, **kwargs)
else: else:
flash("You are not a moderator") flash("You are not a moderator", "is-warning")
return redirect(url_for("index")) return redirect(url_for("main.index"))
return decorated_function return decorated_function
def profile_required(f): def profile_required(f):

@ -1,10 +1,10 @@
import pickle import pickle
from os import path, remove from os import path
from datetime import datetime, timedelta from datetime import datetime, timedelta
from requests import post as r_post
from json import dumps from flask import session
from flask import session, current_app
from suchwow.models import Moderator, Post from suchwow.models import Moderator, Post, AuditEvent, Profile
from suchwow.wownero import Wallet, from_atomic from suchwow.wownero import Wallet, from_atomic
from suchwow import config from suchwow import config
@ -20,27 +20,19 @@ def is_moderator(username):
else: else:
return False return False
def get_profile():
p = Profile.filter(username=get_session_user()).first()
return p
def audit_event(event):
e = AuditEvent(user=get_profile(), action=event)
e.save()
def get_session_user(): def get_session_user():
if "auth" not in session or not session["auth"]: if "auth" not in session or not session["auth"]:
return None return None
return session["auth"]["preferred_username"].strip() return session["auth"]["preferred_username"].strip()
def post_webhook(msg):
try:
if current_app.config["DEBUG"]:
msg = "[DEBUG] " + msg
data = {
"text": msg,
"channel": config.MM_CHANNEL,
"username": config.MM_USERNAME,
"icon_url": config.MM_ICON
}
res = r_post(config.MM_ENDPOINT, data=dumps(data))
res.raise_for_status()
return True
except:
return False
def get_latest_tipped_posts(): def get_latest_tipped_posts():
key_name = 'latest_tips' key_name = 'latest_tips'
posts = [] posts = []
@ -96,7 +88,8 @@ def get_top_posts(days=1):
except: except:
days = 1 days = 1
if days not in [1, 3, 7, 30]: # stupid magic number bcuz fuck it
if days not in [1, 3, 7, 30, 9999]:
days = 7 days = 7
hours = 24 * days hours = 24 * days

@ -33,12 +33,16 @@ class Wallet(object):
r = requests.get( r = requests.get(
self.endpoint, self.endpoint,
data=json.dumps({'method': method, 'params': params}), data=json.dumps({'method': method, 'params': params}),
auth=self.auth auth=self.auth,
timeout=10
) )
if 'error' in r.json(): if 'error' in r.json():
return r.json()['error'] return r.json()['error']
else: else:
return r.json()['result'] return r.json()['result']
def store(self):
self.make_wallet_rpc('store')
def height(self): def height(self):
return self.make_wallet_rpc('get_height', {}) return self.make_wallet_rpc('get_height', {})
@ -58,6 +62,7 @@ class Wallet(object):
def new_account(self, label=None): def new_account(self, label=None):
_account = self.make_wallet_rpc('create_account', {'label': label}) _account = self.make_wallet_rpc('create_account', {'label': label})
self.store()
return _account['account_index'] return _account['account_index']
def addresses(self, account, addr_indices=None): def addresses(self, account, addr_indices=None):
@ -83,6 +88,7 @@ class Wallet(object):
def new_address(self, account, label=None): def new_address(self, account, label=None):
data = {'account_index': account, 'label': label} data = {'account_index': account, 'label': label}
_address = self.make_wallet_rpc('create_address', data) _address = self.make_wallet_rpc('create_address', data)
self.store()
return (_address['address_index'], _address['address']) return (_address['address_index'], _address['address'])
def transfers(self, account, address_indices=[]): def transfers(self, account, address_indices=[]):
@ -113,6 +119,7 @@ class Wallet(object):
'ring_size': 22 'ring_size': 22
} }
transfer = self.make_wallet_rpc('transfer', data) transfer = self.make_wallet_rpc('transfer', data)
self.store()
return transfer return transfer
def sweep_all(self, account, dest_address): def sweep_all(self, account, dest_address):
@ -121,6 +128,7 @@ class Wallet(object):
'account_index': account, 'account_index': account,
} }
sweep = self.make_wallet_rpc('sweep_all', data) sweep = self.make_wallet_rpc('sweep_all', data)
self.store()
return sweep return sweep
def incoming_transfers(self, account, transfer_type='all', verbose=True): def incoming_transfers(self, account, transfer_type='all', verbose=True):

Loading…
Cancel
Save