package ninja.siden.sirusi; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; import javax.imageio.ImageIO; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; public class SirusiHandler implements RequestHandler<Request, Response> { static final Pattern index = Pattern.compile("index\\.htm[l]?", Pattern.CASE_INSENSITIVE); @Override public Response handleRequest(Request input, Context context) { Response res = new Response(); try { if (input.getSeed().equalsIgnoreCase("favicon.ico")) { throw new Redirect(input.getImgroot() + "/favicon.ico"); } else if (input.getSeed().length() < 1 || index.matcher(input.getSeed()).matches()) { AmazonS3 s3 = createS3(); S3Object object = s3.getObject(input.getBucket(), "index.html"); int length = (int) object.getObjectMetadata().getContentLength(); byte[] bytes = new byte[length]; object.getObjectContent().read(bytes); String content = new String(bytes, StandardCharsets.UTF_8); res.setContent(content); return res; } else { byte[] bytes = render(input.getSeed(), input.getSize(), input.getType()); String filename = hash(input.getSeed()) + "_" + input.getSize() + "." + input.getType(); ObjectMetadata om = new ObjectMetadata(); om.setContentType(input.getContentType()); om.setContentLength(bytes.length); Map<String, String> meta = new HashMap<>(); meta.put("Seed", input.getSeed()); om.setUserMetadata(meta); AmazonS3 s3 = createS3(); s3.putObject(input.getBucket(), filename, new ByteArrayInputStream(bytes), om); throw new Redirect(input.getImgroot() + "/" + filename); } } catch (IOException | GeneralSecurityException e) { context.getLogger().log(e.getMessage()); throw new RuntimeException(e); } } static String hash(String seed) throws GeneralSecurityException { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] bytes = md.digest(seed.getBytes(StandardCharsets.UTF_8)); return Base64.getUrlEncoder().encodeToString(bytes); } AmazonS3 createS3() { return new AmazonS3Client(); } public byte[] render(String seed, int size, String formatName) throws IOException { Random r = new Random(new BigInteger(seed.getBytes()).longValue()); int[] kinds = { 5, 6, 7 }; int boxel = kinds[Math.abs(r.nextInt()) % kinds.length]; BufferedImage original = make(r, boxel); BufferedImage img = resize(boxel, original, size); ByteArrayOutputStream out = new ByteArrayOutputStream(48 * 48 * 2); ImageIO.write(img, formatName, out); return out.toByteArray(); } BufferedImage make(Random r, int boxel) { BufferedImage img = square(boxel); int fa = Math.abs(r.nextInt()); int ba = Color.WHITE.getRGB(); int half = (boxel - (boxel % 2)) / 2 + 1; for (int x = 0; x < half; x++) { for (int y = 0; y < boxel; y++) { int col = r.nextBoolean() ? ba : fa; img.setRGB(x, y, col); img.setRGB(boxel - 1 - x, y, col); } } return img; } BufferedImage resize(int boxel, BufferedImage src, int size) { int mod = size % boxel; int scale = (size - mod) / boxel; AffineTransform at = new AffineTransform(); at.scale(scale, scale); AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); BufferedImage out = square(size); Graphics2D g2d = out.createGraphics(); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, size, size); g2d.dispose(); int border = mod / 2; op.filter(src, out.getSubimage(border, border, out.getWidth() - border, out.getHeight() - border)); return out; } BufferedImage square(int size) { return new BufferedImage(size, size, BufferedImage.TYPE_INT_BGR); } }