works on system, lets try on docker
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -141,4 +141,5 @@ dist
|
|||||||
config.json
|
config.json
|
||||||
state.json
|
state.json
|
||||||
cache.json
|
cache.json
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
public/
|
||||||
127
commands/utility/updateUpgrades.js
Normal file
127
commands/utility/updateUpgrades.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const torn = require('../../torn.js');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const { createCanvas, registerFont } = require('canvas');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('updateupgrades')
|
||||||
|
.setDescription('Generate the faction upgrades PNG'),
|
||||||
|
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply(); // give more time for image generation
|
||||||
|
|
||||||
|
const data = await torn.faction.upgrades();
|
||||||
|
|
||||||
|
const lines = [];
|
||||||
|
lines.push("Core Upgrades:");
|
||||||
|
let armoryNames = [];
|
||||||
|
for (const upgrade of data.upgrades.core.upgrades) {
|
||||||
|
if (upgrade.name && String(upgrade.name).toLowerCase().includes('armory')) {
|
||||||
|
armoryNames.push(upgrade.name.replace(/\s+armory$/i, ''));
|
||||||
|
} else {
|
||||||
|
lines.push(` ${upgrade.name} - ${upgrade.ability}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (armoryNames.length) {
|
||||||
|
lines.push(` Armory: ${armoryNames.join(', ')}`);
|
||||||
|
}
|
||||||
|
lines.push("");
|
||||||
|
lines.push("Peace Upgrades:");
|
||||||
|
for (const branch of data.upgrades.peace) {
|
||||||
|
lines.push(` ${branch.name}`);
|
||||||
|
for (const upgrade of branch.upgrades) {
|
||||||
|
lines.push(` ${upgrade.name} - ${upgrade.ability}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push("");
|
||||||
|
lines.push("War Upgrades:");
|
||||||
|
for (const branch of data.upgrades.war) {
|
||||||
|
lines.push(` ${branch.name}`);
|
||||||
|
for (const upgrade of branch.upgrades) {
|
||||||
|
lines.push(` ${upgrade.name} - ${upgrade.ability}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image rendering settings
|
||||||
|
const padding = 24;
|
||||||
|
const maxWidth = 1100;
|
||||||
|
const fontSize = 18;
|
||||||
|
const fontFamily = 'Sans';
|
||||||
|
const lineHeight = Math.round(fontSize * 1.4);
|
||||||
|
const fontSpec = `${fontSize}px ${fontFamily}`;
|
||||||
|
|
||||||
|
// Temporary canvas for measurement
|
||||||
|
const measureCanvas = createCanvas(10, 10);
|
||||||
|
const measureCtx = measureCanvas.getContext('2d');
|
||||||
|
measureCtx.font = fontSpec;
|
||||||
|
|
||||||
|
function wrapLine(ctx, text, maxW) {
|
||||||
|
const words = text.split(' ');
|
||||||
|
const wrapped = [];
|
||||||
|
let line = '';
|
||||||
|
for (const word of words) {
|
||||||
|
const test = line ? `${line} ${word}` : word;
|
||||||
|
const w = ctx.measureText(test).width;
|
||||||
|
if (w > maxW && line) {
|
||||||
|
wrapped.push(line);
|
||||||
|
line = word;
|
||||||
|
} else {
|
||||||
|
line = test;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line) wrapped.push(line);
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
let visualLines = [];
|
||||||
|
let measuredMaxWidth = 0;
|
||||||
|
const textMaxWidth = maxWidth - padding * 2;
|
||||||
|
for (const ln of lines) {
|
||||||
|
if (!ln) {
|
||||||
|
visualLines.push('');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const wrapped = wrapLine(measureCtx, ln, textMaxWidth);
|
||||||
|
for (const wln of wrapped) {
|
||||||
|
visualLines.push(wln);
|
||||||
|
measuredMaxWidth = Math.max(measuredMaxWidth, Math.ceil(measureCtx.measureText(wln).width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvasWidth = Math.min(maxWidth, measuredMaxWidth + padding * 2);
|
||||||
|
const canvasHeight = padding * 2 + visualLines.length * lineHeight;
|
||||||
|
|
||||||
|
const canvas = createCanvas(canvasWidth, canvasHeight);
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.fillStyle = '#0A2472';
|
||||||
|
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
ctx.font = fontSpec;
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
|
||||||
|
let y = padding;
|
||||||
|
for (const vln of visualLines) {
|
||||||
|
ctx.fillText(vln, padding, y);
|
||||||
|
y += lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const outDir = path.resolve(__dirname, '..', '..', 'public', 'images');
|
||||||
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
|
|
||||||
|
const outFile = path.join(outDir, 'upgrades.png');
|
||||||
|
const buffer = canvas.toBuffer('image/png');
|
||||||
|
fs.writeFileSync(outFile, buffer);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await interaction.editReply({ files: [outFile] });
|
||||||
|
} catch (err) {
|
||||||
|
await interaction.editReply('Generated upgrades image but failed to attach it.');
|
||||||
|
console.debug('Failed to attach image to interaction reply:', err.message || err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -5,4 +5,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./config.json:/usr/src/bot/config.json
|
- ./config.json:/usr/src/bot/config.json
|
||||||
- ./state.json:/usr/src/bot/state.json
|
- ./state.json:/usr/src/bot/state.json
|
||||||
- ./cache.json:/usr/src/bot/cache.json
|
- ./cache.json:/usr/src/bot/cache.json
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
36
index.js
36
index.js
@@ -2,6 +2,7 @@ const cron = require('node-cron');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const torn = require('./torn.js');
|
const torn = require('./torn.js');
|
||||||
|
const express = require('express');
|
||||||
|
|
||||||
let config, state;
|
let config, state;
|
||||||
try {
|
try {
|
||||||
@@ -154,3 +155,38 @@ client.on(Events.MessageCreate, message => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ensure public folder exists
|
||||||
|
const publicDir = path.resolve(__dirname, 'public');
|
||||||
|
fs.mkdirSync(publicDir, { recursive: true });
|
||||||
|
|
||||||
|
// load optional config.json (use httpPort) or PORT env var
|
||||||
|
let cfg = {};
|
||||||
|
const cfgPath = path.resolve(__dirname, 'config.json');
|
||||||
|
if (fs.existsSync(cfgPath)) {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
try { cfg = require(cfgPath); } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
const port = process.env.PORT || cfg.httpPort || 3000;
|
||||||
|
|
||||||
|
// create simple static server
|
||||||
|
const app = express();
|
||||||
|
app.use(express.static(publicDir));
|
||||||
|
|
||||||
|
// convenience routes
|
||||||
|
app.get('/upgrades.png', (req, res) => {
|
||||||
|
const imgPath = path.join(publicDir, 'images', 'upgrades.png');
|
||||||
|
if (fs.existsSync(imgPath)) return res.sendFile(imgPath);
|
||||||
|
res.status(404).send('Not found');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/images', (req, res) => {
|
||||||
|
const imgDir = path.join(publicDir, 'images');
|
||||||
|
if (!fs.existsSync(imgDir)) return res.status(404).send('No images');
|
||||||
|
const files = fs.readdirSync(imgDir).filter(f => /\.(png|jpe?g|gif)$/i.test(f));
|
||||||
|
res.send(`<pre>${files.join('\n')}</pre>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Static server running: http://localhost:${port}/ (serving ${publicDir})`);
|
||||||
|
});
|
||||||
|
|||||||
1211
package-lock.json
generated
1211
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,9 @@
|
|||||||
"homepage": "https://github.com/CesiumCs/tornbot#readme",
|
"homepage": "https://github.com/CesiumCs/tornbot#readme",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"canvas": "^3.2.0",
|
||||||
"discord.js": "^14.18.0",
|
"discord.js": "^14.18.0",
|
||||||
|
"express": "^5.2.1",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"node-cron": "^3.0.3"
|
"node-cron": "^3.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
2
torn.js
2
torn.js
@@ -194,7 +194,7 @@ module.exports.faction = {
|
|||||||
async upgrades() {
|
async upgrades() {
|
||||||
const response = await fetch(`https://api.torn.com/v2/faction/upgrades?key=${config.torn}`);
|
const response = await fetch(`https://api.torn.com/v2/faction/upgrades?key=${config.torn}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return(data.upgrades);
|
return(data);
|
||||||
},
|
},
|
||||||
async news(category, from) {
|
async news(category, from) {
|
||||||
const response = await fetch(`https://api.torn.com/v2/faction/news?striptags=false&limit=100&sort=DESC&from=${from}&cat=${category}&key=${config.torn}`)
|
const response = await fetch(`https://api.torn.com/v2/faction/news?striptags=false&limit=100&sort=DESC&from=${from}&cat=${category}&key=${config.torn}`)
|
||||||
|
|||||||
Reference in New Issue
Block a user