diff --git a/commands/image-editing/snapchat.js b/commands/image-editing/snapchat.js new file mode 100644 index 0000000..afd9b63 --- /dev/null +++ b/commands/image-editing/snapchat.js @@ -0,0 +1,21 @@ +const ImageCommand = require("../../classes/imageCommand.js"); + +class SnapchatCommand extends ImageCommand { + params(args, url) { + const newArgs = args.filter(item => !item.includes(url)); + return { + caption: newArgs.join(" ").replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") + }; + } + + static description = "Adds a Snapchat style caption to an image"; + static aliases = ["snap", "caption3"]; + static arguments = ["[text]"]; + + static requiresText = true; + static noText = "You need to provide some text to add a caption!"; + static noImage = "You need to provide an image to add a caption!"; + static command = "snapchat"; +} + +module.exports = SnapchatCommand; \ No newline at end of file diff --git a/natives/image.cc b/natives/image.cc index 34e4569..6d7dbc2 100644 --- a/natives/image.cc +++ b/natives/image.cc @@ -26,6 +26,7 @@ #include "retro.h" #include "reverse.h" #include "scott.h" +#include "snapchat.h" #include "speed.h" #include "sonic.h" #include "spin.h" @@ -63,6 +64,7 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) exports.Set(Napi::String::New(env, "retro"), Napi::Function::New(env, Retro)); exports.Set(Napi::String::New(env, "reverse"), Napi::Function::New(env, Reverse)); exports.Set(Napi::String::New(env, "scott"), Napi::Function::New(env, Scott)); + exports.Set(Napi::String::New(env, "snapchat"), Napi::Function::New(env, Snapchat)); exports.Set(Napi::String::New(env, "speed"), Napi::Function::New(env, Speed)); exports.Set(Napi::String::New(env, "sonic"), Napi::Function::New(env, Sonic)); exports.Set(Napi::String::New(env, "spin"), Napi::Function::New(env, Spin)); diff --git a/natives/snapchat.cc b/natives/snapchat.cc new file mode 100644 index 0000000..4b81dff --- /dev/null +++ b/natives/snapchat.cc @@ -0,0 +1,73 @@ +#include +#include + +#include + +using namespace std; +using namespace Magick; + +Napi::Value Snapchat(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + + try { + Napi::Object obj = info[0].As(); + Napi::Buffer data = obj.Get("data").As>(); + string caption = obj.Get("caption").As().Utf8Value(); + string type = obj.Get("type").As().Utf8Value(); + int delay = + obj.Has("delay") ? obj.Get("delay").As().Int32Value() : 0; + + Blob blob; + + list frames; + list coalesced; + list captioned; + Blob caption_blob; + readImages(&frames, Blob(data.Data(), data.Length())); + + size_t width = frames.front().baseColumns(); + string query(to_string(width - ((width / 25) * 2)) + "x"); + Image caption_image(Geometry(query), Color("none")); + caption_image.backgroundColor(Color("none")); + caption_image.fillColor("white"); + caption_image.font("Helvetica Neue"); + caption_image.fontPointsize(width / 25); + caption_image.textGravity(Magick::CenterGravity); + caption_image.read("pango:" + caption); + caption_image.backgroundColor(Color("rgba(0, 0, 0, 0.7)")); + caption_image.extent(Geometry(width, caption_image.rows() + (width / 25)), + Magick::CenterGravity); + + coalesceImages(&coalesced, frames.begin(), frames.end()); + + for (Image &image : coalesced) { + list images; + image.composite(caption_image, Magick::CenterGravity, + Magick::OverCompositeOp); + image.magick(type); + image.animationDelay(delay == 0 ? image.animationDelay() : delay); + captioned.push_back(image); + } + + optimizeTransparency(captioned.begin(), captioned.end()); + + if (type == "gif") { + for (Image &image : captioned) { + image.quantizeDither(false); + image.quantize(); + } + } + + writeImages(captioned.begin(), captioned.end(), &blob); + + Napi::Object result = Napi::Object::New(env); + result.Set("data", Napi::Buffer::Copy(env, (char *)blob.data(), + blob.length())); + result.Set("type", type); + return result; + } catch (std::exception const &err) { + throw Napi::Error::New(env, err.what()); + } catch (...) { + throw Napi::Error::New(env, "Unknown error"); + } +} \ No newline at end of file diff --git a/natives/snapchat.h b/natives/snapchat.h new file mode 100644 index 0000000..4d4b378 --- /dev/null +++ b/natives/snapchat.h @@ -0,0 +1,8 @@ +#ifndef ESMBOT_NATIVES_SNAPCHAT_H_ +#define ESMBOT_NATIVES_SNAPCHAT_H_ + +#include + +Napi::Value Snapchat(const Napi::CallbackInfo& info); + +#endif \ No newline at end of file diff --git a/utils/image.js b/utils/image.js index 6d4ffc8..d368a70 100644 --- a/utils/image.js +++ b/utils/image.js @@ -182,22 +182,23 @@ const start = (object, num) => { const res = start(object, num); reject(res); // this is done to differentiate the result from a step }); - } - currentServer.write(data, (err) => { - if (err) { - if (err.code === "EPIPE") { - logger.log(`Lost connection to ${currentServer.remoteAddress}, attempting to reconnect...`); - currentServer.connect(8080, currentServer.remoteAddress, () => { - const res = start(object, num); - reject(res); // this is done to differentiate the result from a step - }); + } else { + currentServer.write(data, (err) => { + if (err) { + if (err.code === "EPIPE") { + logger.log(`Lost connection to ${currentServer.remoteAddress}, attempting to reconnect...`); + currentServer.connect(8080, currentServer.remoteAddress, () => { + const res = start(object, num); + reject(res); // this is done to differentiate the result from a step + }); + } else { + reject(err); + } } else { - reject(err); + resolve(currentServer.remoteAddress); } - } else { - resolve(currentServer.remoteAddress); - } - }); + }); + } }); }).catch((result) => { throw result;