First commit

This commit is contained in:
mcneb10 2024-06-01 16:46:19 -05:00
commit da366208b6
10 changed files with 11564 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Delete verse.lua to compile from source
#verse.lua
config_private.lua
services.json

17
README.md Normal file
View file

@ -0,0 +1,17 @@
# Lua XMPP Privacy Bot
This bot replaces links to popular sites such as youtube with privacy respecting front ends such as individuous. It is written in 100% pure lua
# How to run
Make sure `make`, `tar`, `gzip`, `lua`, and `luarocks` are installed.
Then do `luarocks install luasocket luaexpat luasec`
Next configure the bot to your liking in `config.lua`. Also don't forget to copy `config_private_example.lua` to `config_private.lua` and fill that out as well.
Then run the `./run` script to run the bot. It will download the farside `services.json` list and compile the `verse.lua` xmpp library.
# List of supported front ends
**TODO**

6
clean Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env lua
print("Deleting compiled verse")
os.remove("verse.lua")
print("Deleting services.json")
os.remove("services.json")

73
config.lua Normal file
View file

@ -0,0 +1,73 @@
-- Main privacy bot configuration file
config = {
-- Log verbosity
verbosity = 1,
-- Bot nickname
name = "Privacy Link Bot",
--[[
This will set the type of url to replace the service domain with. Can be:
- clearnet
- onion
- eepsite
- yggdrasil
- TODO: make it work and more types?
]]--
prefered_website_medium = "clearnet",
-- Choose random frontend instead of fallback one, will force clearnet
random_frontend = true,
-- List of desired frontends to extract from `services.json`
sites = {
-- Key is domain pattern
["reddit[.]com"] = {
-- Specify which frontents should be used
frontends = { "libreddit", "redlib" }
},
["instagram[.]com"] = {
frontends = { "proxigram" }
},
["github[.]com"] = {
frontends = { "gothub" }
},
["google[.]com"] = {
frontends = { "searxng" }
},
["youtube[.]com"] = {
frontends = { "piped", "invidious"}
},
["www[.]youtube[.]com"] = {
frontends = { "piped", "invidious"}
},
["youtu[.]be"] = {
frontends = { "piped", "invidious", }
},
["twitter[.]com"] = {
frontends = { "nitter", }
},
["x[.]com"] = {
frontends = { "nitter", }
},
["wikipedia[.]org"] = {
frontends = { "wikiless", }
},
["medium[.]com"] = {
frontends = { "scribe", }
},
["imgur[.]com"] = {
frontends = { "rimgo", }
},
["translate[.]google[.]com"] = {
frontends = { "lingva", }
},
["tiktok[.]com"] = {
frontends = { "proxitok", }
},
["fandom[.]com"] = {
frontends = { "breezewiki", }
},
-- TODO: the rest
}
}
-- Load config file with private information
dofile("config_private.lua")

View file

@ -0,0 +1,12 @@
-- JID for the bot
config.jid = "bot@server.net"
-- Bot password, stored in plaintext
config.password = "password"
-- Rooms the bot will attempt to join
config.rooms_to_join = {
-- Normal room
{jid = "room@server.net",},
-- Password protected room, password stored in plaintext
{jid = "protected@server.net", password = "pass"},
}

6
get_frontends Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env lua
local farside_instance_json_url = "https://git.sr.ht/~benbusby/farside/blob/HEAD/services.json"
os.remove("services.json")
os.execute(string.format("wget \"%s\"", farside_instance_json_url))

65
main.lua Executable file
View file

@ -0,0 +1,65 @@
-- Get the verse lib
local verse = require("verse")
-- Setup logging and config
require("utils")
local log = setup_log(string.format("%s_main", config.name))
log("info", "Log initialized")
-- Get the client lib
local client_lib = verse.init("client")
-- Make the client
local client = client_lib.new()
-- Load client plugins
client:add_plugin("groupchat")
client:add_plugin("version")
-- Client hooks
client:hook("disconnected", function()
log("error", "XMPP connetion lost. Quitting...")
os.exit(1)
end)
client:hook("authentication-failure", function()
log("error", "Failed to authenticate with XMPP Server. Quitting...")
os.exit(1)
end)
client:hook("authentication-success", function()
log("info", "XMPP authentication sucessful!")
end)
client:hook("ready", function()
log("info", "Client ready")
-- Main code goes here
for _, room in pairs(config.rooms_to_join) do
local room, err = client:join_room(room.jid, config.name, {}, config.password)
if room then
-- Run on message events
log("info", "Joined room \"%s\"", room.jid)
room:hook("message", function(event)
if event.stanza.attr.type == "groupchat" and not string.find(event.stanza.attr.from, "/" .. config.name) then
local body = event.stanza:get_child_text("body")
if body then
for site, services in pairs(config.sites) do
local instance = choose_instance(services.frontends)
-- TODO: make it reply using XEP-0461
for match in string.gmatch(body, string.format("%%s(%s/%%S+)", site)) do
room:send_message(string.format("> %s\nPrivate frontend: %s", match, string.gsub(match, site, instance)))
end
for match in string.gmatch(body, string.format("(https?://%s/%%S+)", site)) do
room:send_message(string.format("> %s\nPrivate frontend: %s", match, string.gsub(match, site, instance)))
end
end
end
end
end)
else
log("error", "Error joining room \"%s\": %s", room.jid, err)
end
end
end)
log("info", "Connecting to server...")
client:connect_client(config.jid, config.password)
verse.loop()
log("info", "Verse loop exited. Quitting...")

51
run Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env lua
-- TODO: luarocks?
-- Download frontends list
if not os.execute(string.format("ls services.json 2>/dev/null >/dev/null")) then
dofile("get_frontends")
end
-- Squish commit hash
local squish_version = "tip"
-- Squish script url
local squish_script = string.format("http://code.matthewwild.co.uk/squish/raw-file/%s/squish.lua", squish_version)
-- Verse commit hash
local verse_version = "98dc1750584d"
-- Location for various verse files
local verse_folder_name = string.format("verse-%s", verse_version)
local verse_archive_filename = string.format("%s.tar.gz", verse_version)
local verse_dist = string.format("http://code.matthewwild.co.uk/verse/archive/%s", verse_archive_filename)
-- Options for verse `./configure` script
local verse_config_opts = ""
-- Options for verse `make`
local verse_make_opts = ""
-- Check if verse module exists and compile if it doesn't
if not os.execute(string.format("ls verse.lua 2>/dev/null >/dev/null")) then
-- Download source archive
os.execute(string.format("wget \"%s\"", verse_dist))
-- Extract the source archive
os.execute(string.format("tar -xf \"%s\"", verse_archive_filename))
-- Delete the source archive
os.remove(verse_archive_filename)
-- Compile library
os.execute(string.format(
[[sh -c "cd \"%s\" && # Go to library dir
wget \"%s\" -O ./buildscripts/squish && # Replace squish with stripped down version that actually works
./configure %s && # Configure the library
make %s # Compile"]],
verse_folder_name, squish_script, verse_config_opts, verse_make_opts
))
-- Copy file
os.execute(string.format("cp \"%s/verse.lua\" .", verse_folder_name))
-- Delete folder
os.execute(string.format("rm -rf \"%s\"", verse_folder_name))
end
-- Load main module
require("main")

100
utils.lua Normal file
View file

@ -0,0 +1,100 @@
-- Various utility functions used by the bot
function log_callback(source, level, message, ...)
local output = string.format(
"%s %s [%s]: %s",
os.date(),
source,
level,
string.format(message, ...)
)
if config.verbosity == 0 then
if level ~= "debug" then
print(output)
end
elseif config.verbosity == 1 then
print(output)
end
end
--[[
Make a new logger with `name` and setup a handler based on the log level `v`
TODO: `v` needs to be figured out, for now it's 1 to print debug messages 0 to not print them or any other value to print nothing
]]--
function setup_log(name)
local log_module = require("util.logger")
log_module.add_level_sink("debug", log_callback)
log_module.add_level_sink("info", log_callback)
log_module.add_level_sink("warn", log_callback)
log_module.add_level_sink("error", log_callback)
return log_module.init(name)
end
-- Read a file in it's entirety, returning an empty string on failure
function read_all_text(file)
local file = io.open(file, "rb")
if not file then
print(string.format("Failed to open file \"%s\"!", file))
return ""
end
local text = file:read("*all")
file:close()
return text
end
-- Choose instance from available services
function choose_instance(services)
-- Choose a random service
local service = services[math.random(#services)]
-- Get list of instances for service
local service_instances
for _, service_instance_list in pairs(config.instances) do
if service_instance_list.type == service then
-- Based on config choose instance
if config.random_frontend then
-- TODO: cache this?
local usable_instances = {}
for _, instance_url_list in pairs(service_instance_list.instances) do
-- Instance URLs are split by pipes
for instance in string.gmatch(instance_url_list, "[^|]+") do
if instance.match(instance, "[.]onion$") then
if config.prefered_website_medium == "onion" then
table.insert(usable_instances, instance)
end
elseif instance.match(instance, "[.]i2p$") then
if config.prefered_website_medium == "eepsite" then
table.insert(usable_instances, instance)
end
elseif instance.match(instance, "[[][%d:]+[]]") then
if config.prefered_website_medium == "yggdrasil" then
table.insert(usable_instances, instance)
end
else
-- Assume clearnet
if config.prefered_website_medium == "clearnet" then
table.insert(usable_instances, instance)
end
end
end
end
return string.gsub(usable_instances[math.random(#usable_instances)], "https?://", "")
else
return string.gsub(service_instance_list.fallback, "https?://", "")
end
end
end
return string.format("%s-no-instances-available", service)
end
-- Config file
dofile("config.lua")
-- Load subsitutions
local services_text = read_all_text("services.json")
local json = require("util.json")
local services, err = json.decode(services_text)
if services then
config.instances = services
else
print(string.format("Error loading \"services.json\": %s", err))
end

11230
verse.lua Normal file

File diff suppressed because it is too large Load diff