refactor to /api/, add ratelimit, and add beeping
This commit is contained in:
parent
42b8858b46
commit
42085caf4c
8 changed files with 319 additions and 104 deletions
|
@ -30,6 +30,7 @@ dependencies {
|
|||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-auth:$ktorVersion")
|
||||
implementation("io.ktor:ktor-server-rate-limit:$ktorVersion")
|
||||
implementation("io.github.oshai:kotlin-logging-jvm:5.1.4")
|
||||
implementation("org.slf4j:slf4j-simple:2.0.16")
|
||||
|
||||
|
|
|
@ -12,14 +12,21 @@ import io.ktor.server.application.*
|
|||
import io.ktor.server.netty.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.plugins.ratelimit.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import observer.nelle.nelleObserverBackend.plugins.configureRouting
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
val logger = KotlinLogging.logger("nelle.observer API")
|
||||
|
||||
fun main(args: Array<String>): Unit = EngineMain.main(args)
|
||||
|
||||
fun Application.module() {
|
||||
install(RateLimit) {
|
||||
register {
|
||||
rateLimiter(limit = 10, refillPeriod = 2.minutes)
|
||||
}
|
||||
}
|
||||
install(ContentNegotiation)
|
||||
install(CORS) {
|
||||
allowMethod(HttpMethod.Options)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
@file:Suppress("ktlint:standard:no-wildcard-imports")
|
||||
package observer.nelle.nelleObserverBackend.helpers
|
||||
|
||||
package observer.nelle.nelleObserverBackend
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
// Other Shit
|
||||
// Get balloons
|
||||
val balloon = listOf(" \uD83D\uDCAC ", " \uD83D\uDCAD ")
|
||||
|
||||
// choose a random balloon out of list
|
||||
val randomBalloon = balloon.asSequence().shuffled().find { true }
|
||||
|
||||
// get randomized meow
|
||||
fun getMeow(): String {
|
||||
// source of characters to randomize
|
||||
val mainSrc = "meowrp"
|
||||
|
@ -23,13 +23,8 @@ fun getMeow(): String {
|
|||
.map(mainSrc::get)
|
||||
.joinToString("")
|
||||
|
||||
// Get balloons (only called if the neocat is called)
|
||||
val balloon = listOf(" \uD83D\uDCAC ", " \uD83D\uDCAD ")
|
||||
// choose a random balloon out of list
|
||||
val randomBalloon = balloon.asSequence().shuffled().find { true }
|
||||
|
||||
// get neocat
|
||||
val neoCat =
|
||||
val catEmotes =
|
||||
listOf(
|
||||
"",
|
||||
"",
|
||||
|
@ -122,7 +117,7 @@ fun getMeow(): String {
|
|||
":nkobounce_purple:",
|
||||
)
|
||||
// choose a random cat emote out of list
|
||||
val randomCat = neoCat.asSequence().shuffled().find { true }
|
||||
val randomCat = catEmotes.asSequence().shuffled().find { true }
|
||||
|
||||
// if the neocat returns empty, don't use a balloon, else return cat and random balloon
|
||||
return if (randomCat == "") {
|
||||
|
@ -132,32 +127,86 @@ fun getMeow(): String {
|
|||
}
|
||||
}
|
||||
|
||||
// // Outgoing API Calls
|
||||
// get randomized beep
|
||||
fun getBeep(): String {
|
||||
val bSrc = "b"
|
||||
val eSrc = "e"
|
||||
val pSrc = "p"
|
||||
|
||||
// Make a post with mastodon API
|
||||
// TODO: Replace this with a MastodonAPI Library (that i have not finished making)
|
||||
suspend fun makePost(
|
||||
client: HttpClient,
|
||||
postContent: String,
|
||||
instance: String,
|
||||
) {
|
||||
// make a post!
|
||||
val post: HttpResponse =
|
||||
client.submitForm(
|
||||
url =
|
||||
buildString {
|
||||
append("https://")
|
||||
append(instance)
|
||||
append("/api/v1/statuses")
|
||||
},
|
||||
formParameters =
|
||||
parameters {
|
||||
append("status", postContent)
|
||||
append("visibility", "unlisted")
|
||||
},
|
||||
val bLength = (1..8).random()
|
||||
val eLength = (1..16).random()
|
||||
val pLength = (1..8).random()
|
||||
|
||||
val bGen =
|
||||
java.util
|
||||
.Random()
|
||||
.ints(bLength.toLong(), 0, bSrc.length)
|
||||
.asSequence()
|
||||
.map(bSrc::get)
|
||||
.joinToString("")
|
||||
val eGen =
|
||||
java.util
|
||||
.Random()
|
||||
.ints(eLength.toLong(), 0, eSrc.length)
|
||||
.asSequence()
|
||||
.map(eSrc::get)
|
||||
.joinToString("")
|
||||
val pGen =
|
||||
java.util
|
||||
.Random()
|
||||
.ints(pLength.toLong(), 0, pSrc.length)
|
||||
.asSequence()
|
||||
.map(pSrc::get)
|
||||
.joinToString("")
|
||||
|
||||
val botEmotes =
|
||||
listOf(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
":neubot:",
|
||||
":neubot_pout:",
|
||||
":neubot_smol:",
|
||||
":neubot_frown:",
|
||||
":neubot_glare:",
|
||||
":neubot_greet:",
|
||||
":neubot_heart:",
|
||||
"neubot_laugh:",
|
||||
":neubot_pout2:",
|
||||
":neubot_smile:",
|
||||
":neubot_think:",
|
||||
":neubot_laugh2:",
|
||||
":neubot_exclaim:",
|
||||
":neubot_flushed:",
|
||||
":neubot_offline:",
|
||||
":neubot_sideeye:",
|
||||
":neubot_smoller:",
|
||||
":neubot_no_mouth:",
|
||||
":neubot_low_battery:",
|
||||
":neubot_upside_down:",
|
||||
":neubot_full_battery:",
|
||||
":neubot_half_battery:",
|
||||
":neubot_low_battery_charging:",
|
||||
":neubot_full_battery_charging:",
|
||||
":neubot_half_battery_charging:",
|
||||
)
|
||||
}
|
||||
|
||||
// Send a ntfy message
|
||||
suspend fun ntfyMsg() {
|
||||
val randomBot = botEmotes.asSequence().shuffled().find { true }
|
||||
|
||||
return if (randomBot == "") {
|
||||
"$bGen$eGen$pGen"
|
||||
} else {
|
||||
"$randomBot$randomBalloon$bGen$eGen$pGen"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
@file:Suppress("ktlint:standard:no-wildcard-imports")
|
||||
|
||||
package observer.nelle.nelleObserverBackend.plugins
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
|
||||
// Make a post with mastodon API
|
||||
// TODO: Replace this with a MastodonAPI Library (that i have not finished making)
|
||||
suspend fun makePost(
|
||||
client: HttpClient,
|
||||
postContent: String,
|
||||
instance: String,
|
||||
) {
|
||||
// make a post!
|
||||
val post: HttpResponse =
|
||||
client.submitForm(
|
||||
url =
|
||||
buildString {
|
||||
append("https://")
|
||||
append(instance)
|
||||
append("/api/v1/statuses")
|
||||
},
|
||||
formParameters =
|
||||
parameters {
|
||||
append("status", postContent)
|
||||
append("visibility", "unlisted")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Send a ntfy message
|
||||
suspend fun ntfyMsg() {
|
||||
}
|
|
@ -5,18 +5,19 @@ package observer.nelle.nelleObserverBackend.plugins
|
|||
import io.ktor.client.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.ratelimit.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import observer.nelle.nelleObserverBackend.Config
|
||||
import observer.nelle.nelleObserverBackend.getMeow
|
||||
import observer.nelle.nelleObserverBackend.logger
|
||||
import observer.nelle.nelleObserverBackend.makePost
|
||||
import observer.nelle.nelleObserverBackend.*
|
||||
import observer.nelle.nelleObserverBackend.helpers.getBeep
|
||||
import observer.nelle.nelleObserverBackend.helpers.getMeow
|
||||
import java.util.*
|
||||
import kotlin.concurrent.timerTask
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
var meowTimedOut = false
|
||||
var beepTimedOut = false
|
||||
var timeoutTime = 33.minutes
|
||||
|
||||
fun meowTimer() {
|
||||
|
@ -31,47 +32,130 @@ fun meowTimer() {
|
|||
)
|
||||
}
|
||||
|
||||
fun beepTimer() {
|
||||
beepTimedOut = true
|
||||
Timer("SettingUp", true).schedule(
|
||||
timerTask {
|
||||
beepTimedOut = false
|
||||
logger.debug { "timeout reset" }
|
||||
},
|
||||
// 33 minutes in milliseconds
|
||||
timeoutTime.inWholeMilliseconds,
|
||||
)
|
||||
}
|
||||
|
||||
fun Application.configureRouting(client: HttpClient) {
|
||||
routing {
|
||||
rateLimit {
|
||||
route("/api") {
|
||||
// meow
|
||||
route("/meow") {
|
||||
// get meow timeout
|
||||
get("/meowTimeout") {
|
||||
get {
|
||||
if (meowTimedOut) {
|
||||
call.response.status(HttpStatusCode(423, "Timed Out"))
|
||||
call.respondText("Timed Out")
|
||||
logger.debug { "timed out" }
|
||||
}
|
||||
if (!meowTimedOut) {
|
||||
} else {
|
||||
call.response.status(HttpStatusCode(100, "Not Timed Out"))
|
||||
call.respondText("Not Timed Out")
|
||||
logger.debug { "not timed out" }
|
||||
}
|
||||
}
|
||||
|
||||
// meow button
|
||||
@Suppress("ktlint:standard:comment-wrapping")
|
||||
post("/meow") {
|
||||
post {
|
||||
val meow = getMeow()
|
||||
if (call.receiveText() == Config().superSecret) {
|
||||
call.response.status(HttpStatusCode(201, "Meow Posted"))
|
||||
makePost(client, getMeow(), Config().instanceDomain)
|
||||
makePost(client, meow, Config().instanceDomain)
|
||||
call.respondText("meowed with bypass")
|
||||
logger.info { "meowed with bypass" }
|
||||
logger.info { "'$meow' with bypass" }
|
||||
} else {
|
||||
if (meowTimedOut) {
|
||||
call.response.status(HttpStatusCode(423, "Timed Out"))
|
||||
call.respondText("still Sleeping...")
|
||||
call.respondText("still sleeping...")
|
||||
logger.info { "failed meow" }
|
||||
} else {
|
||||
call.response.status(HttpStatusCode(201, "Meow Posted"))
|
||||
makePost(client, getMeow(), Config().instanceDomain)
|
||||
makePost(client, meow, Config().instanceDomain)
|
||||
meowTimer()
|
||||
call.respondText("meow sent!")
|
||||
logger.info { "meowed" }
|
||||
call.respondText("'$meow' sent!")
|
||||
logger.info { "meowed: '$meow'" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post("/ntfy") {
|
||||
// beep
|
||||
route("/beep") {
|
||||
val beep = getBeep()
|
||||
// get meow timeout
|
||||
get {
|
||||
if (beepTimedOut) {
|
||||
call.response.status(HttpStatusCode(423, "Timed Out"))
|
||||
call.respondText("Timed Out")
|
||||
logger.debug { "timed out" }
|
||||
} else {
|
||||
call.response.status(HttpStatusCode(100, "Not Timed Out"))
|
||||
call.respondText("Not Timed Out")
|
||||
logger.debug { "not timed out" }
|
||||
}
|
||||
}
|
||||
// meow button
|
||||
post {
|
||||
if (call.receiveText() == Config().superSecret) {
|
||||
call.response.status(HttpStatusCode(201, "Meow Posted"))
|
||||
makePost(client, beep, Config().instanceDomain)
|
||||
call.respondText("beeped with bypass")
|
||||
logger.info { "'$beep' with bypass" }
|
||||
} else {
|
||||
if (beepTimedOut) {
|
||||
call.response.status(HttpStatusCode(423, "Timed Out"))
|
||||
call.respondText("still sleeping...")
|
||||
logger.info { "failed beeped" }
|
||||
} else {
|
||||
call.response.status(HttpStatusCode(201, "Meow Posted"))
|
||||
makePost(client, beep, Config().instanceDomain)
|
||||
beepTimer()
|
||||
call.respondText("'$beep' sent!")
|
||||
logger.info { "beeped: '$beep'" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
route("/ntfy") {
|
||||
get {
|
||||
}
|
||||
post {
|
||||
val formParameters = call.receiveParameters()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deprecate
|
||||
route("/meow") {
|
||||
get {
|
||||
call.response.status(HttpStatusCode(301, "endpoint moved to /api/meow"))
|
||||
call.respondText("301: endpoint moved to /api/meow")
|
||||
}
|
||||
post {
|
||||
call.response.status(HttpStatusCode(301, "endpoint moved to /api/meow"))
|
||||
call.respondText("301: endpoint moved to /api/meow")
|
||||
}
|
||||
}
|
||||
// deprecate
|
||||
route("/meowTimeout") {
|
||||
get {
|
||||
call.response.status(HttpStatusCode(301, "endpoint moved to /api/meow"))
|
||||
call.respondText("301: endpoint moved to /api/meow")
|
||||
}
|
||||
post {
|
||||
call.response.status(HttpStatusCode(301, "endpoint moved to /api/meow"))
|
||||
call.respondText("301: endpoint moved to /api/meow")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,3 +25,24 @@ if (response.status === 100) {
|
|||
console.warn("NOT TIMED OUT")
|
||||
}
|
||||
}
|
||||
|
||||
function sendBeep(endpoint) {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open("POST", endpoint);
|
||||
request.send("superSecret=null");
|
||||
console.warn(request.response.text);
|
||||
}
|
||||
|
||||
async function getBeepTimeout(endpoint) {
|
||||
const response = await fetch(endpoint)
|
||||
if (response.status === 423) {
|
||||
beepButton.disabled=true;
|
||||
beepButton.innerHTML = "<span>sleeping...</span>";
|
||||
console.warn("TIMED OUT")
|
||||
}
|
||||
if (response.status === 100) {
|
||||
beepButton.disabled=false;
|
||||
beepButton.innerHTML = "<span>meow</span>";
|
||||
console.warn("NOT TIMED OUT")
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
const meowEndpoint = "https://nelle.observer/meow";
|
||||
const timeOutEndpoint = "https://nelle.observer/meowTimeout";
|
||||
const meowEndpoint = "https://nelle.observer/api/meow";
|
||||
const beepEndpoint = "https://nelle.observer/api/beep";
|
||||
|
||||
// loads all the functions to be loaded on load, pretty simple, it loads shit on load.
|
||||
function onLoad() {
|
||||
|
@ -8,7 +8,8 @@ function onLoad() {
|
|||
redirect();
|
||||
checkBoxes();
|
||||
getPlaceholder();
|
||||
getMeowTimeout(timeOutEndpoint);
|
||||
getMeowTimeout(meowEndpoint);
|
||||
getBeepTimeout(beepEndpoint);
|
||||
}
|
||||
|
||||
// if javascript is enabled, this script will load, enabling all site elements that use javascript, by default these are all hidden.
|
||||
|
@ -59,13 +60,18 @@ function redirect() {
|
|||
|
||||
// meow
|
||||
const meowButton = document.getElementById("meow-button");
|
||||
const justMeowed = document.getElementById("justMeowed");
|
||||
|
||||
let timeout = 1;
|
||||
const beepButton = document.getElementById("beep-button");
|
||||
|
||||
// on send button click
|
||||
async function meowClick() {
|
||||
meowButton.disabled=true;
|
||||
meowButton.innerHTML = "<span>sleeping for 30 minutes...</span>";
|
||||
meowButton.innerHTML = "<span>sleeping...</span>";
|
||||
sendMeow(meowEndpoint);
|
||||
}
|
||||
|
||||
// on send button click
|
||||
async function beepClick() {
|
||||
beepButton.disabled=true;
|
||||
beepButton.innerHTML = "<span>sleeping...</span>";
|
||||
sendMeow(beepEndpoint);
|
||||
}
|
|
@ -1,11 +1,22 @@
|
|||
<div class="funny-meow">
|
||||
<br>
|
||||
<small>press this button to make me meow on the fediverse, <br>you can POST to <span class="glitch">https://nelle.observer/meow</span>. there is a timeout of 33 minutes globally.</small>
|
||||
<small>press the buttons bellow to make me meow/beep on the fediverse,
|
||||
there is a global timeout of 33 minutes, and a separate timer for each button. if it says its sleeping, come back and try again later!
|
||||
<br>you can POST to <span class="glitch">https://nelle.observer/api/meow</span> or <span class="glitch">https://nelle.observer/api/beep</span>
|
||||
respectively to make me beep, and GET at the same endpoints to check the status of the timer.</small>
|
||||
<br>
|
||||
<button
|
||||
class="custom-btn btn-1"
|
||||
style="width: 45%; margin-top: 4%; margin-bottom: 4%;"
|
||||
style="width: 25%; margin-top: 4%; margin-bottom: 4%;"
|
||||
onclick="meowClick()"
|
||||
id="meow-button">
|
||||
<span>meow</span>
|
||||
</button>
|
||||
<button
|
||||
class="custom-btn btn-1"
|
||||
style="width: 25%; margin-top: 4%; margin-bottom: 4%;"
|
||||
onclick="beepClick()"
|
||||
id="beep-button">
|
||||
<span>beep</span>
|
||||
</button>
|
||||
</div>
|
Loading…
Reference in a new issue