Initial commit
parent
739404613f
commit
4b78efd897
|
@ -1 +1,3 @@
|
|||
./output.txt
|
||||
target/
|
||||
.vscode/
|
||||
Cargo.lock
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "mtf-modpost-gen"
|
||||
version = "0.2.0"
|
||||
authors = ["LoneWolfHT <lonewolf04361@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clipboard = "0.5.0"
|
||||
term = "0.7.0"
|
||||
curl = "0.4.34"
|
||||
json = "0.12.4"
|
230
MMG.lua
230
MMG.lua
|
@ -1,230 +0,0 @@
|
|||
local info = {
|
||||
techmodname = "",
|
||||
modname = "",
|
||||
shortdescription = "",
|
||||
repository = "",
|
||||
contentdb = "",
|
||||
license_code = "",
|
||||
license_media = "",
|
||||
depends = "",
|
||||
optional_depends = "",
|
||||
type = "",
|
||||
game = "",
|
||||
game_link = "",
|
||||
download = "",
|
||||
screenshots = {}
|
||||
}
|
||||
|
||||
local answer
|
||||
|
||||
local function cprint(...)
|
||||
io.write(...)
|
||||
end
|
||||
|
||||
local function getinput(type)
|
||||
local temp
|
||||
|
||||
if type == "line" then
|
||||
cprint("\n\n> ")
|
||||
temp = io.read("*line")
|
||||
elseif type == "lines" then
|
||||
cprint("\n\nMultiline input-> ")
|
||||
|
||||
local lines = ""
|
||||
temp = io.read("*line")
|
||||
|
||||
while temp ~= "" do
|
||||
lines = lines .. temp .. "\n"
|
||||
temp = io.read("*line")
|
||||
end
|
||||
|
||||
temp = lines:sub(1, -2)
|
||||
end
|
||||
|
||||
cprint("\n\n")
|
||||
|
||||
return temp
|
||||
end
|
||||
|
||||
local function string_to_table(string)
|
||||
local temptable = {}
|
||||
|
||||
while string:find("\n") ~= nil do
|
||||
table.insert(temptable, string:sub(1, string:find("\n")-1))
|
||||
string = string:sub(string:find("\n") + 1)
|
||||
end
|
||||
|
||||
if string ~= "" then
|
||||
table.insert(temptable, string)
|
||||
end
|
||||
|
||||
return temptable
|
||||
end
|
||||
|
||||
cprint("\n[Minetest Modpost Gen]\nPress enter twice with multiline input to move to the next question\n\n")
|
||||
|
||||
-- Get info needed
|
||||
|
||||
while info.techmodname == "" do
|
||||
cprint("Please enter the name for your mod as defined in your mod.conf")
|
||||
info.techmodname = getinput("line")
|
||||
end
|
||||
|
||||
cprint("Please enter the user-friendly name for your mod (Skip to use capitalized mod.conf name)")
|
||||
info.modname = getinput("line")
|
||||
|
||||
if info.modname == "" then info.modname = info.techmodname:sub(1, 1):upper() .. info.techmodname:sub(2) end
|
||||
|
||||
cprint("Please enter a short description (Skip for none)")
|
||||
info.shortdescription = getinput("lines")
|
||||
|
||||
cprint("Do you have screenshots to provide? Answer yes/no")
|
||||
|
||||
repeat
|
||||
answer = getinput("line"):lower()
|
||||
until answer:find("y") ~= nil or answer:find("n") ~= nil
|
||||
|
||||
if answer:find("y") ~= nil then -- answer was yes
|
||||
cprint("Please provide link(s) to your screenshot(s). One per line")
|
||||
info.screenshots = string_to_table(getinput("lines"))
|
||||
end
|
||||
|
||||
cprint("Please enter the link to your Github repo (Skip for none)")
|
||||
info.repository = getinput("line")
|
||||
|
||||
if info.repository ~= "" and info.repository:sub(-1, -1) ~= "/" then
|
||||
info.repository = info.repository .. "/"
|
||||
end
|
||||
|
||||
cprint("Please enter the link to your CDB page")
|
||||
info.contentdb = getinput("line")
|
||||
|
||||
if info.contentdb == "" then -- No CDB link provided, so can't create a download link out of it
|
||||
while info.download == "" do
|
||||
cprint("Please provide a download link")
|
||||
info.download = getinput("line")
|
||||
end
|
||||
else
|
||||
if info.contentdb:sub(-1, -1) ~= "/" then
|
||||
info.contentdb = info.contentdb .. "/"
|
||||
end
|
||||
end
|
||||
|
||||
while info.license_code == "" do
|
||||
cprint("Please enter the license of your code")
|
||||
info.license_code = getinput("line")
|
||||
end
|
||||
|
||||
cprint("Please enter the license of your media (skip if your mod has no media)")
|
||||
info.license_media = getinput("line")
|
||||
|
||||
cprint("Please enter the dependencies of your mod (skip for none)")
|
||||
info.depends = getinput("line")
|
||||
|
||||
cprint("Please list the optional dependencies of your mod (skip for none)")
|
||||
info.optional_depends = getinput("line")
|
||||
|
||||
if info.depends == "" then info.depends = "None" end
|
||||
if info.optional_depends == "" then info.optional_depends = "None" end
|
||||
|
||||
-- Prepare info for generation
|
||||
|
||||
cprint("Is your mod WIP? Answer yes/no")
|
||||
|
||||
repeat
|
||||
answer = getinput("line"):lower()
|
||||
until answer:find("y") ~= nil or answer:find("n") ~= nil
|
||||
|
||||
if answer:find("y") ~= nil then
|
||||
info.type = "WIP"
|
||||
else
|
||||
info.type = "Mod"
|
||||
end
|
||||
|
||||
cprint("Is your mod made only for a specific game? Type the name of it if so. Skip if not")
|
||||
info.game = getinput("line")
|
||||
|
||||
if info.game ~= "" then
|
||||
cprint("Please provide a link to the game, if there is one")
|
||||
info.game_link = getinput("line")
|
||||
end
|
||||
|
||||
--
|
||||
--- Generate mod topic
|
||||
--
|
||||
|
||||
local file = assert(io.open("output.txt", "w"))
|
||||
|
||||
-- Subject
|
||||
|
||||
file:write("Subject:\n")
|
||||
|
||||
file:write(("-\n[%s] %s [%s]\n-\n"):format(info.type, info.modname, info.techmodname))
|
||||
|
||||
-- Content
|
||||
|
||||
file:write("\nContent:\n-\n")
|
||||
|
||||
file:write(("[size=150][b]%s[/b][/size]\n\n"):format(info.modname))
|
||||
|
||||
if info.shortdescription ~= "" then
|
||||
file:write(info.shortdescription)
|
||||
end
|
||||
|
||||
if info.screenshots ~= {} and #info.screenshots >= 1 then
|
||||
if #info.screenshots == 1 then
|
||||
file:write(("\n\n[img]%s[/img]"):format(info.screenshots[1]))
|
||||
else
|
||||
file:write("\n\n[spoiler=Screenshots]\n")
|
||||
|
||||
for _, imglink in ipairs(info.screenshots) do
|
||||
file:write(("[img]%s[/img]\n"):format(imglink))
|
||||
end
|
||||
|
||||
file:write("[/spoiler]")
|
||||
end
|
||||
end
|
||||
|
||||
local download_link
|
||||
|
||||
if info.contentdb == "" then
|
||||
download_link = info.download
|
||||
else
|
||||
download_link = info.contentdb .. "download/"
|
||||
end
|
||||
|
||||
file:write(("\n\n[b]Downloads:[/b] [url=%s]Latest Stable[/url]\n\n"):format(download_link))
|
||||
|
||||
if info.contentdb ~= "" then
|
||||
file:write(("[b]View:[/b] [url=%s]On ContentDB[/url]\n"):format(info.contentdb))
|
||||
end
|
||||
|
||||
if info.repository ~= "" then
|
||||
file:write(("[b]View:[/b] [url=%s]Source Code[/url]\n"):format(info.repository))
|
||||
end
|
||||
|
||||
file:write("\n")
|
||||
|
||||
if info.license_media == "" then
|
||||
file:write(("[b]License:[/b] %s\n"):format(info.license_code))
|
||||
else
|
||||
file:write(("[b]License of Code:[/b] %s\n"):format(info.license_code))
|
||||
file:write(("[b]License of Media:[/b] %s\n"):format(info.license_media))
|
||||
end
|
||||
|
||||
if info.game == "" then
|
||||
file:write(("\n[b]Depends:[/b] %s\n"):format(info.depends))
|
||||
file:write(("[b]Optional Depends:[/b] %s"):format(info.optional_depends))
|
||||
else
|
||||
if info.game_link == "" then
|
||||
file:write(("\n[b]Depends upon game:[/b] %s"):format(info.game))
|
||||
else
|
||||
file:write(("\n[b]Depends upon game:[/b] [url=%s]%s[/url]"):format(info.game_link, info.game))
|
||||
end
|
||||
end
|
||||
|
||||
file:write("\n-\n")
|
||||
|
||||
file:close()
|
||||
|
||||
cprint("Modpost generated! If you want to include a full description put it at the end of the post.\n")
|
32
README.md
32
README.md
|
@ -2,33 +2,7 @@
|
|||
|
||||
A CLI-based forum topic generator I use for easily creating consistent mod posts for all of my mods on the Minetest Forums
|
||||
|
||||
## Example output
|
||||
There is currently a question-based and cdb-pulling method for generation.
|
||||
|
||||
```
|
||||
Subject:
|
||||
-
|
||||
[Mod] 3d Armor form for mobile users [3d_armor_mobile]
|
||||
-
|
||||
|
||||
Content:
|
||||
-
|
||||
[size=150][b]3d Armor form for mobile users[/b][/size]
|
||||
|
||||
Adds a command to bring up a formspec that allows mobile users to put on armor
|
||||
|
||||
[img]https://github.com/LoneWolfHT/3d_armor_mobile/blob/master/screenshot.png[/img]
|
||||
|
||||
[b]Downloads:[/b] [url=https://content.minetest.net/packages/Lone_Wolf/3d_armor_mobile/download/]Latest Stable[/url]
|
||||
|
||||
[b]View:[/b] [url=https://content.minetest.net/packages/Lone_Wolf/3d_armor_mobile/]On ContentDB[/url]
|
||||
[b]View:[/b] [url=https://github.com/LoneWolfHT/3d_armor_mobile/]Source Code[/url]
|
||||
|
||||
[b]License:[/b] MIT
|
||||
|
||||
[b]Depends:[/b] 3d_armor
|
||||
[b]Optional Depends:[/b] None
|
||||
-
|
||||
```
|
||||
|
||||
Screenshot of my terminal when generating it:
|
||||
https://cdn.discordapp.com/attachments/453772264542961666/676247448716443648/unknown.png
|
||||
Screenshot of my terminal when generating a mod post:
|
||||
![Screenshot](screenshot.png)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
|
@ -0,0 +1,364 @@
|
|||
use std::env;
|
||||
use std::io;
|
||||
|
||||
extern crate term;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use clipboard::ClipboardContext;
|
||||
use clipboard::ClipboardProvider;
|
||||
|
||||
use curl::easy::{Easy, List};
|
||||
|
||||
const SCREENSHOT_LIMIT: usize = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ModInfo {
|
||||
modtype: String,
|
||||
name: String,
|
||||
author: String,
|
||||
desc: String,
|
||||
license: String,
|
||||
media_license: String,
|
||||
repo: String,
|
||||
screenshots: [String; SCREENSHOT_LIMIT],
|
||||
title: String,
|
||||
download_link: String,
|
||||
depends: String,
|
||||
optional_depends: String,
|
||||
contentdb: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap();
|
||||
|
||||
let mut result: String = String::new();
|
||||
let mut input: String = String::new();
|
||||
|
||||
let mut modinfo = ModInfo {
|
||||
modtype: String::from("Mod"),
|
||||
name: String::new(),
|
||||
title: String::new(),
|
||||
author: String::new(),
|
||||
desc: String::new(),
|
||||
license: String::new(),
|
||||
media_license: String::new(),
|
||||
repo: String::new(),
|
||||
screenshots: Default::default(),
|
||||
download_link: String::new(),
|
||||
depends: String::new(),
|
||||
optional_depends: String::new(),
|
||||
contentdb: String::new(),
|
||||
};
|
||||
|
||||
print_color("[MT Forums Modpost Generator]\n\n", term::color::GREEN);
|
||||
|
||||
if args.len() > 1 {
|
||||
if args[1] == "--fromcdb" || args[1] == "-c" {
|
||||
if args.len() <= 2 {
|
||||
get_input(true, "Please link your ContentDB page", &mut input);
|
||||
} else {
|
||||
input = args[2].clone();
|
||||
}
|
||||
|
||||
let mut link: String = input.replace(
|
||||
"https://content.minetest.net/",
|
||||
"https://content.minetest.net/api/",
|
||||
);
|
||||
|
||||
if input == link {
|
||||
print_color("Invalid link!\n\n", term::color::RED);
|
||||
return;
|
||||
} else {
|
||||
let result = get_info_from_cdb(&link);
|
||||
|
||||
modinfo.name.push_str(result["name"].as_str().unwrap());
|
||||
modinfo.title.push_str(result["title"].as_str().unwrap());
|
||||
modinfo.author.push_str(result["author"].as_str().unwrap());
|
||||
modinfo
|
||||
.desc
|
||||
.push_str(result["short_description"].as_str().unwrap());
|
||||
modinfo
|
||||
.license
|
||||
.push_str(result["license"].as_str().unwrap());
|
||||
modinfo
|
||||
.media_license
|
||||
.push_str(result["media_license"].as_str().unwrap());
|
||||
modinfo.repo.push_str(result["repo"].as_str().unwrap());
|
||||
|
||||
for (idx, val) in result["screenshots"].members().enumerate() {
|
||||
if idx < SCREENSHOT_LIMIT {
|
||||
modinfo.screenshots[idx].push_str(val.as_str().unwrap());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
modinfo.download_link = link.replace("api/", "");
|
||||
|
||||
// download link is cdb link without /download on the end
|
||||
modinfo.contentdb = modinfo.download_link.clone();
|
||||
modinfo.download_link.push_str("download/");
|
||||
|
||||
link.push_str("dependencies/");
|
||||
|
||||
let result = get_info_from_cdb(&link);
|
||||
|
||||
for dep in result[modinfo.author.clone() + "/" + &modinfo.name].members() {
|
||||
let mut dependptr = &mut modinfo.depends;
|
||||
if dep["is_optional"] == true {
|
||||
dependptr = &mut modinfo.optional_depends;
|
||||
}
|
||||
|
||||
if !dependptr.is_empty() {
|
||||
dependptr.push_str(", ");
|
||||
}
|
||||
|
||||
dependptr.push_str(dep["name"].as_str().unwrap())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Usage: {} [--fromcdb | -c]", args[0]);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
print_color("To skip a request just press enter\n", term::color::YELLOW);
|
||||
|
||||
get_input(
|
||||
true,
|
||||
"Please provide the mod name used in your mod.conf",
|
||||
&mut modinfo.name,
|
||||
);
|
||||
|
||||
get_input(
|
||||
true,
|
||||
"Please provide a human-readable mod name",
|
||||
&mut modinfo.title,
|
||||
);
|
||||
|
||||
get_input(
|
||||
true,
|
||||
"Please provide a one-line mod description",
|
||||
&mut modinfo.desc,
|
||||
);
|
||||
|
||||
get_input(true, "Please provide a code license", &mut modinfo.license);
|
||||
|
||||
get_input(
|
||||
false,
|
||||
"Please provide a media license",
|
||||
&mut modinfo.media_license,
|
||||
);
|
||||
|
||||
get_input(
|
||||
false,
|
||||
"Please provide a link to your git repo",
|
||||
&mut modinfo.repo,
|
||||
);
|
||||
|
||||
for idx in 0..SCREENSHOT_LIMIT {
|
||||
if !get_input(
|
||||
false,
|
||||
"Please link a screenshot",
|
||||
&mut modinfo.screenshots[idx],
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get_input(
|
||||
false,
|
||||
"Please provide a direct download link",
|
||||
&mut modinfo.download_link,
|
||||
);
|
||||
|
||||
get_input(
|
||||
false,
|
||||
"Please list your hard dependencies",
|
||||
&mut modinfo.depends,
|
||||
);
|
||||
|
||||
get_input(
|
||||
false,
|
||||
"Please list your optional dependencies",
|
||||
&mut modinfo.optional_depends,
|
||||
);
|
||||
|
||||
get_input(false, "Is your mod a Work In Progress? (y/n)", &mut input);
|
||||
|
||||
if input.contains("y") {
|
||||
modinfo.modtype = String::from("WIP")
|
||||
}
|
||||
}
|
||||
|
||||
print_color("[!Forum Post Generated!]\n\n", term::color::GREEN);
|
||||
|
||||
// Give forum post title
|
||||
result.push_str(&format!(
|
||||
"[{}] {} [{}]",
|
||||
modinfo.modtype, modinfo.title, modinfo.name
|
||||
));
|
||||
|
||||
if get_yn_answer("Do you want to copy the mod post title to your clipboard? (y/n)") {
|
||||
ctx.set_contents(result.to_owned()).unwrap();
|
||||
|
||||
println!("Copied to clipboard!\n")
|
||||
}
|
||||
|
||||
print_color(
|
||||
&format!("\n{}\n{}\n{}\n\n", "-".repeat(8), result, "-".repeat(8)),
|
||||
term::color::YELLOW,
|
||||
);
|
||||
|
||||
get_yn_answer("Press Enter to continue"); // you see nothing
|
||||
|
||||
result.clear();
|
||||
get_result(modinfo, &mut result);
|
||||
|
||||
if get_yn_answer("Do you want to copy the mod post contents to your clipboard? (y/n)") {
|
||||
ctx.set_contents(result.to_owned()).unwrap();
|
||||
|
||||
println!("Copied to clipboard!\n")
|
||||
}
|
||||
|
||||
print_color(
|
||||
&format!("\n{}\n{}\n{}\n\n", "-".repeat(15), &result, "-".repeat(15),),
|
||||
term::color::YELLOW,
|
||||
);
|
||||
}
|
||||
|
||||
fn get_result(modinfo: ModInfo, result: &mut String) {
|
||||
// Add header
|
||||
result.push_str(&format!("[h]{}[/h]\n{}\n\n", modinfo.title, modinfo.desc));
|
||||
|
||||
// Show one screenshot and put the rest in a spoiler
|
||||
if !modinfo.screenshots[0].is_empty() {
|
||||
result.push_str(&format!("[img]{}[/img]\n", modinfo.screenshots[0]));
|
||||
|
||||
if !modinfo.screenshots[1].is_empty() {
|
||||
result.push_str("[tspoiler=More Screenshots]");
|
||||
|
||||
for idx in 1..SCREENSHOT_LIMIT {
|
||||
if modinfo.screenshots[idx].is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_str(&format!("\n[img]{}[/img]", modinfo.screenshots[idx]));
|
||||
}
|
||||
|
||||
result.push_str("\n[/tspoiler]\n\n")
|
||||
} else {
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
result.push_str("[h][/h]\n\n");
|
||||
|
||||
// Add direct download link
|
||||
if !modinfo.download_link.is_empty() {
|
||||
result.push_str(&format!(
|
||||
"[b]Downloads:[/b] [url={}]Latest Stable[/url]\n",
|
||||
modinfo.download_link
|
||||
));
|
||||
}
|
||||
|
||||
// Add repo link
|
||||
if !modinfo.repo.is_empty() {
|
||||
result.push_str(&format!(
|
||||
"[b]View:[/b] [url={}]Source Code[/url]",
|
||||
modinfo.repo
|
||||
));
|
||||
if !modinfo.contentdb.is_empty() {
|
||||
result.push_str(&format!(" | [url={}]ContentDB[/url]", modinfo.contentdb));
|
||||
}
|
||||
|
||||
result.push_str("\n\n");
|
||||
} else {
|
||||
result.push('\n');
|
||||
}
|
||||
|
||||
// Add mod license(s)
|
||||
if modinfo.media_license.is_empty() {
|
||||
result.push_str(&format!("[b]License:[/b] {}\n\n", modinfo.license));
|
||||
} else {
|
||||
result.push_str(&format!("[b]License of Code:[/b] {}\n", modinfo.license));
|
||||
result.push_str(&format!(
|
||||
"[b]License of Media:[/b] {}\n\n",
|
||||
modinfo.media_license
|
||||
));
|
||||
}
|
||||
|
||||
// Add mod dependencies
|
||||
if !modinfo.depends.is_empty() {
|
||||
result.push_str(&format!("[b]Depends:[/b] {}\n", modinfo.depends));
|
||||
}
|
||||
if !modinfo.optional_depends.is_empty() {
|
||||
result.push_str(&format!(
|
||||
"[b]Optional Depends:[/b] {}\n",
|
||||
modinfo.optional_depends
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_input(required: bool, message: &str, string: &mut String) -> bool {
|
||||
print!(
|
||||
"\n[{}] {}\n\n",
|
||||
if required { "Required" } else { "Optional" },
|
||||
message
|
||||
);
|
||||
|
||||
while string.is_empty() {
|
||||
io::stdin().read_line(string).expect("Failed to read");
|
||||
*string = String::from(string.trim());
|
||||
|
||||
if !required {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string.shrink_to_fit();
|
||||
return !string.is_empty();
|
||||
}
|
||||
|
||||
// Get y/n answer
|
||||
fn get_yn_answer(message: &str) -> bool {
|
||||
let mut string: String = String::new();
|
||||
|
||||
println!("{}\n", message);
|
||||
|
||||
io::stdin().read_line(&mut string).expect("Failed to read");
|
||||
string = String::from(string.trim());
|
||||
|
||||
return string.contains("y");
|
||||
}
|
||||
|
||||
fn print_color(message: &str, color: term::color::Color) {
|
||||
let mut t = term::stdout().unwrap();
|
||||
|
||||
t.fg(color).unwrap();
|
||||
write!(t, "{}", message).unwrap();
|
||||
|
||||
t.reset().unwrap();
|
||||
}
|
||||
|
||||
fn get_info_from_cdb(link: &String) -> json::JsonValue {
|
||||
let mut easy = Easy::new();
|
||||
let mut list = List::new();
|
||||
let mut json_buf = Vec::new();
|
||||
|
||||
easy.url(&link).unwrap();
|
||||
list.append("Content-Type: application/json").unwrap();
|
||||
easy.http_headers(list).unwrap();
|
||||
{
|
||||
let mut transfer = easy.transfer();
|
||||
transfer
|
||||
.write_function(|data| {
|
||||
json_buf.extend_from_slice(data);
|
||||
Ok(data.len())
|
||||
})
|
||||
.unwrap();
|
||||
transfer.perform().unwrap();
|
||||
}
|
||||
|
||||
return json::parse(std::str::from_utf8(json_buf.as_slice()).expect("Invalid UTF-8")).unwrap();
|
||||
}
|
Loading…
Reference in New Issue