Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
0d803fa182 | |||
3cdce2b1ab | |||
9b6a46ba9d | |||
9c0dc092bd | |||
e7d05f9e73 | |||
7a8f7bf65c | |||
ef24163a7a | |||
af340170a5 | |||
9bca12affa | |||
af2d8d636d | |||
07a13d42f9 | |||
8552738423 | |||
78db967922 | |||
d1f1e829c3 | |||
d6ab920737 | |||
6884aefb1d |
8 changed files with 200 additions and 81 deletions
1
Pipfile
1
Pipfile
|
@ -7,6 +7,7 @@ name = "pypi"
|
||||||
flask = "*"
|
flask = "*"
|
||||||
flask-uploads = "*"
|
flask-uploads = "*"
|
||||||
wand = "*"
|
wand = "*"
|
||||||
|
gunicorn = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
|
10
Pipfile.lock
generated
10
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "2b757113898edc814da1a9d4b93adea38e861ac3590944a8cae1d77602fe06db"
|
"sha256": "64858e875b29283189ce9e29e098ccb1b84744bdfba378646b95c4fa9f5770b7"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -38,6 +38,14 @@
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.2.1"
|
"version": "==0.2.1"
|
||||||
},
|
},
|
||||||
|
"gunicorn": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7ef2b828b335ed58e3b64ffa84caceb0a7dd7c5ca12f217241350dec36a1d5dc",
|
||||||
|
"sha256:bc59005979efb6d2dd7d5ba72d99f8a8422862ad17ff3a16e900684630dd2a10"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==19.8.1"
|
||||||
|
},
|
||||||
"itsdangerous": {
|
"itsdangerous": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"webcomponentsjs": "^2.0.1",
|
"webcomponentsjs": "^2.0.1",
|
||||||
"vaadin-upload": "^4.1.0",
|
"vaadin-upload": "^4.1.0",
|
||||||
"clipboard-copy": "advanced-rest-client/clipboard-copy#^2.0.1",
|
"clipboard-copy": "advanced-rest-client/clipboard-copy#^2.0.1",
|
||||||
"paper-tooltip": "PolymerElements/paper-tooltip#^2.1.1"
|
"paper-tooltip": "PolymerElements/paper-tooltip#^2.1.1",
|
||||||
|
"paper-toggle-button": "PolymerElements/paper-toggle-button#^2.1.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"webcomponentsjs": "^v1.1.0"
|
"webcomponentsjs": "^v1.1.0"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/env/bin python3
|
#!/usr/env/bin python3
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from wand.image import Image
|
from wand.image import Image
|
||||||
from wand.drawing import Drawing
|
from wand.drawing import Drawing
|
||||||
|
@ -14,50 +15,53 @@ app.config.from_pyfile('settings.cfg')
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html')
|
return render_template('index.html')
|
||||||
|
|
||||||
@app.route('/itmeirl-bot')
|
@app.route('/itmeirlbot-protect')
|
||||||
def meirl_index():
|
def meirl_index():
|
||||||
return render_template('itmeirl-bot/index.html')
|
return render_template('itmeirlbot-protect/index.html')
|
||||||
|
|
||||||
@app.route('/itmeirl-bot/upload', methods=['POST'])
|
@app.route('/itmeirlbot-protect/upload', methods=['POST'])
|
||||||
def meirl_upload():
|
def meirl_upload():
|
||||||
if 'meme' in request.files:
|
if 'meme' in request.files:
|
||||||
directory = os.path.join(app.config['UPLOAD_DIR'], 'itmeirl')
|
directory = os.path.join(app.config['UPLOAD_DIR'], 'itmeirlbot-protect')
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
|
filename = str(uuid.uuid4()) + '.jpg'
|
||||||
|
path = os.path.join(directory, filename)
|
||||||
|
|
||||||
watermark = Image(filename=os.path.join('static', 'watermark.png'))
|
|
||||||
meme = Image(file=request.files['meme'])
|
meme = Image(file=request.files['meme'])
|
||||||
with Image(width=meme.width, height=meme.height + watermark.height) as img:
|
|
||||||
img.composite(meme, 0, 0)
|
|
||||||
watermark.resize(width=meme.width)
|
|
||||||
img.composite(watermark, 0, meme.height)
|
|
||||||
|
|
||||||
img.format = 'jpeg'
|
if request.form.get('nomolest', 'off') == 'on':
|
||||||
filename = str(uuid.uuid4()) + '.jpg'
|
meme.format = 'jpeg'
|
||||||
path = os.path.join(directory, filename)
|
meme.save(filename=path)
|
||||||
img.save(filename=path)
|
else:
|
||||||
watermark.close()
|
watermark = Image(filename=os.path.join('static', 'watermark.png'))
|
||||||
|
with Image(width=meme.width, height=meme.height + watermark.height) as img:
|
||||||
|
img.composite(meme, 0, 0)
|
||||||
|
watermark.resize(width=meme.width)
|
||||||
|
img.composite(watermark, 0, meme.height)
|
||||||
|
|
||||||
|
img.format = 'jpeg'
|
||||||
|
img.save(filename=path)
|
||||||
|
watermark.close()
|
||||||
meme.close()
|
meme.close()
|
||||||
|
protocol = urlparse(request.url).scheme
|
||||||
url = url_for('meirl_show_reddit', file=filename[:-4])
|
url = protocol + '://i.redd.it.' + request.host + url_for('meirl_show_reddit', file=filename[:-4])
|
||||||
print(request.args)
|
|
||||||
if request.args.get('noredirect') is not None:
|
if request.args.get('noredirect') is not None:
|
||||||
return url
|
return url
|
||||||
else:
|
else:
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
|
|
||||||
|
|
||||||
# TODO: do these via nginx before you kill your server
|
# Expecting lots of traffic? Do these via nginx before you kill your server
|
||||||
@app.route('/itmeirl-bot/files/<file>')
|
@app.route('/files/itmeirlbot-protect/<file>')
|
||||||
def meirl_show_reddit(file):
|
def meirl_show_reddit(file):
|
||||||
directory = os.path.join(app.config['UPLOAD_DIR'], 'itmeirl')
|
directory = os.path.join(app.config['UPLOAD_DIR'], 'itmeirlbot-protect')
|
||||||
return send_from_directory(directory, file + '.jpg')
|
return send_from_directory(directory, file + '.jpg')
|
||||||
|
|
||||||
@app.route('/itmeirl-bot/files/<file>.jpg')
|
@app.route('/files/itmeirlbot-protect/<file>.jpg')
|
||||||
def meirl_show_meirlbot(file):
|
def meirl_show_meirlbot(file):
|
||||||
return app.send_static_file('bad.jpg')
|
return app.send_static_file('bad.jpg')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/components/<path:path>')
|
@app.route('/components/<path:path>')
|
||||||
def components(path):
|
def components(path):
|
||||||
return send_from_directory(os.path.join('static', 'components'), path)
|
return send_from_directory(os.path.join('static', 'components'), path)
|
||||||
|
|
|
@ -28,6 +28,39 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 16px 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#upload_form {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
#upload_form form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#upload_input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px 0px;
|
||||||
|
}
|
||||||
|
#upload_button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#upload_checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
#upload_checkbox > input {
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#options {
|
||||||
|
padding: 16px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +101,14 @@ body {
|
||||||
animation-duration: 0.5s;
|
animation-duration: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.codeblock {
|
||||||
|
background-color: var(--secondary-background-color);
|
||||||
|
color: var(--secondary-foreground-color);
|
||||||
|
font-family: 'Roboto Mono', monospace;
|
||||||
|
padding: 8px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
a.nostyle:link {
|
a.nostyle:link {
|
||||||
text-decoration: inherit;
|
text-decoration: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
{% extends '/skel.html' %}
|
|
||||||
{% block imports %}
|
|
||||||
<link rel="import" href="{{ url_for('components', path='vaadin-upload/vaadin-upload.html') }}">
|
|
||||||
<link rel="import" href="{{ url_for('components', path='polymer/lib/elements/dom-repeat.html') }}">
|
|
||||||
<link rel="import" href="{{ url_for('custom_components', path='vaadin-permalinked-upload-file.html') }}">
|
|
||||||
{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<div id="mainwrapper">
|
|
||||||
<h1>
|
|
||||||
Excuse me sir, that meme appears to be <i>stolen</i>
|
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
Protect your memes from the evil clutches of @ItMeIRL.
|
|
||||||
</p>
|
|
||||||
<div id="upload_form">
|
|
||||||
<vaadin-upload id="file_upload" files="{%raw%}{{files}}{%endraw%}" target="{{ url_for('meirl_upload') }}?noredirect" method="POST" form-data-name="meme" accept="image/*" max-file-size="5000000">
|
|
||||||
<div slot="drop-label-icon"></div>
|
|
||||||
<span slot="drop-label" class="font-headline">or drag a file here (5MB maximum)</span>
|
|
||||||
<div slot="file-list">
|
|
||||||
<template is="dom-repeat" items="[[files]]" as="file">
|
|
||||||
<vaadin-permalinked-upload-file file="[[file]]"></vaadin-permalinked-upload-file>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</vaadin-upload>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
{% block script %}
|
|
||||||
<script>
|
|
||||||
window.addEventListener('WebComponentsReady', function() {
|
|
||||||
var upload = document.querySelector('vaadin-upload#file_upload');
|
|
||||||
|
|
||||||
upload.addEventListener('upload-response', function(event) {
|
|
||||||
console.log(event.detail);
|
|
||||||
event.detail.file.url = window.location.protocol + "//" + window.location.host + event.detail.xhr.response;
|
|
||||||
// window.location.href = event.detail.xhr.response;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
118
templates/itmeirlbot-protect/index.html
Normal file
118
templates/itmeirlbot-protect/index.html
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
{% extends '/skel.html' %}
|
||||||
|
{% block imports %}
|
||||||
|
<script src="{{ url_for('components', path='webcomponentsjs/webcomponents-loader.js') }}"></script>
|
||||||
|
<link rel="import" href="{{ url_for('components', path='polymer/lib/elements/dom-bind.html') }}">
|
||||||
|
<link rel="import" href="{{ url_for('components', path='polymer/lib/elements/dom-repeat.html') }}">
|
||||||
|
<link rel="import" href="{{ url_for('components', path='polymer/lib/elements/dom-if.html') }}">
|
||||||
|
<link rel="import" href="{{ url_for('components', path='vaadin-upload/vaadin-upload.html') }}">
|
||||||
|
<link rel="import" href="{{ url_for('custom_components', path='vaadin-permalinked-upload-file.html') }}">
|
||||||
|
<link rel="import" href="{{ url_for('components', path='paper-toggle-button/paper-toggle-button.html') }}">
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div id="mainwrapper">
|
||||||
|
<h1>
|
||||||
|
twitt normies get <i>hecked</i>
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
Protect your memes from the evil clutches of @ItMeIRL.
|
||||||
|
</p>
|
||||||
|
<div id="upload_form">
|
||||||
|
<form class="noscript" action="{{ url_for('meirl_upload')}}" method="POST" enctype="multipart/form-data">
|
||||||
|
<input id="upload_input" name="meme" type="file" accept="image/*"></input>
|
||||||
|
<div id="upload_checkbox">
|
||||||
|
<input name="nomolest" type="checkbox"></input>
|
||||||
|
<p>No footer</p>
|
||||||
|
</div>
|
||||||
|
<button id="upload_button">Upload</button>
|
||||||
|
</form>
|
||||||
|
<dom-bind>
|
||||||
|
<template>
|
||||||
|
<vaadin-upload id="file_upload" files="{%raw%}{{files}}{%endraw%}" target="{{ url_for('meirl_upload') }}?noredirect" method="POST" form-data-name="meme" accept="image/*" max-file-size="5000000" nodrop="[[nodrop]]">
|
||||||
|
<style is="custom-style">
|
||||||
|
[nodrop] [part="upload-button"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
[nodrop] #addFiles {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div slot="drop-label-icon"></div>
|
||||||
|
<span slot="drop-label" class="font-headline">or drag a file here (5MB maximum)</span>
|
||||||
|
<div slot="file-list">
|
||||||
|
<template is="dom-repeat" items="[[files]]" as="file">
|
||||||
|
<vaadin-permalinked-upload-file file="[[file]]"></vaadin-permalinked-upload-file>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</vaadin-upload>
|
||||||
|
<div id="options">
|
||||||
|
<paper-toggle-button checked="{%raw%}{{add_footer}}{%endraw%}">Add footer</paper-toggle-button>
|
||||||
|
</div>
|
||||||
|
<template is="dom-if" if="[[!add_footer]]">
|
||||||
|
<p>
|
||||||
|
That's cool, we understand you don't want your meme molested, but if you could add something like the following as a comment that would be rad:
|
||||||
|
</p>
|
||||||
|
<div class="codeblock" on-click="onCommentClick">
|
||||||
|
<p>
|
||||||
|
This meme is protected from the Twitter bot using ✨ computer magic ✨
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
See [the original post](https://www.reddit.com/r/me_irl/comments/8r8nxj/meirl/e0pc5q8/) or just [flex on those Twitter users](https://memepolice.xyz/itmeirlbot-protect)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<template is="dom-if" if="[[files.length]]">
|
||||||
|
<p>
|
||||||
|
Also, it looks like you've already uploaded. Please note that you'll have to reupload any files for this option will take effect!
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</dom-bind>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
function isES6()
|
||||||
|
{
|
||||||
|
try { Function("() => {};"); return true; }
|
||||||
|
catch(exception) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('WebComponentsReady', function() {
|
||||||
|
// Remove noscript / dinosaur componenets if both webcomponents and es6 avalible
|
||||||
|
if (isES6()){
|
||||||
|
var noscripts = document.querySelectorAll('.noscript');
|
||||||
|
for (var i = noscripts.length-1; i >= 0; i--){
|
||||||
|
noscripts[i].parentNode.removeChild(noscripts[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Abort polymer loading to fix IE become ing responsive for a bit
|
||||||
|
}
|
||||||
|
|
||||||
|
var upload = document.querySelector('vaadin-upload#file_upload');
|
||||||
|
var binder = document.querySelector('dom-bind');
|
||||||
|
|
||||||
|
upload.addEventListener('upload-response', function(event) {
|
||||||
|
event.detail.file.url = event.detail.xhr.response;
|
||||||
|
});
|
||||||
|
|
||||||
|
upload.addEventListener('upload-request', function(event) {
|
||||||
|
event.detail.formData.append('nomolest', binder.add_footer ? 'off' : 'on');
|
||||||
|
});
|
||||||
|
|
||||||
|
binder.files = [];
|
||||||
|
binder.add_footer = true;
|
||||||
|
binder.onCommentClick = function(event) {
|
||||||
|
var sel, range;
|
||||||
|
sel = window.getSelection();
|
||||||
|
console.log(event.target);
|
||||||
|
if(sel.toString() == '') {
|
||||||
|
range = document.createRange();
|
||||||
|
range.selectNodeContents(event.target);
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -8,30 +8,16 @@
|
||||||
<meta name="description" content="meme steal no more">
|
<meta name="description" content="meme steal no more">
|
||||||
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono" rel="stylesheet" />
|
||||||
<link href="{{ url_for('static', filename='style.css') }}" type="text/css" rel="stylesheet" />
|
<link href="{{ url_for('static', filename='style.css') }}" type="text/css" rel="stylesheet" />
|
||||||
|
|
||||||
<script src="{{ url_for('components', path='webcomponentsjs/webcomponents-loader.js') }}"></script>
|
|
||||||
<link rel="import" href="{{ url_for('components', path='polymer/lib/elements/dom-bind.html') }}">
|
|
||||||
{% block imports %}
|
{% block imports %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
{% block body %}
|
||||||
<p>
|
{% endblock %}
|
||||||
This doesn't work without JS at the moment.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Ping me if you really would rather it would: <a href="https://t.me/skehmatics">telegram</a> and <a href="https://niu.moe/@skehmatics">mastodon</a>
|
|
||||||
</p>
|
|
||||||
</noscript>
|
|
||||||
<dom-bind>
|
|
||||||
<template>
|
|
||||||
{% block body %}
|
|
||||||
{% endblock %}
|
|
||||||
</template>
|
|
||||||
</dom-bind>
|
|
||||||
|
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue