import Masto from 'mastodon'
import dotenv from 'dotenv'
import sharp from 'sharp'
import fs from 'fs'
import https from 'https'
import moment from 'moment'
import Parser from 'rss-parser'
import {load} from 'cheerio'
let local_node_env_conf = dotenv.config()
const myArgs = process.argv.slice(2)
export const reallySendPost = hasCliArgument('--force')
console.log('reallySendPost', reallySendPost)
export const folderBlogPostsPreview = process.cwd() + '/assets/blog_posts_medias/'
export function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min) | 1
}
export function getRandomElementOfArray(listItems) {
return listItems[Math.floor(Math.random() * listItems.length)]
}
let nowDate = new Date()
export let defaultConfigMasto = {
author: 'curator',
visibility: 'public',
language: 'fr',
sensitive: false,
disable_slugify: false,
reallySendPost,
image: '',
folder_image: process.cwd() + '/assets/blog_posts_medias/',
message: 'Hey coucou! on est le' + nowDate,
scheduled_at: '',
scheduled_at_bool: false,
content_type: 'text/markdown',
website: 'qzine',
slug: 'default_post_title',
postObject: {},
}
export function tokenForAuthorIsPresentInDotEnv(author) {
return process.env['TOKEN_' + author.toUpperCase()]
}
/**
* create a masto fetcher instance
* @param userNickName
* @returns {Mastodon}
*/
export function createMastoFetcherWithAuthorLogin(userNickName) {
let accessToken = process.env['TOKEN_' + userNickName.toUpperCase()]
const masto = new Masto({
access_token: accessToken,
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
})
return masto
}
/**
* send post to mastodon with config
* @param config
* @returns {*}
*/
export default function sendPostMastodon(config) {
// console.log('send post', config.postObject.post_guid , config.postObject.guid )
// override defaults with input argument
config = {
...defaultConfigMasto,
...config,
}
// console.log("sendPostMastodon config", config)
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('configPost.folder_image', config.folder_image)
console.log('config', config.message)
} else {
console.log(' ')
if (process.env.INSTANCE_MASTODON && tokenForAuthorIsPresentInDotEnv(config.author)) {
let visibility = 'public'
let language = 'fr'
let sensitive = false
let accessToken = process.env['TOKEN_' + config.author.toUpperCase()]
const masto = new Masto({
access_token: accessToken,
api_url: process.env.INSTANCE_MASTODON + '/api/v1/',
})
let params = {
status: config.message,
visibility,
language,
sensitive
}
if (config.cw) {
params['spoiler_text'] = config.cw
}
if (config.scheduled_at && config.scheduled_at_bool) {
let dateschedule = new Date(config.scheduled_at)
params['scheduled_at'] = dateschedule.toISOString()
}
/**
* envoi sans fichier joint
*/
if (!config.image) {
console.log('pas d image dans la config')
if (config.reallySendPost) {
masto.post('statuses', params).then(rep => {
console.log('posté, yay!')
}, err => {
console.error(err)
})
}
}
/**
* envoi avec fichier,
* on doit d'abord faire un upload du fichier,
* puis relier son id de média au nouveau post.
*/
else if (config.image) {
var id
console.log('envoi du média', config.image)
// upload new media
return masto.post('media', {file: fs.createReadStream(config.image)})
.then(resp => {
id = resp.data.id
params.media_ids = [id]
console.log('\n ✅ image, id', id)
masto.post('statuses', params).then(rep => {
// console.log('rep', rep)
console.log('\n ✅ posté avec une nouvelle image, WOOT')
}, err => {
console.error(err)
console.log('erreur T_T')
})
})
}
// }
} else {
console.error(`pas de token pour l'auteur "${config.author}" ou pas d'instance mastodon définie`)
}
}
}
// Slugify a string
export function slugify(str) {
str = str.replace(/^\s+|\s+$/g, '')
// Make the string lowercase
str = str.toLowerCase()
// Remove accents, swap ñ for n, etc
var from = 'ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;'
var to = 'AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------'
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
}
// Remove invalid chars
str = str.replace(/[^a-z0-9 -]/g, '')
// Collapse whitespace and replace by -
.replace(/\s+/g, '-')
// Collapse dashes
.replace(/-+/g, '-')
return str
}
/**
* @name listFilesOfFolder
* lister les noms de fichier que l'on peut publier dans un dossier.
* retourne un tableau
*/
export function listFilesOfFolder(folderPath) {
let filesNames = []
fs.readdirSync(folderPath).map(fileName => {
return filesNames.push(fileName)
})
return filesNames
}
/**
* @name initializeFolderForPictures
* 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.
*/
export function initializeFolderForPictures(folderName) {
try {
if (!fs.existsSync(folderName)) {
fs.mkdirSync(folderName)
}
} catch (err) {
console.error(err)
}
}
/**
* find first image in blog post and return the src value of the img tag
* @param htmlContent
* @returns {string}
*/
export function findFirstImageInContent(htmlContent = '') {
let result = ''
let foundPictures = htmlContent.match(/]*?src\s*=\s*['\"]([^'\"]*?)['\"][^>]*?>/)
let first = ''
if (foundPictures && foundPictures[0]) {
first = foundPictures[0]
} else {
console.log('pas d image trouvée dans le contenu ', htmlContent)
}
if (first) {
result = first.match(/src\=\"(.*)\"/i)
if (result.length && result[0]) {
result = result[0].split('"')
result = result[1]
}
}
result = clearLink(result)
console.log('clearLink', result)
return result
}
function clearLink(linkString) {
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://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')
return linkString
}
/**
* usage:
* downloadImage('https://upload.wikimedia.org/wikipedia/en/thumb/7/7d/Lenna_%28test_image%29.png/440px-Lenna_%28test_image%29.png', 'lena.png')
* .then(console.log)
* .catch(console.error);
* @param url
* @param filepath
* @returns {Promise}
*/
export function downloadImage(url, filepath) {
return new Promise((resolve, reject) => {
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'}
}
https.get(url, options, (res) => {
if (res.statusCode === 200) {
res.pipe(fs.createWriteStream(filepath))
.on('error', reject)
.once('close', () => resolve(filepath))
} else {
// Consume response data to free up memory
res.resume()
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}; \n ${res.statusMessage} `))
}
})
})
}
/**
* faire un
* @param pictureName
* @param width
* @param height
* @returns {Promise