This commit is contained in:
aggie 2025-12-11 09:30:21 -05:00
commit bf9a37e952
14 changed files with 653 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
[*.{py,html,css,js}]
indent_style = space
indent_size = 4

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
database.db
.env
__pycache__/
static/draws/*
/venv
/venv/
/venv/*

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"trailingComma": "es5",
"semi": false
}

135
app.py Normal file
View file

@ -0,0 +1,135 @@
import os
import subprocess
import sqlite3
import requests
from dotenv import load_dotenv
from flask import Flask, redirect, request, jsonify, abort, render_template
from flask_talisman import Talisman
DATABASE = "database.db"
ALLOWED_EXTENSIONS = {"png", "webp"}
env = load_dotenv()
SECRET_TOKEN = os.getenv("SECRET_TOKEN")
ENV = os.getenv("ENV")
app = Flask(
__name__, static_url_path="/", static_folder="static", template_folder="static"
)
app.config["UPLOAD_FOLDER"] = "static/draws/"
app.config["MAX_CONTENT_LENGTH"] = 16 * 1000 * 1000
def init_db():
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute(
"""CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL
)"""
)
cursor.execute(
"""CREATE TABLE IF NOT EXISTS draws (
id INTEGER PRIMARY KEY AUTOINCREMENT
)"""
)
conn.commit()
conn.close()
@app.route("/")
def index():
return app.send_static_file("index.html")
@app.route("/praise", methods=["POST"])
def create_post():
if not request.json:
return jsonify({"error": "The request is not JSON"}), 400
content = request.json.get("content")
if not content:
return jsonify({"error": "Content is required"}), 400
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("INSERT INTO posts (content) VALUES (?)", (content,))
conn.commit()
post_id = cursor.lastrowid
conn.close()
return jsonify({"id": post_id, "content": content}), 201
@app.route("/praise-with-presets", methods=["POST"])
def create_post_but_for_losers():
a = request.form.get("a")
b = request.form.get("b")
c = request.form.get("c")
d = request.form.get("action")
# WONTFIX cause funny
content = f"{a} {b} {c} {d}".strip()
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("INSERT INTO posts (content) VALUES (?)", (content,))
conn.commit()
conn.close()
return redirect("/thank-you.html")
@app.route("/praises", methods=["GET"])
def get_posts():
auth_token = request.headers.get("Authorization")
if auth_token != SECRET_TOKEN:
abort(403)
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("SELECT * FROM posts")
posts = [{"id": row[0], "content": row[1]} for row in cursor.fetchall()]
conn.close()
return jsonify(posts), 200
@app.route("/draw", methods=["GET", "POST"])
def create_draw():
if request.method == "GET":
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("SELECT * FROM draws")
images = cursor.fetchall()
conn.close()
return render_template("draw.html", images=images)
if "image" not in request.files:
return jsonify({"error": "Content is requried"}), 400
image = request.files["image"]
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("INSERT INTO draws DEFAULT VALUES")
id = cursor.lastrowid
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
path = os.path.join(app.config["UPLOAD_FOLDER"], f"{id}.png")
image.save(path)
conn.commit()
conn.close()
return redirect(request.url)
if __name__ == "__main__":
init_db()
if ENV == "production":
Talisman(app, force_https=True)
subprocess.run(["gunicorn", "-w", "4", "-b", "0.0.0.0:3697", "app:app"])
else:
app.run(host="0.0.0.0", port=3697)

1
last.id Normal file
View file

@ -0,0 +1 @@
7

4
requirements.txt Normal file
View file

@ -0,0 +1,4 @@
Flask
gunicorn>=20.0.4
flask_talisman
python-dotenv

42
send_to_discord.py Normal file
View file

@ -0,0 +1,42 @@
import sqlite3
import time
import requests
import os
from dotenv import load_dotenv
load_dotenv()
WEBHOOK_URL = os.getenv("DISCORD_WEBHOOK_URL")
DATABASE = "database.db"
def send_to_discord(message):
if not WEBHOOK_URL:
print("No webhook set")
return
requests.post(WEBHOOK_URL, json={"content": message})
def main():
last_id = 0
if os.path.exists("last.id"):
with open("last.id", "r") as f:
last_id = int(f.read().strip())
while True:
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute("SELECT id, content FROM posts WHERE id > ?", (last_id,))
rows = cursor.fetchall()
conn.close()
for post_id, content in rows:
send_to_discord(f"New praise #{post_id}: {content}")
last_id = post_id
with open("last.id", "w") as f:
f.write(str(last_id))
time.sleep(5)
if __name__ == "__main__":
main()

4
start.sh Normal file
View file

@ -0,0 +1,4 @@
#!/bin/bash
pip3 install -r requirements.txt
python3 app.py
# gunicorn -w 4 -b 0.0.0.0:3000 app:app

84
static/draw.html Normal file
View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Draw something for meow-d</title>
<link rel="stylesheet" href="style.css">
<style>
#draw-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 1em;
width: fit-content;
}
#draw-canvas {
display: block;
border: 1px solid black;
max-width: 100%;
max-height: 400px;
aspect-ratio: 1;
}
.buttons {
width: 100%;
display: flex;
gap: 0.5em;
}
.buttons button {
padding: 0.25em 0.5em;
font-size: 1em;
cursor: pointer;
}
.draw-display {
display: grid;
gap: 1em;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
grid-template-rows: repeat(auto-fit, minmax(150px, 1fr));
}
.draw-display>* {
border: 1px solid rgba(1, 1, 1, 0.2);
aspect-ratio: 1/1;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<h1>draw something for me!!</h1>
<section id="draw-section">
<canvas id="draw-canvas" width="400" height="400"></canvas>
<div class="buttons">
<button id="clear-button" onclick="clearCanvas()">Clear</button>
<span class="spacer"></span>
<button id="save-button" onclick="saveImage()">Download</button>
<button id="submit-button" onclick="submitImage()">Submit!!</button>
</div>
<script src="draw.js"></script>
<form id="image-form" action="/draw" method="POST" enctype="multipart/form-data">
<input type="file" name="image" id="imageInput" style="display:none">
<button type="submit" style="display:none">:3 jumpscare!!!!!!!!!!!!!!! rarrrrrrrggggg (submit button)</button>
</form>
</section>
<section>
<h2>submssions</h2>
<div class="draw-display">
{% for image in images %}
<img src="/draws/{{image[0]}}.png" alt="">
{% endfor %}
</div>
</section>
</body>
</html>

83
static/draw.js Normal file
View file

@ -0,0 +1,83 @@
const canvas = document.getElementById("draw-canvas")
const ctx = canvas.getContext("2d")
const width = canvas.clientWidth
const height = canvas.clientHeight
canvas.width = Math.floor(width * devicePixelRatio)
canvas.height = Math.floor(height * devicePixelRatio)
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0)
ctx.lineWidth = 2
ctx.lineCap = "round"
ctx.lineJoin = "round"
let prevX = null
let prevY = null
let isPainting = false
canvas.addEventListener("mousedown", (e) => {
isPainting = true
draw(e)
})
window.addEventListener("mouseup", () => (isPainting = false))
canvas.addEventListener("mousemove", draw)
canvas.addEventListener("mouseenter", (e) => {
prevX = e.offsetX
prevY = e.offsetY
})
canvas.addEventListener("touchstart", (e) => {
e.preventDefault()
isPainting = true
draw(e)
})
window.addEventListener("touchend", () => {
isPainting = false
})
canvas.addEventListener("touchmove", (e) => {
e.preventDefault()
draw(e)
})
function draw(e) {
if (prevX == null || prevY == null || !isPainting) {
prevX = e.offsetX
prevY = e.offsetY
return
}
let currentX = e.offsetX
let currentY = e.offsetY
ctx.beginPath()
ctx.moveTo(prevX, prevY)
ctx.lineTo(currentX, currentY)
ctx.stroke()
prevX = currentX
prevY = currentY
}
function clearCanvas() {
isPainting = false
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
function saveImage() {
const dataURL = canvas.toDataURL()
const link = document.createElement("a")
link.href = dataURL
link.download = "canvas.png"
link.click()
}
function submitImage() {
canvas.toBlob((image) => {
const input = document.getElementById("image-input")
const file = new File([image], "image.png", { type: "image/png" })
const dataTransfer = new DataTransfer()
dataTransfer.items.add(file)
input.files = dataTransfer.files
document.getElementById("image-form").submit()
})
}

229
static/index.html Normal file
View file

@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Please Praise Me!!!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>pwease... praise me......</h1>
<a href="https://github.com/meow-d/please-praise-meow-d/tree/7b9e7bcee749b78729c4ddd72fc5514f559acb71">i did not make this site originally; here's the code</a>
<section>
<p>or: <a href="/draw">draw something for me!</a></p>
</section>
<section>
<h2>send me stuff ;3</h2>
<p>all fields are optional</p>
<form action="/praise-with-presets" method="post">
<div class="dropdowns">
<select name="a" id="a">
<option value="">--prefix--</option>
<optgroup label="...a...">
<option value="You're such a">You're such a</option>
<option value="Nothing but a">Nothing but a</option>
<option value="Who's a">Who's a</option>
<option value="Oh look, a">Oh look, a</option>
<option value="Is that a">Is that a</option>
<option value="Imagine being such a">Imagine being such a</option>
</optgroup>
<optgroup label="...my...">
<option value="Who's my little">Who's my little</option>
<option value="Proud of my">Proud of my</option>
<option value="There goes my favorite">There goes my favorite</option>
<option value="You're my">You're my</option>
</optgroup>
<optgroup label="misc">
<option value="Oh, sweet">Oh, sweet</option>
<option value="You're so">You're so</option>
<option value="Just another">Just another</option>
</optgroup>
<optgroup label="genuine sounding ones">
<option value="No joke, I genuinely think you're a">No joke, I genuinely think you're a</option>
<option value="I wanna cuddle with you,">I wanna cuddle with you,</option>
</optgroup>
<optgroup label="???">
<option value="Miku dayo,">Miku dayo,</option>
<option value="It's joeover,">It's joeover,</option>
<option
value="'Cause you make my earth quake, Oh, you make my earth quake, Riding around, your love be shakin' me up, And it's making my heart break">
'Cause you make my earth quake, Oh, you make my earth quake, Riding around, your love be
shakin'
me up, And it's making my heart break</option>
<option
value="I can't take it anymore i'm actually madly in love with you i iknoww your home address i inow your ic i'm planning to eat your kidneys while yo're asleep i know every single part of your body better than you do i waanna just kidnap you and forcefem you and make you my sex slave in OUR sex dungeon i'm not kidding so enjoy your last days before i start moving you little">
I can't take it anymore i'm actually madly in love with you i iknoww your home address i
inow your ic i'm planning to eat your kidneys while yo're asleep i know every single part of
your body better than you do i waanna just kidnap you and forcefem you and make you my sex
slave in OUR sex dungeon i'm not kidding so enjoy your last days before i start moving you
little
</option>
</optgroup>
</select>
<select name="b" id="b">
<option value="">--adjective--</option>
<optgroup label="misc">
<option value="little">little</option>
<option value="purring">purring</option>
<option value="soft-hearted">soft-hearted</option>
<option value="clingy">clingy</option>
<option value="blushing">blushing</option>
<option value="adorable">adorable</option>
<option value="precious">precious</option>
<option value="dykeish">dykeish</option>
<option value="lovable">lovable</option>
<option value="wholesome">wholesome</option>
<option value="affectionate">affectionate</option>
<option value="tease">tease</option>
<option value="sweet">sweet</option>
<option value="playful">playful</option>
<option value="timid">timid</option>
<option value="bold">bold</option>
<option value="adorably shy">adorably shy</option>
<option value="submissive">submissive</option>
</optgroup>
<optgroup label="lewd">
<option value="naughty">naughty</option>
<option value="innocent">innocent</option>
<option value="dirty-minded">dirty-minded</option>
<option value="teasing">teasing</option>
<option value="seductive">talented</option>
<option value="gorgeous">gorgeous</option>
<option value="vivisectable">vivisectable</option>
</optgroup>
<optgroup label="fancy words">
<option value="whimsical">whimsical</option>
<option value="dapper">dapper</option>
<option value="chiseled">chiseled</option>
<option value="graceful">graceful</option>
<option value="radiant">radiant</option>
<option value="subservient">subservient</option>
<option value="pulchritudinous">pulchritudinous</option>
</optgroup>
<optgroup label="???">
<option value="cringe">cringe</option>
<!-- hiding to force you guys to be creative -->
<!-- <option value="dumbass">dumbass</option> -->
<!-- <option value="bitch ass">bitch ass</option> -->
</optgroup>
</select>
<select name="c" id="c">
<option value="">--noun--</option>
<optgroup label="degrading!!">
<option value="dumbass">dumbass</option>
<option value="slut">slut</option>
<option value="whore">whore</option>
<option value="toy">toy</option>
</optgroup>
<optgroup label="ownership?!">
<option value="sugar baby">sugar baby</option>
<option value="babydoll">babydoll</option>
</optgroup>
<optgroup label="endearing">
<option value="cutie">cutie</option>
<option value="cinnamon roll">cinnamon roll</option>
<option value="sweetheart">sweetheart</option>
<option value="sweetpea">sweetpea</option>
<option value="cutie pie">cutie pie</option>
<option value="angelic cutie">angelic cutie</option>
<option value="angel">angel</option>
<option value="princess">princess</option>
<option value="babygirl">babygirl</option>
<option value="good girl">good girl</option>
<optgroup label="fluffy">
<option value="fluffy">fluffy</option>
<option value="lil' bunny">lil' bunny</option>
<option value="puppy">puppy</option>
<option value="catgirl">catgirl</option>
<option value="kitten">kitten</option>
<option value="machine">machine</option>
<option value="kittyy">kittyy</option>
<option value="cat">cat</option>
</optgroup>
<optgroup label="respectful stuff... i guess...">
<option value="queen">queen</option>
<option value="goddess">goddess</option>
<option value="mistress">mistress</option>
</optgroup>
<optgroup label="???">
<option value="gamer girl">gamer girl</option>
<option value="motherfucker">motherfucker</option>
<option value="motherfucker &lt;3">motherfucker &lt;3</option>
</optgroup>
</select>
<select name="action" id="action">
<option value="">--action--</option>
<optgroup label="misc">
<option value="*winks playfully*">*winks playfully*</option>
<option value="*gives a mischievous smile*">*gives a mischievous smile*</option>
<option value="*stares intensely*">*stares intensely*</option>
<option value="*twirls hair*">*twirls hair*</option>
<option value="*whispers in ear*">*whispers in ear*</option>
</optgroup>
<optgroup label="touchy">
<option value="*gives a gentle pat*">*pets head*</option>
<option value="*pinches nose*">*pinches nose*</option>
<option value="*pinches cheek*">*pinches cheek*</option>
<option value="*vivisects you*">*vivisects you*</option>
<option value="*holds your hand*">*holds your hand*</option>
<option value="*pulls your teeth out*">*pulls your teeth out*</option>
</optgroup>
<optgroup label="physical">
<option value="*pulls you close*">*pulls you close*</option>
<option value="*scoops you up*">*scoops you up*</option>
</optgroup>
<optgroup label="hugs and cuddles">
<option value="*sits on lap*">*sits on lap*</option>
<option value="*hugs*">*hugs*</option>
<option value="*gives a soft squeeze*">*gives a soft squeeze*</option>
<option value="*hugs tightly*">*hugs tightly*</option>
<option value="*cuddles*">*cuddles*</option>
<option value="*nuzzles neck*">*nuzzles neck*</option>
<option value="*rubs back*">*rubs back*</option>
</optgroup>
<optgroup label="kiss">
<option value="*kisses forehead*">*kisses forehead*</option>
<option value="*kisses cheek*">*kisses cheek*</option>
<option value="*light kiss*">*light kiss on hand*</option>
</optgroup>
<optgroup label="violence">
<option value="*slaps ass*">*slaps ass*</option>
<option value="*chokes you*">*chokes you*</option>
<option value="*hits with metal pipe*">*hits with metal pipe*</option>
<option value="*busts out flamethrower*">*busts out flamethrower*</option>
</optgroup>
</select>
</div>
<button type="submit">submit praise!!</button>
</form>
</section>
</body>
</html>

0
static/praises.html Normal file
View file

40
static/style.css Normal file
View file

@ -0,0 +1,40 @@
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 1em;
}
code {
background-color: #f4f4f4;
border: 1px solid #ddd;
border-radius: 4px;
display: block;
margin: 1em 0;
padding: 1em;
overflow-x: auto;
white-space: pre-wrap;
}
h2 {
margin-top: 4em;
}
#a {
width: 200px;
}
.dropdowns {
display: flex;
flex-wrap: wrap;
gap: 0.2em;
margin-bottom: 1em;
}
.dropdowns select {
flex-grow: 1;
}
.spacer {
flex-grow: 1;
}

16
static/thank-you.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>thank you!!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>asljfasdfadf thanks &lt;3 aldjfldsjfa</h1>
<p><a href="/">praise me more..?</a></p>
</body>
</html>