feat: assemblage d'image pour la recommandation de comptes par le curator
This commit is contained in:
parent
ac38059f80
commit
033b6d99dc
Binary file not shown.
After Width: | Height: | Size: 318 KiB |
|
@ -1,51 +1,48 @@
|
||||||
import Masto from "mastodon";
|
import Masto from 'mastodon'
|
||||||
import dotenv from "dotenv";
|
import dotenv from 'dotenv'
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp'
|
||||||
import fs from 'fs';
|
import fs from 'fs'
|
||||||
import https from 'https';
|
import https from 'https'
|
||||||
import moment from "moment";
|
import moment from 'moment'
|
||||||
|
|
||||||
import Parser from 'rss-parser';
|
|
||||||
|
|
||||||
|
import Parser from 'rss-parser'
|
||||||
|
import { load } from 'cheerio'
|
||||||
|
|
||||||
let local_node_env_conf = dotenv.config()
|
let local_node_env_conf = dotenv.config()
|
||||||
const myArgs = process.argv.slice(2);
|
const myArgs = process.argv.slice(2)
|
||||||
export const reallySendPost = hasCliArgument('--force');
|
export const reallySendPost = hasCliArgument('--force')
|
||||||
export const folderBlogPostsPreview = process.cwd() + '/assets/blog_posts_medias/'
|
export const folderBlogPostsPreview = process.cwd() + '/assets/blog_posts_medias/'
|
||||||
|
|
||||||
|
export function randomIntFromInterval (min, max) { // min and max included
|
||||||
export function randomIntFromInterval(min, max) { // min and max included
|
return Math.floor(Math.random() * (max - min + 1) + min) | 1
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min) | 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomElementOfArray(listItems) {
|
export function getRandomElementOfArray (listItems) {
|
||||||
return listItems[Math.floor(Math.random() * listItems.length)]
|
return listItems[Math.floor(Math.random() * listItems.length)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let nowDate = new Date()
|
let nowDate = new Date()
|
||||||
|
|
||||||
export let defaultConfigMasto = {
|
export let defaultConfigMasto = {
|
||||||
author: 'curator',
|
author: 'curator',
|
||||||
visibility: 'public',
|
visibility: 'public',
|
||||||
language: 'fr',
|
language: 'fr',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
disable_slugify: false,
|
disable_slugify: false,
|
||||||
reallySendPost,
|
reallySendPost,
|
||||||
image: '',
|
image: '',
|
||||||
folder_image: process.cwd() + '/assets/blog_posts_medias/',
|
folder_image: process.cwd() + '/assets/blog_posts_medias/',
|
||||||
message: "Hey coucou! on est le" + nowDate,
|
message: 'Hey coucou! on est le' + nowDate,
|
||||||
scheduled_at: "",
|
scheduled_at: '',
|
||||||
scheduled_at_bool: false,
|
scheduled_at_bool: false,
|
||||||
content_type: "text/markdown",
|
content_type: 'text/markdown',
|
||||||
website: 'qzine',
|
website: 'qzine',
|
||||||
slug: 'default_post_title',
|
slug: 'default_post_title',
|
||||||
postObject: {},
|
postObject: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tokenForAuthorIsPresentInDotEnv (author) {
|
||||||
export function tokenForAuthorIsPresentInDotEnv(author) {
|
return process.env['TOKEN_' + author.toUpperCase()]
|
||||||
return process.env['TOKEN_' + author.toUpperCase()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,13 +50,13 @@ export function tokenForAuthorIsPresentInDotEnv(author) {
|
||||||
* @param userNickName
|
* @param userNickName
|
||||||
* @returns {Mastodon}
|
* @returns {Mastodon}
|
||||||
*/
|
*/
|
||||||
export function createMastoFetcherWithAuthorLogin(userNickName) {
|
export function createMastoFetcherWithAuthorLogin (userNickName) {
|
||||||
let accessToken = process.env['TOKEN_' + userNickName.toUpperCase()]
|
let accessToken = process.env['TOKEN_' + userNickName.toUpperCase()]
|
||||||
const masto = new Masto({
|
const masto = new Masto({
|
||||||
access_token: accessToken,
|
access_token: accessToken,
|
||||||
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
|
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
|
||||||
});
|
})
|
||||||
return masto;
|
return masto
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,140 +64,139 @@ export function createMastoFetcherWithAuthorLogin(userNickName) {
|
||||||
* @param config
|
* @param config
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export default function sendPostMastodon(config) {
|
export default function sendPostMastodon (config) {
|
||||||
|
|
||||||
// console.log('send post', config.postObject.post_guid , config.postObject.guid )
|
// console.log('send post', config.postObject.post_guid , config.postObject.guid )
|
||||||
// override defaults with input argument
|
// override defaults with input argument
|
||||||
config = {
|
config = {
|
||||||
...defaultConfigMasto,
|
...defaultConfigMasto,
|
||||||
...config,
|
...config,
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("sendPostMastodon config", config)
|
// console.log("sendPostMastodon config", config)
|
||||||
|
|
||||||
if (!config.reallySendPost) {
|
if (!config.reallySendPost) {
|
||||||
|
|
||||||
console.log("\n\n =========== le message ne sera PAS réellement posté sur le compte @" + config.author + "@" + process.env.INSTANCE_MASTODON + " =========== \n")
|
console.log('\n\n =========== le message ne sera PAS réellement posté sur le compte @' + config.author + '@' + process.env.INSTANCE_MASTODON + ' =========== \n')
|
||||||
// console.log('configPost.folder_image', config.folder_image)
|
// console.log('configPost.folder_image', config.folder_image)
|
||||||
console.log('config', config.message)
|
console.log('config', config.message)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
console.log(" ")
|
console.log(' ')
|
||||||
if (process.env.INSTANCE_MASTODON && tokenForAuthorIsPresentInDotEnv(config.author)) {
|
if (process.env.INSTANCE_MASTODON && tokenForAuthorIsPresentInDotEnv(config.author)) {
|
||||||
|
|
||||||
let visibility = 'public';
|
let visibility = 'public'
|
||||||
let language = 'fr';
|
let language = 'fr'
|
||||||
let sensitive = false;
|
let sensitive = false
|
||||||
|
|
||||||
let accessToken = process.env['TOKEN_' + config.author.toUpperCase()]
|
let accessToken = process.env['TOKEN_' + config.author.toUpperCase()]
|
||||||
const masto = new Masto({
|
const masto = new Masto({
|
||||||
access_token: accessToken,
|
access_token: accessToken,
|
||||||
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
|
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
|
||||||
});
|
})
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
status: config.message,
|
status: config.message,
|
||||||
visibility,
|
visibility,
|
||||||
language,
|
language,
|
||||||
sensitive
|
sensitive
|
||||||
}
|
}
|
||||||
if (config.cw) {
|
if (config.cw) {
|
||||||
params['spoiler_text'] = config.cw
|
params['spoiler_text'] = config.cw
|
||||||
}
|
}
|
||||||
if (config.scheduled_at && config.scheduled_at_bool) {
|
if (config.scheduled_at && config.scheduled_at_bool) {
|
||||||
let dateschedule = new Date(config.scheduled_at)
|
let dateschedule = new Date(config.scheduled_at)
|
||||||
params['scheduled_at'] = dateschedule.toISOString()
|
params['scheduled_at'] = dateschedule.toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* envoi sans fichier joint
|
* envoi sans fichier joint
|
||||||
*/
|
*/
|
||||||
if (!config.image) {
|
if (!config.image) {
|
||||||
console.log('pas d image dans la config')
|
console.log('pas d image dans la config')
|
||||||
|
|
||||||
if (config.reallySendPost) {
|
if (config.reallySendPost) {
|
||||||
|
|
||||||
masto.post('statuses', params).then(rep => {
|
masto.post('statuses', params).then(rep => {
|
||||||
|
|
||||||
console.log("posté, yay!")
|
console.log('posté, yay!')
|
||||||
}, err => {
|
}, err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* envoi avec fichier,
|
* envoi avec fichier,
|
||||||
* on doit d'abord faire un upload du fichier,
|
* on doit d'abord faire un upload du fichier,
|
||||||
* puis relier son id de média au nouveau post.
|
* puis relier son id de média au nouveau post.
|
||||||
*/
|
*/
|
||||||
else if (config.image) {
|
else if (config.image) {
|
||||||
|
|
||||||
var id;
|
var id
|
||||||
console.log("envoi du média", config.image)
|
console.log('envoi du média', config.image)
|
||||||
// upload new media
|
// upload new media
|
||||||
return masto.post('media', {file: fs.createReadStream(config.image)})
|
return masto.post('media', { file: fs.createReadStream(config.image) })
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
id = resp.data.id;
|
id = resp.data.id
|
||||||
params.media_ids = [id]
|
params.media_ids = [id]
|
||||||
console.log("\n ✅ image, id", id)
|
console.log('\n ✅ image, id', id)
|
||||||
masto.post('statuses', params).then(rep => {
|
masto.post('statuses', params).then(rep => {
|
||||||
// console.log('rep', rep)
|
// console.log('rep', rep)
|
||||||
console.log("\n ✅ posté avec une nouvelle image, WOOT")
|
console.log('\n ✅ posté avec une nouvelle image, WOOT')
|
||||||
}, err => {
|
}, err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|
||||||
console.log("erreur T_T")
|
console.log('erreur T_T')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.error(`pas de token pour l'auteur "${config.author}" ou pas d'instance mastodon définie`)
|
console.error(`pas de token pour l'auteur "${config.author}" ou pas d'instance mastodon définie`)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slugify a string
|
// Slugify a string
|
||||||
export function slugify(str) {
|
export function slugify (str) {
|
||||||
str = str.replace(/^\s+|\s+$/g, '');
|
str = str.replace(/^\s+|\s+$/g, '')
|
||||||
|
|
||||||
// Make the string lowercase
|
// Make the string lowercase
|
||||||
str = str.toLowerCase();
|
str = str.toLowerCase()
|
||||||
|
|
||||||
// Remove accents, swap ñ for n, etc
|
// Remove accents, swap ñ for n, etc
|
||||||
var from = "ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;";
|
var from = 'ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;'
|
||||||
var to = "AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------";
|
var to = 'AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------'
|
||||||
for (var i = 0, l = from.length; i < l; i++) {
|
for (var i = 0, l = from.length; i < l; i++) {
|
||||||
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
|
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove invalid chars
|
// Remove invalid chars
|
||||||
str = str.replace(/[^a-z0-9 -]/g, '')
|
str = str.replace(/[^a-z0-9 -]/g, '')
|
||||||
// Collapse whitespace and replace by -
|
// Collapse whitespace and replace by -
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
// Collapse dashes
|
// Collapse dashes
|
||||||
.replace(/-+/g, '-');
|
.replace(/-+/g, '-')
|
||||||
|
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name listFilesOfFolder
|
* @name listFilesOfFolder
|
||||||
* lister les noms de fichier que l'on peut publier dans un dossier.
|
* lister les noms de fichier que l'on peut publier dans un dossier.
|
||||||
* retourne un tableau
|
* retourne un tableau
|
||||||
*/
|
*/
|
||||||
export function listFilesOfFolder(folderPath) {
|
export function listFilesOfFolder (folderPath) {
|
||||||
let filesNames = []
|
let filesNames = []
|
||||||
fs.readdirSync(folderPath).map(fileName => {
|
fs.readdirSync(folderPath).map(fileName => {
|
||||||
return filesNames.push(fileName);
|
return filesNames.push(fileName)
|
||||||
});
|
})
|
||||||
|
|
||||||
return filesNames;
|
return filesNames
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,14 +204,14 @@ export function listFilesOfFolder(folderPath) {
|
||||||
* crée un dossier d'assets, avec ses sous dossiers not_published et published si ils manquent.
|
* crée un dossier d'assets, avec ses sous dossiers not_published et published si ils manquent.
|
||||||
* une fois que l'on prendra une image dans le dossier non publié, on la déplacera dans le dossier des images publées.
|
* une fois que l'on prendra une image dans le dossier non publié, on la déplacera dans le dossier des images publées.
|
||||||
*/
|
*/
|
||||||
export function initializeFolderForPictures(folderName) {
|
export function initializeFolderForPictures (folderName) {
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(folderName)) {
|
if (!fs.existsSync(folderName)) {
|
||||||
fs.mkdirSync(folderName);
|
fs.mkdirSync(folderName)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,36 +219,36 @@ export function initializeFolderForPictures(folderName) {
|
||||||
* @param htmlContent
|
* @param htmlContent
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function findFirstImageInContent(htmlContent = '') {
|
export function findFirstImageInContent (htmlContent = '') {
|
||||||
let result = ''
|
let result = ''
|
||||||
let foundPictures = htmlContent.match(/<img\s[^>]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/);
|
let foundPictures = htmlContent.match(/<img\s[^>]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/)
|
||||||
let first = '';
|
let first = ''
|
||||||
if (foundPictures && foundPictures[0]) {
|
if (foundPictures && foundPictures[0]) {
|
||||||
|
|
||||||
first = foundPictures[0]
|
first = foundPictures[0]
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log('pas d image trouvée dans le contenu ', htmlContent)
|
console.log('pas d image trouvée dans le contenu ', htmlContent)
|
||||||
}
|
}
|
||||||
if (first) {
|
if (first) {
|
||||||
result = first.match(/src\=\"(.*)\"/i)
|
result = first.match(/src\=\"(.*)\"/i)
|
||||||
if (result.length && result[0]) {
|
if (result.length && result[0]) {
|
||||||
result = result[0].split('"')
|
result = result[0].split('"')
|
||||||
result = result[1]
|
result = result[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = clearLink(result)
|
result = clearLink(result)
|
||||||
console.log('clearLink', result)
|
console.log('clearLink', result)
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearLink(linkString) {
|
function clearLink (linkString) {
|
||||||
linkString = linkString.replace('http:', 'https:')
|
linkString = linkString.replace('http:', 'https:')
|
||||||
linkString = linkString.replace('https://www.ailesse.info/~tykayn/bazar/kotlife', 'https://www.tykayn.fr/wp-content/uploads/i/kotlife')
|
linkString = linkString.replace('https://www.ailesse.info/~tykayn/bazar/kotlife', 'https://www.tykayn.fr/wp-content/uploads/i/kotlife')
|
||||||
linkString = linkString.replace('https://blog.artlemoine.com/public/i', 'https://www.tykayn.fr/wp-content/uploads/i')
|
linkString = linkString.replace('https://blog.artlemoine.com/public/i', 'https://www.tykayn.fr/wp-content/uploads/i')
|
||||||
linkString = linkString.replace('https://www.ailesse.com/%7Etykayn/bazar', 'https://www.tykayn.fr/wp-content/uploads/i/bazar')
|
linkString = linkString.replace('https://www.ailesse.com/%7Etykayn/bazar', 'https://www.tykayn.fr/wp-content/uploads/i/bazar')
|
||||||
|
|
||||||
return linkString
|
return linkString
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,25 +260,25 @@ function clearLink(linkString) {
|
||||||
* @param filepath
|
* @param filepath
|
||||||
* @returns {Promise<unknown>}
|
* @returns {Promise<unknown>}
|
||||||
*/
|
*/
|
||||||
export function downloadImage(url, filepath) {
|
export function downloadImage (url, filepath) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const options = {
|
const options = {
|
||||||
headers: {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52"}
|
headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52' }
|
||||||
};
|
}
|
||||||
|
|
||||||
https.get(url, options, (res) => {
|
https.get(url, options, (res) => {
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
res.pipe(fs.createWriteStream(filepath))
|
res.pipe(fs.createWriteStream(filepath))
|
||||||
.on('error', reject)
|
.on('error', reject)
|
||||||
.once('close', () => resolve(filepath));
|
.once('close', () => resolve(filepath))
|
||||||
} else {
|
} else {
|
||||||
// Consume response data to free up memory
|
// Consume response data to free up memory
|
||||||
res.resume();
|
res.resume()
|
||||||
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}; \n ${res.statusMessage} `));
|
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}; \n ${res.statusMessage} `))
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,124 +289,238 @@ export function downloadImage(url, filepath) {
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export function CropPicture(pictureName, width = 500, height = 300) {
|
export function CropPicture (pictureName, width = 500, height = 300) {
|
||||||
|
|
||||||
return sharp(pictureName)
|
return sharp(pictureName)
|
||||||
|
|
||||||
.extract({left: 0, top: 0, width, height})
|
.extract({ left: 0, top: 0, width, height })
|
||||||
.toFile('thumb_' + pictureName, function (err) {
|
.toFile('thumb_' + pictureName, function (err) {
|
||||||
if (err) console.log(err);
|
if (err) console.log(err)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* prendre un post parmi tous ceux du blog, dans ceux qui ont été publiés
|
* prendre un post parmi tous ceux du blog, dans ceux qui ont été publiés
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export function getRandomLinkGeneral(tkpostsjson) {
|
export function getRandomLinkGeneral (tkpostsjson) {
|
||||||
let filteredLinks = []
|
let filteredLinks = []
|
||||||
if (tkpostsjson[0].post_status) {
|
if (tkpostsjson[0].post_status) {
|
||||||
|
|
||||||
filteredLinks = tkpostsjson.filter(elem => elem.post_status === 'publish')
|
filteredLinks = tkpostsjson.filter(elem => elem.post_status === 'publish')
|
||||||
} else if (tkpostsjson[0].status) {
|
} else if (tkpostsjson[0].status) {
|
||||||
filteredLinks = tkpostsjson.filter(elem => elem.status === 'publish')
|
filteredLinks = tkpostsjson.filter(elem => elem.status === 'publish')
|
||||||
}
|
}
|
||||||
return getRandomElementOfArray(filteredLinks)
|
return getRandomElementOfArray(filteredLinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* trouver l'image du contenu si il y en a
|
* trouver l'image du contenu si il y en a
|
||||||
* @param postContent
|
* @param postContent
|
||||||
* @param configPost
|
* @param configPost
|
||||||
*/
|
*/
|
||||||
export function findPictureAndSendPost(postContent, configPost) {
|
export function findPictureAndSendPost (postContent, configPost) {
|
||||||
|
|
||||||
let firstPictureSource = findFirstImageInContent(postContent);
|
let firstPictureSource = findFirstImageInContent(postContent)
|
||||||
|
|
||||||
|
let filePathForDownloadedImage = `${configPost.folder_image}_${configPost.website}_media_post_${slugify(configPost.slug)}.jpg`
|
||||||
|
|
||||||
let filePathForDownloadedImage = `${configPost.folder_image}_${configPost.website}_media_post_${slugify(configPost.slug)}.jpg`
|
if (firstPictureSource) {
|
||||||
|
console.log('firstPictureSource found', firstPictureSource)
|
||||||
|
|
||||||
if (firstPictureSource) {
|
// check if picture already exist
|
||||||
console.log("firstPictureSource found", firstPictureSource)
|
console.log('on envoie le média et l image : ', filePathForDownloadedImage)
|
||||||
|
downloadImage(firstPictureSource, filePathForDownloadedImage)
|
||||||
|
.then((res) => {
|
||||||
|
// suite du poste avec upload d'image
|
||||||
|
|
||||||
// check if picture already exist
|
console.log('média téléchargé, on envoie le post')
|
||||||
console.log('on envoie le média et l image : ', filePathForDownloadedImage)
|
configPost.image = filePathForDownloadedImage
|
||||||
downloadImage(firstPictureSource, filePathForDownloadedImage)
|
|
||||||
.then((res) => {
|
|
||||||
// suite du poste avec upload d'image
|
|
||||||
|
|
||||||
console.log('média téléchargé, on envoie le post')
|
sendPostMastodon(configPost)
|
||||||
configPost.image = filePathForDownloadedImage;
|
|
||||||
|
|
||||||
sendPostMastodon(configPost)
|
},
|
||||||
|
(err) => {
|
||||||
|
console.log('pas dimage trouvée pour l URL ', firstPictureSource, err)
|
||||||
|
sendPostMastodon(configPost)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('erreur avec cette URL ', firstPictureSource, err)
|
||||||
|
sendPostMastodon(configPost)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
|
||||||
},
|
// no image provided
|
||||||
(err) => {
|
|
||||||
console.log('pas dimage trouvée pour l URL ', firstPictureSource, err)
|
|
||||||
sendPostMastodon(configPost)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
console.log('erreur avec cette URL ', firstPictureSource, err)
|
|
||||||
sendPostMastodon(configPost)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// no image provided
|
console.log('pas d\'image dans le corps du texte', configPost.image)
|
||||||
|
// on envoie avec l'image par défaut
|
||||||
console.log("pas d'image dans le corps du texte", configPost.image)
|
sendPostMastodon(configPost)
|
||||||
// on envoie avec l'image par défaut
|
}
|
||||||
sendPostMastodon(configPost)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find cli argument
|
* find cli argument
|
||||||
* @param argument
|
* @param argument
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function hasCliArgument(argument) {
|
export function hasCliArgument (argument) {
|
||||||
return myArgs.indexOf(argument) !== -1
|
return myArgs.indexOf(argument) !== -1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parser = new Parser()
|
||||||
|
|
||||||
let parser = new Parser();
|
export function diffDaysBetweenTwoDates (date1, date2) {
|
||||||
|
|
||||||
|
const a = moment(date1)
|
||||||
export function diffDaysBetweenTwoDates(date1, date2) {
|
const b = moment(date2)
|
||||||
|
return a.diff(b, 'days')
|
||||||
const a = moment(date1);
|
|
||||||
const b = moment(date2);
|
|
||||||
return a.diff(b, 'days');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function filterRegionAgendaDuLibreEvents (events_list, filter_critera) {
|
||||||
export function filterRegionAgendaDuLibreEvents(events_list, filter_critera) {
|
let selection = []
|
||||||
let selection = []
|
events_list.forEach(item => {
|
||||||
events_list.forEach(item => {
|
if (item.region_id == filter_critera) {
|
||||||
if (item.region_id == filter_critera) {
|
selection.push(item)
|
||||||
selection.push(item)
|
}
|
||||||
}
|
})
|
||||||
})
|
return selection
|
||||||
return selection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moment.locale('fr');
|
moment.locale('fr')
|
||||||
|
|
||||||
export function groupEventsByDay(events_list) {
|
export function groupEventsByDay (events_list) {
|
||||||
let selection = {}
|
let selection = {}
|
||||||
events_list.forEach(item => {
|
events_list.forEach(item => {
|
||||||
|
|
||||||
let formattedDay = moment(item.start_time).format('dddd DD')
|
let formattedDay = moment(item.start_time).format('dddd DD')
|
||||||
|
|
||||||
|
if (!selection[formattedDay]) {
|
||||||
|
selection[formattedDay] = []
|
||||||
|
}
|
||||||
|
selection[formattedDay].push(item)
|
||||||
|
})
|
||||||
|
return selection
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertHTMLtoMD (htmlContent) {
|
||||||
|
const $ = load(htmlContent)
|
||||||
|
|
||||||
|
translateNode($)
|
||||||
|
|
||||||
|
return $.html()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function translateNode ($) {
|
||||||
|
const elementsToTranslate = [
|
||||||
|
{ tag: 'h1', mdTag: '##' },
|
||||||
|
{ tag: 'h2', mdTag: '###' },
|
||||||
|
{ tag: 'h3', mdTag: '####' },
|
||||||
|
{ tag: 'h4', mdTag: '#####' },
|
||||||
|
{ tag: 'h5', mdTag: '######' },
|
||||||
|
{ tag: 'p', mdTag: '\n\n' },
|
||||||
|
{ tag: 'ul', preserveChildren: true, transformChild: ($child) => '\n- ' + $.text($child) },
|
||||||
|
{ tag: 'ol', preserveChildren: true, transformChild: ($child, idx) => `\n${idx + 1}. ${$.text($child)}` },
|
||||||
|
{ tag: 'a', parseAttr: ('href', link => `[${link}](${link})`) },
|
||||||
|
{ tag: 'strong', mdFormat: '$&' },
|
||||||
|
{ tag: 'em', mdFormat: '_$&_' },
|
||||||
|
{ tag: 'code', mdFormat: '`$&`' },
|
||||||
|
]
|
||||||
|
|
||||||
|
elementsToTranslate.forEach(element => {
|
||||||
|
$(element.tag).each((_, el) => {
|
||||||
|
const $el = $(el)
|
||||||
|
if (element.parseAttr) {
|
||||||
|
const attrVal = $el.attr(element.parseAttr[0])
|
||||||
|
if (attrVal) {
|
||||||
|
$el.replaceWith(element.parseAttr[1](attrVal))
|
||||||
|
} else {
|
||||||
|
$el.remove()
|
||||||
|
}
|
||||||
|
} else if (element.transformChild) {
|
||||||
|
$el.contents().filter(() => !!this.parent()).each((_, childEl) => {
|
||||||
|
const transformedText = element.transformChild($(childEl), $el.index())
|
||||||
|
$el.before('\n' + transformedText)
|
||||||
|
})
|
||||||
|
$el.remove()
|
||||||
|
} else {
|
||||||
|
$el.replaceWith(element.mdTag + $el.text() + element.mdFormat)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRequestOptions (host, port, path) {
|
||||||
|
const options = {
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
path: path,
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/activity+json',
|
||||||
|
'User-Agent': 'MyApp/1.0.0 (contact@myemail.org)',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendGetRequest (options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request(options, (res) => {
|
||||||
|
let body = Buffer.alloc(0)
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
body = Buffer.concat([body, chunk])
|
||||||
|
})
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
resolve(JSON.parse(body.toString()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
req.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
req.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitTextIntoChunks = (text, limit) => {
|
||||||
|
const words = text.trim().split(/\s+/g)
|
||||||
|
const chunks = []
|
||||||
|
let currentChunk = []
|
||||||
|
let wordCount = 0
|
||||||
|
|
||||||
|
for (const word of words) {
|
||||||
|
if (wordCount + word.length > limit) {
|
||||||
|
chunks.push(currentChunk.join(' '))
|
||||||
|
currentChunk = [word]
|
||||||
|
wordCount = word.length
|
||||||
|
} else {
|
||||||
|
currentChunk.push(word)
|
||||||
|
wordCount += word.length + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentChunk.length > 0) {
|
||||||
|
const joinedWords = currentChunk.join(' ')
|
||||||
|
chunks.push(joinedWords.length <= limit ? joinedWords : joinedWords.substr(0, limit) + '...')
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
export function splitLongDescription (text, limit) {
|
||||||
|
let chunks = splitTextIntoChunks(text, limit)
|
||||||
|
if (chunks) {
|
||||||
|
if (chunks[0]) {
|
||||||
|
return chunks[0]
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!selection[formattedDay]) {
|
|
||||||
selection[formattedDay] = []
|
|
||||||
}
|
|
||||||
selection[formattedDay].push(item)
|
|
||||||
})
|
|
||||||
return selection;
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
|
|
||||||
import sendPostMastodon, {
|
|
||||||
getArticlesFromDaysInRssFeed,
|
|
||||||
makeTitleContentFromRss,
|
|
||||||
parseRssFeed,
|
|
||||||
} from "./libs/utils.mjs";
|
|
||||||
|
|
||||||
// const afis_rss_feed_url = 'https://afis.org/rss.xml';
|
|
||||||
// const days_back_in_rss_feed = 7;
|
|
||||||
|
|
||||||
async function postLink() {
|
|
||||||
|
|
||||||
|
|
||||||
// selectionner un compte parmi les gens suivis dans une liste
|
|
||||||
|
|
||||||
// export des abonnements utilisateur https://mastodon.cipherbliss.com/settings/exports/follows.csv/home/cipherbliss/Nextcloud/inbox/following_accounts.csv
|
|
||||||
// <https://mastodon.cipherbliss.com/api/v1/accounts/1/following?max_id=19658>; rel="next", <https://mastodon.cipherbliss.com/api/v1/accounts/1/following?since_id=20294>; rel="prev"
|
|
||||||
// https://mastodon.cipherbliss.com/api/v1/accounts/relationships?id[]=109484382634211819&id[]=109280269336881811&id[]=109280889310776283&id[]=109248504398682634&id[]=109310268216721885&id[]=109916178850231009&id[]=109541053771316772&id[]=151980&id[]=80685&id[]=106835252230809041&id[]=109167546376395742&id[]=109791782092094822&id[]=109770197347890560&id[]=109768052505508503&id[]=108202891614303950&id[]=109749045115736569&id[]=109739290485102920&id[]=109330282577556324&id[]=109280646010767217&id[]=109722335836985403&id[]=109542013632346871&id[]=109278739925137927&id[]=109659515794606337&id[]=109292570383233152&id[]=109660244192213202&id[]=109303521370666602&id[]=109523867267929049&id[]=109643780720044907&id[]=109386436202793665&id[]=109501996699619458&id[]=109632939061534470&id[]=109621031128728719&id[]=109620790782200477&id[]=139144&id[]=109592762903342668&id[]=109603261782470086&id[]=109591788539379136&id[]=109570049875530270&id[]=109381927764082328&id[]=109554213128019255
|
|
||||||
// exemple d'account:
|
|
||||||
/**
|
|
||||||
{
|
|
||||||
"id": "109484382634211819",
|
|
||||||
"username": "GalmeshRosewood",
|
|
||||||
"acct": "GalmeshRosewood@mastodon.art",
|
|
||||||
"display_name": "Galmesh Rosewood",
|
|
||||||
"locked": false,
|
|
||||||
"bot": false,
|
|
||||||
"discoverable": true,
|
|
||||||
"group": false,
|
|
||||||
"created_at": "2022-12-05T00:00:00.000Z",
|
|
||||||
"note": "<p>I'm Galmesh Rosewood and I want to be a lewd artist. (It's a Monkey Island ref). Male from France. Some content won't be family-friendly.</p>",
|
|
||||||
"url": "https://mastodon.art/@GalmeshRosewood",
|
|
||||||
"avatar": "https://mastodon.cipherbliss.com/system/cache/accounts/avatars/109/484/382/634/211/819/original/4d3c4ded9ae95202.jpg",
|
|
||||||
"avatar_static": "https://mastodon.cipherbliss.com/system/cache/accounts/avatars/109/484/382/634/211/819/original/4d3c4ded9ae95202.jpg",
|
|
||||||
"header": "https://mastodon.cipherbliss.com/system/cache/accounts/headers/109/484/382/634/211/819/original/5b1822520e4ae3b3.png",
|
|
||||||
"header_static": "https://mastodon.cipherbliss.com/system/cache/accounts/headers/109/484/382/634/211/819/original/5b1822520e4ae3b3.png",
|
|
||||||
"followers_count": 762,
|
|
||||||
"following_count": 195,
|
|
||||||
"statuses_count": 327,
|
|
||||||
"last_status_at": "2023-03-04",
|
|
||||||
"emojis": [],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"name": "Linktree",
|
|
||||||
"value": "<a href=\"https://linktr.ee/galmeshrosewood\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">linktr.ee/galmeshrosewood</span><span class=\"invisible\"></span></a>",
|
|
||||||
"verified_at": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// faire un repost de média
|
|
||||||
// POST /api/v1/statuses/109965725969165853/reblog
|
|
||||||
|
|
||||||
// autre action:
|
|
||||||
// faire une liste de recommandation de 3 gens à follow au hasard pris dans cette liste,
|
|
||||||
// avec en image attachée un des médias postés par les comptes en question
|
|
||||||
|
|
||||||
|
|
||||||
// console.log("envoi de post des articles afis sciences publiés depuis " + days_back_in_rss_feed + " jours par le compte afis91")
|
|
||||||
//
|
|
||||||
// let parsedFeed = await parseRssFeed(afis_rss_feed_url)
|
|
||||||
// let articles = await getArticlesFromDaysInRssFeed(parsedFeed, days_back_in_rss_feed)
|
|
||||||
// console.log('articles', articles)
|
|
||||||
// let contentOfPost = 'Articles des ' + days_back_in_rss_feed + ' derniers jours sur le site web de l\'AFIS: \n'
|
|
||||||
// + makeTitleContentFromRss(articles)
|
|
||||||
//
|
|
||||||
// let configPost = {
|
|
||||||
// author: 'afis91',
|
|
||||||
// website: 'afis.org',
|
|
||||||
// // disable_slugify: true,
|
|
||||||
// slug: "afis_picture",
|
|
||||||
// content_type: "text/markdown",
|
|
||||||
// folder_image: process.cwd() + '/assets/',
|
|
||||||
// image: "afis_picture_of_the_day.jpg",
|
|
||||||
// message: `# ${contentOfPost}
|
|
||||||
//
|
|
||||||
// #afis #science #pseudoScience #hebdo`,
|
|
||||||
// }
|
|
||||||
// sendPostMastodon(configPost)
|
|
||||||
}
|
|
||||||
|
|
||||||
postLink();
|
|
|
@ -15,6 +15,7 @@
|
||||||
"express": "~4.16.1",
|
"express": "~4.16.1",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"jade": "~1.11.0",
|
"jade": "~1.11.0",
|
||||||
|
"jimp": "^0.22.10",
|
||||||
"mastodon": "^1.2.2",
|
"mastodon": "^1.2.2",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
"webpage": "^0.3.0"
|
"webpage": "^0.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.7",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
// selectionner un compte parmi les gens suivis dans une liste
|
||||||
|
|
||||||
|
// export des abonnements utilisateur https://mastodon.cipherbliss.com/settings/exports/follows.csv/home/cipherbliss/Nextcloud/inbox/following_accounts.csv
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import Masto from 'mastodon'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* picture generation
|
||||||
|
*/
|
||||||
|
import Jimp from 'jimp'
|
||||||
|
import { getRequestOptions, sendGetRequest } from '../helpers/libs/utils.mjs'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
let reallySendPost = false
|
||||||
|
reallySendPost = true
|
||||||
|
|
||||||
|
async function getFollowers (username, instance) {
|
||||||
|
const url = `${instance}/api/v1/accounts/${username}/following`
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(url)
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to look up the user account number using webfinger
|
||||||
|
async function getUserAccountNumberFromMastodonUsername (username) {
|
||||||
|
const parsedUsername = username.split('@')
|
||||||
|
if (parsedUsername.length !== 2) {
|
||||||
|
throw new Error('Invalid Mastodon username format.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const localPart = parsedUsername[0]
|
||||||
|
const domain = parsedUsername[1]
|
||||||
|
console.log('localPart, domain', localPart, domain)
|
||||||
|
|
||||||
|
const acctPath = '/.well-known/webfinger?resource=' + encodeURIComponent('acct:' + localPart + '@' + domain)
|
||||||
|
const options = getRequestOptions(domain, 443, acctPath)
|
||||||
|
|
||||||
|
console.log('options', options)
|
||||||
|
try {
|
||||||
|
const fingerResult = await sendGetRequest(options)
|
||||||
|
const actorResource = fingerResult['subject'] || fingerResult.links?.find((item) => item.rel === 'self').href
|
||||||
|
|
||||||
|
if (!actorResource) {
|
||||||
|
throw new Error('Failed to obtain the resource identifier.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountHost = actorResource.split('/')[2]
|
||||||
|
const accountPath = actorResource.split('/').pop()
|
||||||
|
|
||||||
|
const accountInfoReqOptions = getRequestOptions(accountHost, 443, '/api/v1/accounts/' + accountPath)
|
||||||
|
const accountInfo = await sendGetRequest(accountInfoReqOptions)
|
||||||
|
|
||||||
|
return accountInfo.id
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main () {
|
||||||
|
|
||||||
|
const userIdOnInstance = '1'
|
||||||
|
|
||||||
|
const instance = 'mastodon.cipherbliss.com'
|
||||||
|
|
||||||
|
const followers = await getFollowers(userIdOnInstance, instance)
|
||||||
|
|
||||||
|
console.log('followers.length', followers.length)
|
||||||
|
|
||||||
|
const randomFollowers = followers?.sort(() => Math.random() - 0.5)?.slice(0, 3)
|
||||||
|
|
||||||
|
// console.log('Random followers:', randomFollowers)
|
||||||
|
let message = '\n Les personnes que l\'on vous recommande de suivre aujourd\'hui:'
|
||||||
|
|
||||||
|
let avatars_urls = []
|
||||||
|
randomFollowers.forEach(account => {
|
||||||
|
message += '\n' + displayDataAboutFollower(account)
|
||||||
|
|
||||||
|
console.log('account', account)
|
||||||
|
avatars_urls.push(account.avatar_static)
|
||||||
|
})
|
||||||
|
message += '\n #fedifollows #curatorRecommendations'
|
||||||
|
|
||||||
|
generateAvatarComposite(avatars_urls)
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* displays username, acct, avatar and url for one account
|
||||||
|
* @param follower
|
||||||
|
*/
|
||||||
|
function displayDataAboutFollower (follower) {
|
||||||
|
|
||||||
|
let text = ''
|
||||||
|
if (follower.note) {
|
||||||
|
text = follower.note.trim().substring(0, 150)
|
||||||
|
if (follower.note.trim() > 150) {
|
||||||
|
text += '...'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ` * _${follower.display_name}_: [${follower.acct}](${follower.url})
|
||||||
|
${text.trim()}`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let folderUnpublished = ''
|
||||||
|
let compositeFileName = ''
|
||||||
|
|
||||||
|
// Function to maintain aspect ratio while scaling an image down.
|
||||||
|
function scaleDownPreserveAspectRatio (image, newWidth, newHeight) {
|
||||||
|
const originalWidth = image.bitmap.width
|
||||||
|
const originalHeight = image.bitmap.height
|
||||||
|
|
||||||
|
const scaleFactor = Math.min(newWidth / originalWidth, newHeight / originalHeight)
|
||||||
|
|
||||||
|
const newWidthScaled = Math.round(originalWidth * scaleFactor)
|
||||||
|
const newHeightScaled = Math.round(originalHeight * scaleFactor)
|
||||||
|
|
||||||
|
return image.scaleToFit(newWidthScaled, newHeightScaled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prend trois URL d'image d'avatar et génère une image composite JPG avec celles-ci,
|
||||||
|
* enregistrée sous le format «YYYY-MM-DDTHH-MM-SS.jpg».
|
||||||
|
*/
|
||||||
|
async function generateAvatarComposite (avatarUrls) {
|
||||||
|
if (!Array.isArray(avatarUrls) || avatarUrls.length !== 3) {
|
||||||
|
throw new Error('Veuillez fournir exactement trois URL d’avatar.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Télécharger chaque avatar
|
||||||
|
const avatarImagesPromises = avatarUrls.map((url) => downloadImage(url))
|
||||||
|
const avatarImages = await Promise.all(avatarImagesPromises)
|
||||||
|
|
||||||
|
// Combiner les images horizontalement
|
||||||
|
// Resize avatars to a consistent dimension of 400x400 pixels while maintaining aspect ratio
|
||||||
|
const resizedAvatarImages = avatarImages.map((image) => scaleDownPreserveAspectRatio(image, 400, 400))
|
||||||
|
|
||||||
|
// Combine the images horizontally
|
||||||
|
const combinedWidth = 3 * resizedAvatarImages[0].bitmap.width
|
||||||
|
const combinedHeight = Math.max(...resizedAvatarImages.map((image) => image.bitmap.height))
|
||||||
|
const composite = new Jimp(combinedWidth, combinedHeight)
|
||||||
|
|
||||||
|
for (let i = 0; i < resizedAvatarImages.length; i++) {
|
||||||
|
const xOffset = i * resizedAvatarImages[0].bitmap.width
|
||||||
|
composite.composite(resizedAvatarImages[i], xOffset, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enregistrer l'image composite
|
||||||
|
compositeFileName = `avatars_reccommendations_${Date.now().toString()}.jpg`
|
||||||
|
await composite.writeAsync(compositeFileName)
|
||||||
|
console.log(`L'image composite a été enregistrée sous le nom "${compositeFileName}".`)
|
||||||
|
return compositeFileName
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Télécharge une image depuis l'URL spécifiée et renvoie une instance Jimp.
|
||||||
|
*/
|
||||||
|
async function downloadImage (url) {
|
||||||
|
return await Jimp.read(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
function publishOnMastodon (folderUnpublished, configPost) {
|
||||||
|
|
||||||
|
let accessToken = process.env['TOKEN_' + configPost.author.toUpperCase()]
|
||||||
|
console.log('accessToken', accessToken)
|
||||||
|
const masto = new Masto({
|
||||||
|
access_token: accessToken,
|
||||||
|
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (configPost.reallySendPost) {
|
||||||
|
|
||||||
|
let imagePath = path.resolve() + configPost.image
|
||||||
|
console.log('imagePath', imagePath)
|
||||||
|
/**
|
||||||
|
* poster le média, puis faire un toot avec le média lié
|
||||||
|
*/
|
||||||
|
masto.post('media', { file: fs.createReadStream(imagePath) })
|
||||||
|
.then(resp => {
|
||||||
|
let id = resp.data.id
|
||||||
|
configPost.media_ids = [id]
|
||||||
|
console.log('\n\n id du média pour le post:', id)
|
||||||
|
masto.post('statuses', configPost).then(rep => {
|
||||||
|
// console.log('rep', rep)
|
||||||
|
console.log(`\n\n posté avec une nouvelle image, ${configPost.image} WOOT`)
|
||||||
|
}, err => {
|
||||||
|
console.error(err)
|
||||||
|
console.log('\n\n erreur T_T')
|
||||||
|
})
|
||||||
|
}, err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('envoi désactivé avec configPost.reallySendPost')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* run all
|
||||||
|
*/
|
||||||
|
|
||||||
|
main().then(message => {
|
||||||
|
// let md_message = convertHTMLtoMD(message)
|
||||||
|
let md_message = message.trim() + '\n'
|
||||||
|
console.log('md_message', md_message)
|
||||||
|
// upload de fileName et création du post par le Curator
|
||||||
|
let configPost = {
|
||||||
|
author: 'curator',
|
||||||
|
website: 'cipherbliss',
|
||||||
|
postObject: {},
|
||||||
|
folder_image: '.',
|
||||||
|
image: compositeFileName,
|
||||||
|
message: md_message,
|
||||||
|
reallySendPost
|
||||||
|
}
|
||||||
|
|
||||||
|
publishOnMastodon(folderUnpublished, configPost)
|
||||||
|
|
||||||
|
}, err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
Loading…
Reference in New Issue