144 lines
3.7 KiB
Lua
144 lines
3.7 KiB
Lua
-- JPEG Glitcher v1.1
|
|
-- Contributed by Francesco <https://github.com/Francesco149>
|
|
do
|
|
local KILOBYTE = 1000
|
|
local MEGABYTE = KILOBYTE * 1000
|
|
|
|
function handle_http_response(code, status)
|
|
if code ~= 200 then
|
|
if status then
|
|
print(status)
|
|
else
|
|
print("http.request failed! (invalid url?)")
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
function get_jpg_header_size(data)
|
|
local res = 417
|
|
for i, byte in ipairs(data) do
|
|
if byte == 255 and data[i + 1] == 218 then
|
|
res = i + 2
|
|
break
|
|
end
|
|
end
|
|
return res
|
|
end
|
|
|
|
function simple_file_size(bytes)
|
|
local simplesize = bytes
|
|
local unit = "B"
|
|
if bytes >= MEGABYTE then
|
|
simplesize = simplesize / MEGABYTE
|
|
unit = "MB"
|
|
elseif bytes >= KILOBYTE then
|
|
simplesize = simplesize / KILOBYTE
|
|
unit = "KB"
|
|
end
|
|
return simplesize, unit
|
|
end
|
|
|
|
local GLITCH_MAX_FILE = MEGABYTE
|
|
|
|
function run(msg, matches)
|
|
http = require("socket.http")
|
|
local stamp = os.time()
|
|
|
|
-- try checking file size from a HEAD request
|
|
local default_timeout = http.TIMEOUT
|
|
http.TIMEOUT = 5
|
|
local _, code, headers, status = http.request {
|
|
url = matches[1],
|
|
method = "HEAD"
|
|
}
|
|
if not handle_http_response(code, status) then
|
|
return "HTTP request failed or file not found"
|
|
end
|
|
|
|
if not string.find(headers["content-type"], "jpeg") then
|
|
-- TODO: convert to jpg?
|
|
return "Not a JPEG file!"
|
|
end
|
|
|
|
local filesize = tonumber(headers["content-length"])
|
|
local response = nil
|
|
|
|
-- if the HEAD request doesn't contain content-length,
|
|
-- send a full GET request. the timeout will abort the request
|
|
-- after 5 seconds if the file is too large or takes too long
|
|
-- to download
|
|
if not filesize then
|
|
print("No content-length, performing a full request...")
|
|
response = http.request(matches[1])
|
|
filesize = #response
|
|
end
|
|
|
|
-- show user-friendly filesize
|
|
simplesize, unit = simple_file_size(filesize)
|
|
print("File Size: " .. simplesize .. unit .. " (" .. filesize .. " bytes)")
|
|
|
|
-- check filesize limit
|
|
if filesize > GLITCH_MAX_FILE then
|
|
return "Sorry, maximum filesize is " .. GLITCH_MAX_FILE / MEGABYTE .. "MB!"
|
|
end
|
|
|
|
-- if we previously got the file size through a HEAD request,
|
|
-- retrieve the actual data with a full request
|
|
if not response then
|
|
response, code, _, status = http.request(matches[1])
|
|
if not handle_http_response(code, status) then
|
|
return "HTTP request failed or file not found"
|
|
end
|
|
end
|
|
|
|
-- convert to byte array for manipulation
|
|
local arr = {}
|
|
response:gsub(".", function(c) table.insert(arr, string.byte(c)) end)
|
|
response = nil
|
|
|
|
-- skip jpeg header
|
|
local header_size = get_jpg_header_size(arr)
|
|
print("JPEG header size: " .. header_size .. " bytes")
|
|
|
|
-- corrupt a set amount of bytes
|
|
local data_end = #arr - 4
|
|
local corrupt_count = matches[2]
|
|
if not corrupt_count then
|
|
corrupt_count = 20
|
|
end
|
|
for i=1, corrupt_count do
|
|
local index = math.random(header_size, data_end)
|
|
print("Corrupting byte " .. index)
|
|
arr[index] = math.floor(math.random() * 255)
|
|
end
|
|
|
|
-- write glitched jpg
|
|
print("Writing glitched.jpg")
|
|
local out = assert(io.open("tmp/"..stamp..".jpg", "wb"))
|
|
local glitched = ""
|
|
for i=1, #arr do
|
|
out:write(string.char(arr[i]))
|
|
end
|
|
arr = nil
|
|
assert(out:close())
|
|
|
|
-- send glitched jpg
|
|
local receiver = get_receiver(msg)
|
|
send_photo(receiver, "tmp/"..stamp..".jpg", function() end, function() end)
|
|
|
|
http.TIMEOUT = default_timeout
|
|
end
|
|
|
|
return {
|
|
description = "Randomly glitches a JPEG image",
|
|
usage = "!glitch [url] [amount]: glitches the JPEG image retrieved from _url_ " ..
|
|
"by corrupting _amount_ bytes. Amount is optional and defaults to 20 bytes.",
|
|
patterns = {
|
|
"^/glitch ([^ ]*) ([0-9]*)$",
|
|
"^/glitch ([^ ]*)$"
|
|
},
|
|
run = run
|
|
}
|
|
end |