chore: remove guestbook, gazebot (now unused)

This commit is contained in:
dusk 2025-02-07 07:59:39 +03:00
parent 629a4d8f0e
commit 84382e5b05
Signed by: dusk
SSH Key Fingerprint: SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw
18 changed files with 1 additions and 3610 deletions

View File

@ -3,8 +3,6 @@
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
inputs.systems.url = "github:nix-systems/x86_64-linux";
inputs.naked-shell.url = "github:yusdacra/mk-naked-shell";
inputs.sbt-derivation.url = "github:zaninime/sbt-derivation";
inputs.sbt-derivation.inputs.nixpkgs.follows = "nixpkgs";
outputs = inp:
inp.parts.lib.mkFlake {inputs = inp;} {
@ -17,7 +15,7 @@
system,
...
}: let
pkgs = inp.nixpkgs.legacyPackages.${system}.appendOverlays [inp.sbt-derivation.overlays.default];
pkgs = inp.nixpkgs.legacyPackages.${system};
packageJson = builtins.fromJSON (builtins.readFile ./package.json);
in {
devShells.default = config.mk-naked-shell.lib.mkNakedShell {
@ -26,7 +24,6 @@
nodejs-slim_latest bun
nodePackages.svelte-language-server
nodePackages.typescript-language-server
sbt
];
shellHook = ''
export PATH="$PATH:$PWD/node_modules/.bin"
@ -90,35 +87,6 @@
runHook postInstall
'';
};
packages.guestbook-jar = pkgs.mkSbtDerivation rec {
pname = "guestbook";
version = "0.0.1-jar";
depsSha256 = "sha256-hIGZTEZpqgBuo6VGgZ6dQd5kK0RLVNy5REVBBYA3Gak=";
src = ./guestbook;
buildPhase = ''
sbt assembly
mkdir -p ./target/graalvm-native-image
'';
installPhase = ''
mkdir -p $out
cp target/scala-3.4.2/${pname}-assembly-0.0.1-SNAPSHOT.jar $out/${pname}.jar
'';
nativeBuildInputs = [
pkgs.graalvm-ce
];
};
packages.guestbook = pkgs.stdenv.mkDerivation rec {
inherit (config.packages.guestbook-jar) pname nativeBuildInputs;
version = "0.0.1";
src = config.packages.guestbook-jar;
buildPhase = ''
native-image -H:+ReportExceptionStackTraces -H:+AddAllCharsets --allow-incomplete-classpath --no-fallback --initialize-at-run-time --enable-http --enable-all-security-services --verbose -jar "./${pname}.jar" ./${pname}
'';
installPhase = ''
mkdir -p $out/bin
cp ${pname} $out/bin/${pname}
'';
};
packages.default = config.packages.gazesys;
};
};

5
gazebot/.gitignore vendored
View File

@ -1,5 +0,0 @@
target
.env
Secrets.toml/target
.shuttle*
Secrets*.toml

3176
gazebot/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +0,0 @@
[package]
name = "gazebot"
version = "0.1.0"
edition = "2021"
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
dotenvy = "0.15.7"
serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
shuttle-serenity = "0.48.0"
shuttle-runtime = "0.48.0"
reqwest = { version = "0.12", features = ["json"] }
scc = "2"

View File

@ -1,133 +0,0 @@
use std::str::FromStr;
use reqwest::{header::AUTHORIZATION, Url};
use scc::HashMap as ConcurrentHashMap;
use serenity::{
all::ActivityData,
async_trait,
json::Value as JsonValue,
model::{channel::Message, prelude::*},
prelude::*,
};
use shuttle_runtime::SecretStore;
const ADMIN_USER_ID: u64 = 853064602904166430;
struct Handler {
http: reqwest::Client,
secrets: SecretStore,
notes: ConcurrentHashMap<MessageId, String>,
}
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
if msg.author.id != UserId::new(ADMIN_USER_ID) {
return;
}
if msg.content.starts_with("do ") {
return;
}
let mut note_content = msg.content.clone();
const BSKY_TAG: &str = ".nobsky";
let no_bsky_posse = note_content.contains(BSKY_TAG);
if no_bsky_posse {
note_content = note_content.replace(BSKY_TAG, "");
}
let mut note_data = serenity::json::JsonMap::new();
note_data.insert(
"content".to_string(),
JsonValue::String(note_content.trim().to_string()),
);
note_data.insert("bskyPosse".to_string(), JsonValue::Bool(!no_bsky_posse));
// add replyTo if we are replying to a previous message
if let Some(reply_msg) = msg.referenced_message.as_deref() {
if let Some(reply_note_id) = self
.notes
.read_async(&reply_msg.id, |_, v| v.to_owned())
.await
{
note_data.insert("replyTo".to_string(), JsonValue::String(reply_note_id));
}
}
// add embed uri
if let Some(uri) = note_content
.split(&[' ', '(', ')'][..])
.find_map(|s| Url::from_str(s).ok())
{
note_data.insert("embedUri".to_string(), JsonValue::String(uri.to_string()));
}
let resp = self
.http
.post("https://gaze.systems/log/create")
.header(AUTHORIZATION, self.secrets.get("DISCORD_TOKEN").unwrap())
.json(&note_data)
.send()
.await
.and_then(|resp| resp.error_for_status());
let resp = match resp {
Ok(r) => r,
Err(why) => {
tracing::error!("could not create note: {why}");
return;
}
};
let note_resp = resp.json::<serenity::json::JsonMap>().await.unwrap();
let created_note_id = note_resp["noteId"]
.as_str()
.expect("note id must be a string");
tracing::info!("succesfully created note with id {created_note_id}");
self.notes
.upsert_async(msg.id, created_note_id.to_string())
.await;
let mut reply_content =
format!("created log at https://gaze.systems/log?id={created_note_id}");
let errors = note_resp["errors"].as_array().expect("must be array");
if !errors.is_empty() {
reply_content.push_str("\n\nerrors:");
for error in errors {
reply_content.push_str("\n--> ");
reply_content.push_str(error.as_str().expect("must be str"));
}
}
let _ = msg.reply(ctx, reply_content).await;
}
async fn ready(&self, ctx: Context, ready: Ready) {
tracing::info!("{} is connected!", ready.user.name);
ctx.set_presence(
Some(ActivityData::listening("messages")),
OnlineStatus::Online,
);
}
}
#[shuttle_runtime::main]
async fn serenity(
#[shuttle_runtime::Secrets] secrets: SecretStore,
) -> shuttle_serenity::ShuttleSerenity {
let token = secrets.get("DISCORD_TOKEN").unwrap();
let intents = GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
let client = Client::builder(&token, intents)
.event_handler(Handler {
http: reqwest::Client::new(),
secrets,
notes: ConcurrentHashMap::new(),
})
.await
.expect("Err creating client");
Ok(client.into())
}

View File

@ -1 +0,0 @@
{"name":"sbt","version":"1.10.1","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["C:\\Users\\dusk\\scoop\\apps\\graalvm-oracle-jdk\\current/bin/java","-Xms100m","-Xmx100m","-classpath","C:\\Users\\dusk\\AppData\\Local\\Coursier\\cache\\arc\\https\\github.com\\sbt\\sbt\\releases\\download\\v1.10.1\\sbt-1.10.1.zip\\sbt\\bin\\sbt-launch.jar","-Dsbt.script=C:\\Users\\dusk\\AppData\\Local\\Coursier\\data\\bin\\sbt.bat","xsbt.boot.Boot","-bsp"]}

View File

@ -1,2 +0,0 @@
version = "3.7.15"
runner.dialect = scala3

View File

@ -1,30 +0,0 @@
val Http4sVersion = "0.23.27"
val CirceVersion = "0.14.9"
val MunitVersion = "1.0.0"
val LogbackVersion = "1.5.6"
val MunitCatsEffectVersion = "2.0.0"
lazy val root = (project in file("."))
.enablePlugins(GraalVMNativeImagePlugin)
.settings(
organization := "systems.gaze",
name := "guestbook",
version := "0.0.1-SNAPSHOT",
scalaVersion := "3.4.2",
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-ember-server" % Http4sVersion,
"org.http4s" %% "http4s-circe" % Http4sVersion,
"org.http4s" %% "http4s-dsl" % Http4sVersion,
"org.scalameta" %% "munit" % MunitVersion % Test,
"org.typelevel" %% "munit-cats-effect" % MunitCatsEffectVersion % Test,
"ch.qos.logback" % "logback-classic" % LogbackVersion % Runtime,
"com.lihaoyi" %% "os-lib" % "0.9.1",
"io.circe" %% "circe-core" % CirceVersion,
"io.circe" %% "circe-generic" % CirceVersion,
"io.circe" %% "circe-parser" % CirceVersion,
),
assembly / assemblyMergeStrategy := {
case "module-info.class" => MergeStrategy.discard
case x => (assembly / assemblyMergeStrategy).value.apply(x)
}
)

View File

@ -1 +0,0 @@
sbt.version=1.10.1

View File

@ -1,8 +0,0 @@
// format: off
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0")
// format: on

View File

@ -1,4 +0,0 @@
addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.1")
addSbtPlugin("io.spray" % "sbt-revolver" % "0.10.0")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4")

View File

@ -1,8 +0,0 @@
// format: off
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0")
// format: on

View File

@ -1,8 +0,0 @@
// format: off
// DO NOT EDIT! This file is auto-generated.
// This file enables sbt-bloop to create bloop config files.
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0")
// format: on

View File

@ -1,16 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- On Windows machines setting withJansi to true enables ANSI
color code interpretation by the Jansi library. This requires
org.fusesource.jansi:jansi:1.8 on the class path. Note that
Unix-based operating systems such as Linux and Mac OS X
support ANSI color codes by default. -->
<withJansi>true</withJansi>
<encoder>
<pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -1,57 +0,0 @@
package systems.gaze.guestbook
import cats.Applicative
import cats.syntax.all.*
import io.circe.Decoder
import io.circe.generic.auto._, io.circe.syntax._, io.circe._, io.circe.parser._
import os.Path
trait Guestbook[F[_]]:
def read(config: Guestbook.Config, from: Int, count: Int): F[Guestbook.Page]
def write(config: Guestbook.Config, entry: Guestbook.Entry): F[Unit]
object Guestbook:
private val logger = org.log4s.getLogger
final case class Config(
val entriesPath: Path,
val entryCountPath: Path,
)
final case class Entry(
val author: String,
val content: String,
val timestamp: Long
)
final case class Page(
val entries: List[Tuple2[Int, Entry]],
val hasNext: Boolean,
)
def impl[F[_]: Applicative]: Guestbook[F] = new Guestbook[F]:
def entriesSize(config: Config): Int =
os.read(config.entryCountPath).toInt
def read(config: Config, from: Int, count: Int): F[Page] =
val entryCount = entriesSize(config)
val entryIds = (1 to entryCount).reverse.drop(from).take(count)
// actually get the entries
val entries = entryIds
.map((no) => // read the entries
logger.info(s"reading entry at $no")
no -> decode[Entry](os.read(config.entriesPath / no.toString)).getOrElse(
Entry(
author = "error",
content = "woops, this is an error!",
timestamp = 0
)
)
)
.toList
Page(entries, hasNext = entries.last._1 > 1).pure[F]
def write(config: Config, entry: Entry): F[Unit] =
val entryNo = entriesSize(config) + 1
val entryPath = config.entriesPath / entryNo.toString
// write entry
os.write.over(entryPath, entry.asJson.toString)
// update entry count
os.write.over(config.entryCountPath, entryNo.toString)
().pure[F]

View File

@ -1,58 +0,0 @@
package systems.gaze.guestbook
import io.circe.generic.auto._
import org.http4s.HttpRoutes
import org.http4s.dsl.Http4sDsl
import org.http4s.circe.CirceEntityEncoder.circeEntityEncoder
import org.http4s.UrlForm
import org.http4s.headers.Location
import cats.effect.IO
import cats.implicits.*
import org.http4s.server.middleware.Throttle
import scala.concurrent.duration.DurationInt
import cats.effect.unsafe.implicits.global
import scala.concurrent.duration.FiniteDuration
import org.http4s.Uri
class GuestbookRoutes(
var guestbookConfig: Guestbook.Config,
var websiteUri: Uri
):
val dsl = new Http4sDsl[IO] {}
import dsl.*
def throttle(
amount: Int,
per: FiniteDuration
)(routes: HttpRoutes[IO]): HttpRoutes[IO] =
Throttle
.httpRoutes[IO](amount, per)(routes)
.unsafeRunSync()
def routes(
G: Guestbook[IO]
): HttpRoutes[IO] =
val putEntry = HttpRoutes.of[IO] {
case req @ POST -> Root =>
for {
entry <- req.as[UrlForm].map { form =>
val author = form.getFirstOrElse("author", "error")
val content = form.getFirstOrElse("content", "error")
Guestbook
.Entry(author, content, timestamp = System.currentTimeMillis / 1000)
}
result <- G.write(guestbookConfig, entry)
resp <- SeeOther(Location(websiteUri / "guestbook"))
} yield resp
}
object OffsetParam extends QueryParamDecoderMatcher[Int]("offset")
object CountParam extends QueryParamDecoderMatcher[Int]("count")
val getEntries = HttpRoutes.of[IO] {
case GET -> Root :? OffsetParam(offset) +& CountParam(count) =>
for {
entries <- G.read(guestbookConfig, offset, count)
resp <- Ok(entries)
} yield resp
}
throttle(30, 2.seconds)(getEntries)
<+> throttle(5, 30.seconds)(putEntry)

View File

@ -1,35 +0,0 @@
package systems.gaze.guestbook
import com.comcast.ip4s.*
import fs2.io.net.Network
import org.http4s.ember.server.EmberServerBuilder
import org.http4s.implicits.*
import org.http4s.server.middleware.Logger
import cats.effect.IO
import org.http4s.Uri
object GuestbookServer:
def run(
guestbookConfig: Guestbook.Config,
websiteUri: Uri,
): IO[Nothing] = {
val guestbookAlg = Guestbook.impl[IO]
// Combine Service Routes into an HttpApp.
// Can also be done via a Router if you
// want to extract segments not checked
// in the underlying routes.
val httpApp =
(GuestbookRoutes(guestbookConfig, websiteUri).routes(guestbookAlg)).orNotFound
// With Middlewares in place
val finalHttpApp = Logger.httpApp(true, true)(httpApp)
EmberServerBuilder
.default[IO]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(finalHttpApp)
.build
}.useForever

View File

@ -1,20 +0,0 @@
package systems.gaze.guestbook
import cats.effect.IOApp
import org.http4s.Uri
object Main extends IOApp.Simple:
val websiteUri =
Uri
.fromString(sys.env("GUESTBOOK_WEBSITE_URI"))
.getOrElse(throw new Exception("write better uris lol"))
val guestbookConfig = Guestbook.Config(
entriesPath = os.pwd / "entries",
entryCountPath = os.pwd / "entries_size"
)
// make the entries path if it doesnt exist
os.makeDir.all(guestbookConfig.entriesPath)
// make the entry count path if it doesnt exist
if !os.exists(guestbookConfig.entryCountPath) then
os.write(guestbookConfig.entryCountPath, 0.toString)
val run = GuestbookServer.run(guestbookConfig, websiteUri)