├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── site
│ └── purrbot
│ └── api
│ ├── ImageAPI.java
│ ├── ImageUtil.java
│ ├── TextOWOifier.java
│ ├── mapper
│ └── GsonMapper.java
│ └── objects
│ ├── ErrorResponse.java
│ ├── ImgLinkListResponse.java
│ ├── ImgLinkResponse.java
│ ├── OWOifiedTextResponse.java
│ ├── OWOifyRequest.java
│ ├── RequestDetails.java
│ ├── RequestResponse.java
│ └── openapi
│ ├── APIParameter.java
│ ├── APIPath.java
│ └── APIPathsResponse.java
└── resources
├── info.json
└── logback.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Intellij+all ###
2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
4 |
5 | # User-specific stuff:
6 | .idea/**/workspace.xml
7 | .idea/**/tasks.xml
8 | .idea/dictionaries
9 |
10 | # Sensitive or high-churn files:
11 | .idea/**/dataSources/
12 | .idea/**/dataSources.ids
13 | .idea/**/dataSources.xml
14 | .idea/**/dataSources.local.xml
15 | .idea/**/sqlDataSources.xml
16 | .idea/**/dynamic.xml
17 | .idea/**/uiDesigner.xml
18 |
19 | # Gradle:
20 | .idea/**/gradle.xml
21 | .idea/**/libraries
22 |
23 | # CMake
24 | cmake-build-debug/
25 |
26 | # Mongo Explorer plugin:
27 | .idea/**/mongoSettings.xml
28 |
29 | ## File-based project format:
30 | *.iws
31 |
32 | ## Plugin-specific files:
33 |
34 | # IntelliJ
35 | /out/
36 |
37 | # mpeltonen/sbt-idea plugin
38 | .idea_modules/
39 |
40 | # JIRA plugin
41 | atlassian-ide-plugin.xml
42 |
43 | # Cursive Clojure plugin
44 | .idea/replstate.xml
45 |
46 | # Ruby plugin and RubyMine
47 | /.rakeTasks
48 |
49 | # Crashlytics plugin (for Android Studio and IntelliJ)
50 | com_crashlytics_export_strings.xml
51 | crashlytics.properties
52 | crashlytics-build.properties
53 | fabric.properties
54 |
55 | ### Intellij+all Patch ###
56 | # Ignores the whole idea folder
57 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
58 |
59 | .idea/
60 |
61 | ### Maven ###
62 | target/
63 | pom.xml.tag
64 | pom.xml.releaseBackup
65 | pom.xml.versionsBackup
66 | pom.xml.next
67 | release.properties
68 | dependency-reduced-pom.xml
69 | buildNumber.properties
70 | .mvn/timing.properties
71 |
72 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
73 | !/.mvn/wrapper/maven-wrapper.jar
74 |
75 | .gradle/
76 | build/
77 | /img/icon/
78 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Andre_601 (support@purrbot.site)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [docs]: https://docs.purrbot.site/api
2 | [purr]: https://purrbot.site
3 |
4 | # PurrBot Image API
5 | This API was created to provide random images.
6 | The reason behind this and not to just use any existing api (e.g. nekos.life) was to have more controll over the shown images.
7 |
8 | With version 1.2.0 was this API now merged with the original PurrBotAPI to have one central API to use.
9 |
10 | You can see the API being used by the bot [\*Purr*][purr].
11 |
12 | ## Endpoints
13 | For a complete list of all API endpoints (URLs) available, refer to the [online docs][docs]
14 |
15 | ## Report images
16 | Please report any images that may be seen as illegal (i.e. against a company ToS) on [our Discord](https://purrbot.site/discord) or through mail at support@purrbot.site
17 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
23 | 4.0.0
24 |
25 | site.purrbot.api
26 | ImageAPI
27 | 2.3.0
28 |
29 | Purrbot API
30 | A public API made for the Discord Bot *Purr* that contains random image and text features.
31 |
32 |
33 | 8
34 | 8
35 | UTF-8
36 |
37 |
38 |
39 |
40 | io.javalin
41 | javalin
42 | 6.4.0
43 | compile
44 |
45 |
46 | ch.qos.logback
47 | logback-classic
48 | 1.5.16
49 | compile
50 |
51 |
52 | com.google.code.gson
53 | gson
54 | 2.12.1
55 | compile
56 |
57 |
58 | io.swagger.parser.v3
59 | swagger-parser
60 | 2.1.29
61 | compile
62 |
63 |
64 |
65 |
66 | ImageAPI
67 |
68 |
69 | src/main/resources
70 | true
71 |
72 |
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-shade-plugin
77 | 3.6.0
78 |
79 |
80 | package
81 |
82 | shade
83 |
84 |
85 |
86 |
87 | site.purrbot.api.ImageAPI
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/ImageAPI.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api;
20 |
21 | import com.google.gson.Gson;
22 | import com.google.gson.GsonBuilder;
23 | import com.google.gson.JsonObject;
24 | import com.google.gson.JsonSyntaxException;
25 | import io.javalin.Javalin;
26 | import io.javalin.http.Context;
27 | import io.swagger.v3.oas.models.OpenAPI;
28 | import io.swagger.v3.oas.models.Operation;
29 | import io.swagger.v3.oas.models.PathItem;
30 | import io.swagger.v3.oas.models.media.Content;
31 | import io.swagger.v3.oas.models.media.MediaType;
32 | import io.swagger.v3.oas.models.parameters.Parameter;
33 | import io.swagger.v3.parser.OpenAPIV3Parser;
34 | import io.swagger.v3.parser.core.models.SwaggerParseResult;
35 | import org.slf4j.Logger;
36 | import org.slf4j.LoggerFactory;
37 | import site.purrbot.api.mapper.GsonMapper;
38 | import site.purrbot.api.objects.ErrorResponse;
39 | import site.purrbot.api.objects.OWOifyRequest;
40 | import site.purrbot.api.objects.RequestDetails;
41 | import site.purrbot.api.objects.openapi.APIPath;
42 | import site.purrbot.api.objects.openapi.APIPathsResponse;
43 | import site.purrbot.api.objects.openapi.APIParameter;
44 |
45 | import java.io.*;
46 | import java.util.*;
47 |
48 | public class ImageAPI{
49 |
50 | private final Logger logger = LoggerFactory.getLogger(ImageAPI.class);
51 | private final File base = new File("img/");
52 | private final Gson gson = new GsonBuilder()
53 | .setPrettyPrinting()
54 | .create();
55 |
56 | private JsonObject infoJson = null;
57 | private OpenAPI openAPI = null;
58 |
59 | private TextOWOifier owoifier;
60 |
61 | public static void main(String[] args){
62 | new ImageAPI().start();
63 | }
64 |
65 | private void start(){
66 | logger.info("Starting ImageAPI...");
67 | if(!base.exists()){
68 | logger.info("Couldn't find base folder. Generating it...");
69 | if(base.mkdirs()){
70 | logger.info("Successfully created base folder!");
71 | }else{
72 | logger.warn("Couldn't create base folder!");
73 | }
74 | }
75 |
76 | ImageUtil util = new ImageUtil(this);
77 | this.owoifier = new TextOWOifier();
78 |
79 | // Setup Javalin and make it handle all Exceptions
80 | Javalin app = Javalin.create(config -> {
81 | config.jsonMapper(new GsonMapper(gson));
82 | config.requestLogger.http((ctx, ms) -> logger.info("Processed request {} in {}ms", ctx.path(), ms));
83 | }).start(2000);
84 | app.exception(Exception.class, (ex, ctx) -> {
85 | logger.error("Exception caught", ex);
86 |
87 | sendErrorJSON(500, "Encountered an Exception while handling request. Exception: " + ex.getMessage(), ctx, System.currentTimeMillis());
88 | });
89 |
90 | app.get("/v2/", ctx -> {
91 | fetchOpenAPIJson(ctx, System.currentTimeMillis());
92 | });
93 |
94 | // New v2 API endpoints.
95 | app.get("/v2/list/", ctx -> {
96 | long time = System.currentTimeMillis();
97 |
98 | String path = ctx.pathParam("path");
99 | util.listContent(path, ctx, time, false);
100 | });
101 | app.get("/v2/img/", ctx -> {
102 | long time = System.currentTimeMillis();
103 |
104 | String path = ctx.pathParam("path");
105 | util.getFile(path, ctx, time, false);
106 | });
107 |
108 | app.post("/v2/owoify", ctx -> processOWOifyJSON(ctx, false))
109 | .get("/v2/owoify", ctx -> {
110 | long time = System.currentTimeMillis();
111 |
112 | String text = ctx.queryParam("text");
113 | boolean stutter = Boolean.parseBoolean(ctx.queryParam("stutter"));
114 | boolean emoticons = Boolean.parseBoolean(ctx.queryParam("emoticons"));
115 | boolean wordSubstitution = Boolean.parseBoolean(ctx.queryParam("replace-words"));
116 |
117 | if(text == null || text.isEmpty()){
118 | sendErrorJSON(400, "Received request does not contain a 'text' query parameter, or it was empty.", ctx, time);
119 | return;
120 | }
121 |
122 | owoifier.owoify(new OWOifyRequest(text, stutter, emoticons, wordSubstitution), ctx, time, false);
123 | });
124 |
125 | app.get("/", ctx -> {
126 | long time = System.currentTimeMillis();
127 | fetchInfoJson(ctx, time);
128 | });
129 |
130 | // Old /api/list/img/* Endpoints
131 | app.get("/api/list/", ctx -> {
132 | logger.info("Handle GET request for {}", ctx.path());
133 | long time = System.currentTimeMillis();
134 |
135 | String path = ctx.pathParam("path");
136 | util.listContent(path, ctx, time, true);
137 | });
138 |
139 | // Old /api/img/* Endpoints
140 | app.get("/api/img/", ctx -> {
141 | logger.info("Handle GET request for {}", ctx.path());
142 | long time = System.currentTimeMillis();
143 |
144 | String path = ctx.pathParam("path");
145 | util.getFile(path, ctx, time, true);
146 | });
147 |
148 | // Old /api/owoify Endpoints
149 | app.post("/api/owoify", ctx -> processOWOifyJSON(ctx, true)).get("/api/owoify", ctx -> {
150 | logger.info("Handle GET request for {}", ctx.path());
151 | long time = System.currentTimeMillis();
152 |
153 | String text = ctx.queryParam("text");
154 | boolean stutter = Boolean.getBoolean(ctx.queryParam("stutter"));
155 | boolean emoticons = Boolean.getBoolean(ctx.queryParam("emoticons"));
156 | boolean wordSubstitution = Boolean.getBoolean(ctx.queryParam("replace-words"));
157 |
158 | if(text == null || text.isEmpty()){
159 | sendErrorJSON(400, "Received request does not contain a 'text' query parameter, or it was empty.", ctx, time);
160 | return;
161 | }
162 |
163 | owoifier.owoify(new OWOifyRequest(text, stutter, emoticons, wordSubstitution), ctx, time, true);
164 | });
165 |
166 | // Old /api/info Endpoint
167 | app.get("/api/info", ctx -> {
168 | logger.info("Handle GET request for {}", ctx.path());
169 | long time = System.currentTimeMillis();
170 |
171 | fetchInfoJson(ctx, time);
172 | });
173 |
174 | // Handle unsupported requests.
175 | app.post("/api/quote", ctx -> {
176 | logger.info("Unsupported POST request on /api/quote");
177 | sendErrorJSON(410, "/api/quote has been removed from the API.", ctx, System.currentTimeMillis());
178 | }).post("/api/status", ctx -> {
179 | logger.info("Unsupported POST request on /api/status");
180 | sendErrorJSON(410, "/api/status has been removed from the API.", ctx, System.currentTimeMillis());
181 | }).post("/api/img/*", ctx -> {
182 | logger.info("Not allowed POST request towards {}", ctx.path());
183 | ctx.header("Allow", "GET");
184 | sendErrorJSON(405, "POST requests towards " + ctx.path() + " are not allowed.", ctx, System.currentTimeMillis());
185 | }).post("/api/list/*", ctx -> {
186 | logger.info("Not allowed POST request towards {}", ctx.path());
187 | ctx.header("Allow", "GET");
188 | sendErrorJSON(405, "POST requests towards " + ctx.path() + " are not allowed.", ctx, System.currentTimeMillis());
189 | });
190 | }
191 |
192 | void sendErrorJSON(int code, String msg, Context ctx, long time){
193 | ErrorResponse response = new ErrorResponse(getDetails(ctx), msg, code, time);
194 |
195 | ctx.json(response);
196 | ctx.status(code);
197 | }
198 |
199 | private RequestDetails getDetails(Context ctx){
200 | return new RequestDetails(
201 | ctx.path(),
202 | ctx.contentType() == null ? "NONE" : ctx.contentType(),
203 | ctx.userAgent() == null ? "NONE" : ctx.userAgent()
204 | );
205 | }
206 |
207 | private void fetchInfoJson(Context ctx, long time){
208 | if(infoJson != null){
209 | ctx.json(infoJson);
210 | ctx.status(200);
211 | return;
212 | }
213 |
214 | try(InputStream stream = getClass().getResourceAsStream("/info.json")){
215 | if(stream == null){
216 | sendErrorJSON(500, "Cannot retrieve API information. Reason: Received input stream was null.", ctx, time);
217 | return;
218 | }
219 |
220 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
221 |
222 | infoJson = gson.fromJson(reader, JsonObject.class);
223 | if(infoJson == null){
224 | sendErrorJSON(500, "Cannot retrieve API information. Reason: Retrieved JSON was null.", ctx, time);
225 | reader.close();
226 | return;
227 | }
228 |
229 | ctx.json(infoJson);
230 | ctx.status(200);
231 |
232 | reader.close();
233 | }catch(IOException ex){
234 | sendErrorJSON(500, "Encountered an IOException: " + ex.getMessage(), ctx, time);
235 | }
236 | }
237 |
238 | private void fetchOpenAPIJson(Context ctx, long time){
239 | if(openAPI != null){
240 | displayAPIPaths(ctx, time);
241 | return;
242 | }
243 |
244 | SwaggerParseResult result = new OpenAPIV3Parser().readLocation("https://raw.githubusercontent.com/purrbot-site/Docs/master/docs/assets/imageapi.json", null, null);
245 |
246 | if(result.getMessages() != null && !result.getMessages().isEmpty()){
247 | sendErrorJSON(
248 | 500,
249 | String.format(
250 | "Encountered an error while parsing OpenAPI JSON: %s",
251 | String.join(";", result.getMessages())
252 | ),
253 | ctx,
254 | time
255 | );
256 | return;
257 | }
258 |
259 | OpenAPI openAPI = result.getOpenAPI();
260 | if(openAPI == null){
261 | sendErrorJSON(
262 | 500,
263 | "Received OpenAPI instance was null.",
264 | ctx,
265 | time
266 | );
267 | return;
268 | }
269 |
270 | this.openAPI = openAPI;
271 |
272 | displayAPIPaths(ctx, time);
273 | }
274 |
275 | private void processOWOifyJSON(Context ctx, boolean deprecated){
276 | long time = System.currentTimeMillis();
277 |
278 | try{
279 | OWOifyRequest request = gson.fromJson(ctx.body(), OWOifyRequest.class);
280 | if(request == null){
281 | sendErrorJSON(400, "The received JSON was invalid or didn't exist.", ctx, time);
282 | return;
283 | }
284 |
285 | if(request.getText() == null || request.getText().isEmpty()){
286 | sendErrorJSON(400, "The received JSON does not contain a 'text' field or it was empty.", ctx, time);
287 | return;
288 | }
289 |
290 | if(owoifier == null){
291 | sendErrorJSON(500, "The TextOWOIfier is not available. If this issue persists, report it to the developer!", ctx, time);
292 | return;
293 | }
294 |
295 | owoifier.owoify(request, ctx, time, deprecated);
296 | }catch(JsonSyntaxException ex){
297 | sendErrorJSON(400, "Received invalid JSON Body: " + ex.getMessage(), ctx, time);
298 | }
299 | }
300 |
301 | private void displayAPIPaths(Context ctx, long time){
302 | List paths = new ArrayList<>();
303 | for(Map.Entry path : openAPI.getPaths().entrySet()){
304 | if(path.getValue().getGet() != null){
305 | paths.add(processAPIPath(path.getKey(), "GET", path.getValue().getGet()));
306 | }
307 | if(path.getValue().getPost() != null){
308 | paths.add(processAPIPath(path.getKey(), "POST", path.getValue().getPost()));
309 | }
310 | }
311 |
312 | APIPathsResponse response = new APIPathsResponse(time, paths);
313 |
314 | ctx.status(200);
315 | ctx.json(response);
316 | }
317 |
318 | private APIPath processAPIPath(String name, String method, Operation operation){
319 | String pathName = "https://api.purrbot.site/v2" + name;
320 | String description = operation.getDescription();
321 | Map requestBodies = null;
322 |
323 |
324 | if(operation.getRequestBody() != null && operation.getRequestBody().getContent() != null){
325 | Content content = operation.getRequestBody().getContent();
326 | requestBodies = new HashMap<>();
327 | for(Map.Entry schema : content.entrySet()){
328 | if(schema.getValue().getSchema() == null || schema.getValue().getSchema().get$ref() == null)
329 | continue;
330 |
331 | String ref = schema.getValue().getSchema().get$ref().toLowerCase(Locale.ROOT);
332 | if(ref.startsWith("#/components/schemas/")){
333 | requestBodies.put(
334 | schema.getKey(),
335 | "https://docs.purrbot.site/api/#" + ref.substring("#/components/schemas/".length()));
336 | }else{
337 | requestBodies.put(schema.getKey(), ref);
338 | }
339 | }
340 | }
341 |
342 | Boolean deprecated = operation.getDeprecated();
343 |
344 | List parameters = null;
345 | if(operation.getParameters() != null){
346 | parameters = new ArrayList<>();
347 | for(Parameter parameter : operation.getParameters())
348 | parameters.add(new APIParameter(parameter.getName(), parameter.getIn(), parameter.getDescription(), parameter.getRequired()));
349 | }
350 |
351 | return new APIPath(pathName, method, description, requestBodies, deprecated, parameters);
352 | }
353 | }
354 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/ImageUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api;
20 |
21 | import ch.qos.logback.classic.Logger;
22 | import io.javalin.http.Context;
23 | import org.slf4j.LoggerFactory;
24 | import site.purrbot.api.objects.ImgLinkListResponse;
25 | import site.purrbot.api.objects.ImgLinkResponse;
26 |
27 | import java.io.*;
28 | import java.util.*;
29 | import java.util.List;
30 | import java.util.stream.Collectors;
31 |
32 | public class ImageUtil{
33 |
34 | private final ImageAPI api;
35 |
36 | private final Random random = new Random();
37 | private final Logger logger = (Logger)LoggerFactory.getLogger(ImageUtil.class);
38 | private final List extensions = Arrays.asList(".png", ".jpg", ".jpeg", ".gif", ".svg");
39 | private final File base = new File("img/");
40 | private final FilenameFilter filter = (dir, name) -> {
41 | for(String ext : extensions){
42 | if(name.endsWith(ext))
43 | return true;
44 | }
45 |
46 | return false;
47 | };
48 |
49 | public ImageUtil(ImageAPI api){
50 | this.api = api;
51 | }
52 |
53 | void listContent(String path, Context ctx, long time, boolean deprecated){
54 | File[] files = getFiles(path, ctx, time);
55 | if(files.length == 0)
56 | return;
57 |
58 | List links = Arrays.stream(files).map(this::getPath).collect(Collectors.toList());
59 |
60 | ctx.status(200);
61 | if(deprecated){
62 | ctx.json(new ImgLinkListResponse(links, time, String.format(
63 | "This endpoint was deprecated and will be removed in the future. " +
64 | "Please forward any future requests towards https://api.purrbot.site/v2/list/%s",
65 | path
66 | )));
67 | }else{
68 | ctx.json(new ImgLinkListResponse(links, time));
69 | }
70 | }
71 |
72 | void getFile(String path, Context ctx, long time, boolean deprecated){
73 | File[] files = getFiles(path, ctx, time);
74 | if(files.length == 0)
75 | return;
76 |
77 | File selected = files[random.nextInt(files.length)];
78 |
79 | ctx.status(200);
80 | if(deprecated){
81 | ctx.json(new ImgLinkResponse(getPath(selected), time, String.format(
82 | "This endpoint was deprecated. Please forward future requests towards https://api.purrbot.site/v2/img/%s",
83 | path
84 | )));
85 | }else{
86 | ctx.json(new ImgLinkResponse(getPath(selected), time));
87 | }
88 | }
89 |
90 | private File[] getFiles(String path, Context ctx, long time){
91 | File folder = new File(base, path + "/");
92 |
93 | if(!folder.exists() || folder.isAbsolute()){
94 | logger.info("Received invalid path {} for Image GET request.", path);
95 |
96 | api.sendErrorJSON(403, "The provided path is not valid.", ctx, time);
97 | return new File[0];
98 | }else{
99 | File[] files = folder.listFiles(filter);
100 | if(files == null || files.length == 0){
101 | logger.info("Received path {} for Image GET request does not contain any images.", path);
102 |
103 | api.sendErrorJSON(403, "The provided path does not contain any images.", ctx, time);
104 | return new File[0];
105 | }
106 |
107 | return files;
108 | }
109 | }
110 |
111 | private String getPath(File file){
112 | return ("https://cdn.purrbot.site/" + file.getPath().substring("img/".length())).replace("\\", "/");
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/TextOWOifier.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api;
20 |
21 | import io.javalin.http.Context;
22 | import site.purrbot.api.objects.OWOifiedTextResponse;
23 | import site.purrbot.api.objects.OWOifyRequest;
24 |
25 | import java.util.*;
26 |
27 | public class TextOWOifier{
28 |
29 | private final List emotes = Arrays.asList("(・`ω´・)", ";;w;;", ">w<", "^w^", "UwU", "owo", "^^", "x3");
30 | private final Map wordMap = new HashMap(){{
31 | put("hello", "hewwo");
32 | put("hi", "hai");
33 | put("hey", "haiii");
34 | put("love", "wuv");
35 | put("friend", "fwend");
36 | put("stop", "stahp");
37 | put("no", "nu");
38 | put("you're", "ur");
39 | put("you", "uu");
40 | put("has", "haz");
41 | }};
42 |
43 | private final Random random = new Random();
44 |
45 | public void owoify(OWOifyRequest request, Context ctx, long time, boolean deprecated){
46 | String[] words = request.getText().split("\\s+");
47 | StringBuilder builder = new StringBuilder();
48 |
49 | for(String word : words){
50 | String key = word.toLowerCase(Locale.ROOT);
51 |
52 | if(request.isWordSubstitutions() && wordMap.containsKey(key)){
53 | word = matchCase(word, wordMap.get(key));
54 | }
55 |
56 | word = word.replaceAll("[rl]", "w").replaceAll("[RL]", "W");
57 |
58 | double d;
59 | synchronized(random){
60 | d = random.nextDouble();
61 | }
62 | if(request.isStutter() && word.length() > 2 && Character.isLetter(word.charAt(0)) && d < 0.2){
63 | word = word.charAt(0) + "-" + word;
64 | }
65 |
66 | builder.append(word).append(' ');
67 | }
68 |
69 | String owoified = builder.toString().trim();
70 |
71 | if(request.isEmoticons()){
72 | owoified = owoified.replaceAll("[.!?]", " " + getRandomEmoticon());
73 | }
74 |
75 | ctx.status(200);
76 | if(deprecated){
77 | ctx.json(new OWOifiedTextResponse(
78 | owoified,
79 | time,
80 | "This endpoint was deprecated and will be removed in the future. " +
81 | "Please forward any future requests towards https://api.purrbot.site/v2/owoify"
82 | ));
83 | }else{
84 | ctx.json(new OWOifiedTextResponse(owoified, time));
85 | }
86 | }
87 |
88 | private String matchCase(String original, String replacement){
89 | if(original.equals(original.toLowerCase(Locale.ROOT)))
90 | return replacement.toLowerCase(Locale.ROOT);
91 |
92 | if(original.equals(original.toUpperCase(Locale.ROOT)))
93 | return replacement.toUpperCase(Locale.ROOT);
94 |
95 | if(Character.isUpperCase(original.charAt(0)))
96 | return Character.toUpperCase(replacement.charAt(0)) + replacement.substring(1);
97 |
98 | return replacement;
99 | }
100 |
101 | private String getRandomEmoticon(){
102 | synchronized(random){
103 | return emotes.get(random.nextInt(emotes.size()));
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/mapper/GsonMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.mapper;
20 |
21 | import com.google.gson.Gson;
22 | import io.javalin.json.JsonMapper;
23 | import org.jetbrains.annotations.NotNull;
24 |
25 | import java.lang.reflect.Type;
26 |
27 | public class GsonMapper implements JsonMapper{
28 | private final Gson gson;
29 |
30 | public GsonMapper(Gson gson){
31 | this.gson = gson;
32 | }
33 |
34 | @NotNull
35 | @Override
36 | public String toJsonString(@NotNull Object obj, @NotNull Type type){
37 | return gson.toJson(obj, type);
38 | }
39 |
40 | @NotNull
41 | @Override
42 | public T fromJsonString(@NotNull String json, @NotNull Type targetType){
43 | return gson.fromJson(json, targetType);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/ErrorResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | public class ErrorResponse extends RequestResponse{
22 |
23 | private final RequestDetails details;
24 | private final String message;
25 |
26 | public ErrorResponse(RequestDetails details, String message, int responseCode, long time){
27 | super(true, responseCode, time);
28 |
29 | this.details = details;
30 | this.message = message;
31 | }
32 | public ErrorResponse(RequestDetails details, String message, int responseCode, long time, String deprecationWarning){
33 | super(true, responseCode, time, deprecationWarning);
34 |
35 | this.details = details;
36 | this.message = message;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/ImgLinkListResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | import java.util.List;
22 |
23 | public class ImgLinkListResponse extends RequestResponse{
24 |
25 | private final List links;
26 |
27 | public ImgLinkListResponse(List links, long time){
28 | super(false, 200, time);
29 |
30 | this.links = links;
31 | }
32 |
33 | public ImgLinkListResponse(List links, long time, String deprecationWarning){
34 | super(false, 200, time, deprecationWarning);
35 |
36 | this.links = links;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/ImgLinkResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | public class ImgLinkResponse extends RequestResponse{
22 |
23 | private final String link;
24 |
25 | public ImgLinkResponse(String link, long time){
26 | super(false, 200, time);
27 |
28 | this.link = link;
29 | }
30 |
31 | public ImgLinkResponse(String link, long time, String deprecationWarning){
32 | super(false, 200, time, deprecationWarning);
33 |
34 | this.link = link;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/OWOifiedTextResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | public class OWOifiedTextResponse extends RequestResponse{
22 |
23 | private final String text;
24 |
25 | public OWOifiedTextResponse(String text, long time){
26 | super(false, 200, time);
27 |
28 | this.text = text;
29 | }
30 |
31 | public OWOifiedTextResponse(String text, long time, String deprecationWarning){
32 | super(false, 200, time, deprecationWarning);
33 |
34 | this.text = text;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/OWOifyRequest.java:
--------------------------------------------------------------------------------
1 | package site.purrbot.api.objects;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | public class OWOifyRequest{
6 | private final String text;
7 | private final boolean stutter;
8 | private final boolean emoticons;
9 | @SerializedName("replace-words")
10 | private final boolean wordSubstitutions;
11 |
12 | public OWOifyRequest(String text, boolean stutter, boolean emoticons, boolean wordSubstitutions){
13 | this.text = text;
14 | this.stutter = stutter;
15 | this.emoticons = emoticons;
16 | this.wordSubstitutions = wordSubstitutions;
17 | }
18 |
19 | public String getText(){
20 | return text;
21 | }
22 |
23 | public boolean isStutter(){
24 | return stutter;
25 | }
26 |
27 | public boolean isEmoticons(){
28 | return emoticons;
29 | }
30 |
31 | public boolean isWordSubstitutions(){
32 | return wordSubstitutions;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/RequestDetails.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | import com.google.gson.annotations.SerializedName;
22 |
23 | public class RequestDetails{
24 |
25 | private final String path;
26 | @SerializedName("content-type")
27 | private final String contentType;
28 | @SerializedName("user-agent")
29 | private final String userAgent;
30 |
31 | public RequestDetails(String path, String contentType, String userAgent){
32 | this.path = path;
33 | this.contentType = contentType;
34 | this.userAgent = userAgent;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/RequestResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Andre601
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
7 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 | *
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial
10 | * portions of the Software.
11 | *
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 | */
18 |
19 | package site.purrbot.api.objects;
20 |
21 | import com.google.gson.annotations.SerializedName;
22 |
23 | public class RequestResponse{
24 |
25 | private final boolean error;
26 | @SerializedName("response-code")
27 | private final int responseCode;
28 | private final long time;
29 | @SerializedName("deprecation-warning")
30 | private final String deprecationWarning;
31 |
32 | public RequestResponse(boolean error, int responseCode, long time){
33 | this(error, responseCode, time, null);
34 | }
35 |
36 | public RequestResponse(boolean error, int responseCode, long time, String deprecationWarning){
37 | this.error = error;
38 | this.responseCode = responseCode;
39 | this.time = System.currentTimeMillis() - time;
40 | this.deprecationWarning = deprecationWarning;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/openapi/APIParameter.java:
--------------------------------------------------------------------------------
1 | package site.purrbot.api.objects.openapi;
2 |
3 | public class APIParameter{
4 | private final String name;
5 | private final String location;
6 | private final String description;
7 | private final Boolean required;
8 |
9 | public APIParameter(String name, String location, String description, Boolean required){
10 | this.name = name;
11 | this.location = location;
12 | this.description = description;
13 | this.required = required;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/openapi/APIPath.java:
--------------------------------------------------------------------------------
1 | package site.purrbot.api.objects.openapi;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | public class APIPath{
9 |
10 | private final String path;
11 | private final String method;
12 | private final String description;
13 | @SerializedName("request-bodies")
14 | private final Map requestBodies;
15 | private final Boolean deprecated;
16 | private final List parameters;
17 |
18 | public APIPath(String path, String method, String description, Map requestBodies, Boolean deprecated, List parameters){
19 | this.path = path;
20 | this.method = method;
21 | this.description = description;
22 | this.requestBodies = requestBodies;
23 | this.deprecated = deprecated;
24 | this.parameters = parameters;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/site/purrbot/api/objects/openapi/APIPathsResponse.java:
--------------------------------------------------------------------------------
1 | package site.purrbot.api.objects.openapi;
2 |
3 | import site.purrbot.api.objects.RequestResponse;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class APIPathsResponse extends RequestResponse{
9 |
10 | private final List paths;
11 |
12 | public APIPathsResponse(long time, List paths){
13 | super(false, 200, time);
14 | this.paths = paths;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/resources/info.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "${project.name}",
3 | "version": "${project.version}",
4 | "description": "${project.description}",
5 | "links": {
6 | "documentation": "https://docs.purrbot.site/api",
7 | "source": "https://github.com/purrbot-site/ImageAPI",
8 | "api": "https://api.purrbot.site/v2",
9 | "legal": "https://docs.purrbot.site/legal/api"
10 | },
11 | "license": {
12 | "type": "MIT",
13 | "link": "https://github.com/purrbot-site/ImageAPI/blob/master/LICENSE"
14 | }
15 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | %highlight([) %boldWhite(%d{dd.MM.yyyy HH:mm:ss}) %highlight(%-5level) %highlight(]) %cyan([%t]) %boldWhite([%logger{0}]) %boldRed(-) %white(%msg) %n
27 |
28 |
29 |
30 |
31 |
32 |
33 | INFO
34 |
35 | ${DEV_HOME}/Purr.log
36 |
37 | %d{dd.MM.yyyy HH:mm:ss} [%thread] [ %-5level] [%logger{0}] %msg%n
38 |
39 |
40 |
41 | ${DEV_HOME}/archived/purr-%d{dd.MM.yyyy}.%i.log
42 | 100MB
43 | 60
44 | 20GB
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------