<commit_before>const dbOps = require('../util/dbOps.js')
const config = require('../config.json')
const MenuUtils = require('../structs/MenuUtils.js')
const FeedSelector = require('../structs/FeedSelector.js')
const log = require('../util/logger.js')
const URLS = ['{link}', '{guid}', '{reddit_direct}', '{reddit_author}']
const EMBED_PROPERTIES = {
  color: { name: 'Color', description: 'Sidebar color\nMUST be an integer color between 0 and 16777215. See https://www.shodor.org/stella2java/rgbint.html' },
  authorTitle: { name: 'Author Title', description: 'Title at the top\nAccepts placeholders' },
  authorURL: { name: 'Author URL', description: 'Clicking on the Author Title will lead to this URL\nMUST be a link' },
  authorAvatarURL: { name: 'Author Avatar URL', description: 'Avatar icon to the left of Author Title\nMUST be a link to an image. If Author Title is unspecified, the Author Avatar will be hidden' },
  title: { name: 'Title', description: 'Title under Author Title\nAccepts placeholders' },
  thumbnailURL: { name: 'Thumbnail Image URL', description: 'Image on the right side\nMUST be a link to an image, OR an {imageX} placeholder' },
  url: { name: 'URL', description: 'Clicking on the Title/Thumbnail will lead to this URL\nMUST be a link. Set to the article\'s url by default' },
  imageURL: { name: 'Image URL', description: 'Image on the bottom\nMUST be a link to an image, OR an {imageX} placeholder' },
  message: { name: 'Message', description: 'Main message\nAccepts placeholders' },
  footerText: { name: 'Footer Text', description: 'Bottom-most text\nAccepts placeholders' },
  footerIconURL: { name: 'Footer Icon URL', description: 'Icon to the left of Footer Text\nMUST be a link to an image. If Footer Text is unspecified, the Footer Icon will be hidden\nAccepts placeholders' },
  timestamp: { name: 'Timestamp', description: 'Date that is visually localized to every user\nIf an invalid timestamp date is detected, timestamp will not be shown' }
}
const EMBED_PROPERTIES_KEYS = Object.keys(EMBED_PROPERTIES)
const EMBED_PROPERTIES_KEYS_LEN = EMBED_PROPERTIES_KEYS.length

let EMBED_PROPERTIES_LIST = '```Markdown\n# Available Properties #\n\n'
for (var pn in EMBED_PROPERTIES) {
  const cur = EMBED_PROPERTIES[pn]
  EMBED_PROPERTIES_LIST += `[${cur.name}]: ${cur.description}\n\n${pn === EMBED_PROPERTIES_KEYS[EMBED_PROPERTIES_KEYS_LEN - 1] ? '```' : ''}`
}

function validURL (input) { // A simple check is enough
  return input.startsWith('http://') || input.startsWith('https://') || URLS.includes(input) || validImg(input)
}

function validate (prop, setting) {
  const lprop = prop.toLowerCase()
  switch (lprop) {
    case 'color':
      return isNaN(parseInt(setting, 10)) ? 'The color must be an **number**. See <https://www.shodor.org/stella2java/rgbint.html>. Try again, or type `exit` to cancel.' : parseInt(setting, 10) < 0 || parseInt(setting, 10) > 16777215 ? 'The color must be a number between 0 and 16777215. Try again, or type `exit` to cancel.' : true
    case 'thumbnailURL':
    case 'authorAvatarURL':
    case 'imageURL':
    case 'footerIconURL':
      return validImg(setting) ? true : 'URLs must link to actual images or be `{imageX}` placeholders. Try again, or type `exit` to cancel.'
    case 'authorURL':
    case 'url':
      return validURL(setting) ? true : 'URLs must be links or the {link} placeholder. Try again, or type `exit` to cancel.'
  }
  return true
}

// Check valid image URLs via extensions
function validImg (input) {
  if (input.startsWith('http')) {
    const matches = input.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i)
    return !!matches
  } else if (input.startsWith('{')) {
    const results = input.startsWith('{image') ? input.search(/^{image[1-9](\|\|(.+))*}$/) : input.search(/^{(description|image|title):image[1-5](\|\|(.+))*}$/)
    if (results === -1) return false
    const arr = input.split('||')
    if (arr.length === 1) return true
    let valid = true
    for (var x = 0; x < arr.length; ++x) {
      if (!valid) continue
      const term = x === 0 ? `${arr[x]}}` : x === arr.length - 1 ? `{${arr[x]}` : `{${arr[x]}}`
      if (!validImg(term)) valid = false
    }
    return valid
  } else return false
}

function feedSelectorFn (m, data, callback) {
  const { guildRss, rssName } = data
  const source = guildRss.sources[rssName]

  let currentEmbedProps = '```Markdown\n# Current Properties #\n\n'
  let changed = false
  if (source.embedMessage && source.embedMessage.properties) {
    const propertyList = source.embedMessage.properties
    for (var property in propertyList) {
      for (var p in EMBED_PROPERTIES) {
        if (p === property && propertyList[property]) {
          currentEmbedProps += `[${EMBED_PROPERTIES[p].name}]: ${propertyList[property]}\n\n`
          changed = true
        }
      }
    }
  }

  if (!changed) currentEmbedProps = '```\nNo properties set.\n'
  const m1 = `The current embed properties for ${source.link} are: \n${currentEmbedProps + '```'}\n`
  const m2 = `The list of embed properties that can be set are:\n${EMBED_PROPERTIES_LIST}\n**Type the embed property (shown in brackets [property]) you want to set/reset, or multiple properties by separation with commas.** Type \`reset\` to remove all properties, or type \`exit\` to cancel.`
  let mFull
  mFull = (m1 + m2).length < 1995 ? `${m1}\n${m2}` : [m1, m2] // Separate into two messages if it exceeds Discord's max length of 2000
  callback(null, { ...data,
    next: {
      text: mFull,
      embed: null }
  })
}<commit_msg>Adjusted rssembed prompt to be more clear<commit_after>const dbOps = require('../util/dbOps.js')
const config = require('../config.json')
const MenuUtils = require('../structs/MenuUtils.js')
const FeedSelector = require('../structs/FeedSelector.js')
const log = require('../util/logger.js')
const URLS = ['{link}', '{guid}', '{reddit_direct}', '{reddit_author}']
const EMBED_PROPERTIES = {
  color: { name: 'Color', description: 'Sidebar color\nMUST be an integer color between 0 and 16777215. See https://www.shodor.org/stella2java/rgbint.html' },
  authorTitle: { name: 'Author Title', description: 'Title at the top\nAccepts placeholders' },
  authorURL: { name: 'Author URL', description: 'Clicking on the Author Title will lead to this URL\nMUST be a link' },
  authorAvatarURL: { name: 'Author Avatar URL', description: 'Avatar icon to the left of Author Title\nMUST be a link to an image. If Author Title is unspecified, the Author Avatar will be hidden' },
  title: { name: 'Title', description: 'Title under Author Title\nAccepts placeholders' },
  thumbnailURL: { name: 'Thumbnail Image URL', description: 'Image on the right side\nMUST be a link to an image, OR an {imageX} placeholder' },
  url: { name: 'URL', description: 'Clicking on the Title/Thumbnail will lead to this URL\nMUST be a link. Set to the article\'s url by default' },
  imageURL: { name: 'Image URL', description: 'Image on the bottom\nMUST be a link to an image, OR an {imageX} placeholder' },
  message: { name: 'Message', description: 'Main message\nAccepts placeholders' },
  footerText: { name: 'Footer Text', description: 'Bottom-most text\nAccepts placeholders' },
  footerIconURL: { name: 'Footer Icon URL', description: 'Icon to the left of Footer Text\nMUST be a link to an image. If Footer Text is unspecified, the Footer Icon will be hidden\nAccepts placeholders' },
  timestamp: { name: 'Timestamp', description: 'Date that is visually localized to every user\nIf an invalid timestamp date is detected, timestamp will not be shown' }
}
const EMBED_PROPERTIES_KEYS = Object.keys(EMBED_PROPERTIES)
const EMBED_PROPERTIES_KEYS_LEN = EMBED_PROPERTIES_KEYS.length

let EMBED_PROPERTIES_LIST = '```Markdown\n# Available Properties #\n\n'
for (var pn in EMBED_PROPERTIES) {
  const cur = EMBED_PROPERTIES[pn]
  EMBED_PROPERTIES_LIST += `[${cur.name}]: ${cur.description}\n\n${pn === EMBED_PROPERTIES_KEYS[EMBED_PROPERTIES_KEYS_LEN - 1] ? '```' : ''}`
}

function validURL (input) { // A simple check is enough
  return input.startsWith('http://') || input.startsWith('https://') || URLS.includes(input) || validImg(input)
}

function validate (prop, setting) {
  const lprop = prop.toLowerCase()
  switch (lprop) {
    case 'color':
      return isNaN(parseInt(setting, 10)) ? 'The color must be an **number**. See <https://www.shodor.org/stella2java/rgbint.html>. Try again, or type `exit` to cancel.' : parseInt(setting, 10) < 0 || parseInt(setting, 10) > 16777215 ? 'The color must be a number between 0 and 16777215. Try again, or type `exit` to cancel.' : true
    case 'thumbnailURL':
    case 'authorAvatarURL':
    case 'imageURL':
    case 'footerIconURL':
      return validImg(setting) ? true : 'URLs must link to actual images or be `{imageX}` placeholders. Try again, or type `exit` to cancel.'
    case 'authorURL':
    case 'url':
      return validURL(setting) ? true : 'URLs must be links or the {link} placeholder. Try again, or type `exit` to cancel.'
  }
  return true
}

// Check valid image URLs via extensions
function validImg (input) {
  if (input.startsWith('http')) {
    const matches = input.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i)
    return !!matches
  } else if (input.startsWith('{')) {
    const results = input.startsWith('{image') ? input.search(/^{image[1-9](\|\|(.+))*}$/) : input.search(/^{(description|image|title):image[1-5](\|\|(.+))*}$/)
    if (results === -1) return false
    const arr = input.split('||')
    if (arr.length === 1) return true
    let valid = true
    for (var x = 0; x < arr.length; ++x) {
      if (!valid) continue
      const term = x === 0 ? `${arr[x]}}` : x === arr.length - 1 ? `{${arr[x]}` : `{${arr[x]}}`
      if (!validImg(term)) valid = false
    }
    return valid
  } else return false
}

function feedSelectorFn (m, data, callback) {
  const { guildRss, rssName } = data
  const source = guildRss.sources[rssName]

  let currentEmbedProps = '```Markdown\n# Current Properties #\n\n'
  let changed = false
  if (source.embedMessage && source.embedMessage.properties) {
    const propertyList = source.embedMessage.properties
    for (var property in propertyList) {
      for (var p in EMBED_PROPERTIES) {
        if (p === property && propertyList[property]) {
          currentEmbedProps += `[${EMBED_PROPERTIES[p].name}]: ${propertyList[property]}\n\n`
          changed = true
        }
      }
    }
  }

  if (!changed) currentEmbedProps = '```\nNo properties set.\n'
  const m1 = `The current embed properties for ${source.link} are: \n${currentEmbedProps + '```'}\n`
  const m2 = `The list of embed properties that can be set are:\n${EMBED_PROPERTIES_LIST}\nType the embed property (for example, \`color\` or \`message\`) you want to set/reset, or multiple properties by separation with commas (for example, \`color, message\`). Type \`reset\` to remove all properties, or type \`exit\` to cancel.`
  let mFull
  mFull = (m1 + m2).length < 1995 ? `${m1}\n${m2}` : [m1, m2] // Separate into two messages if it exceeds Discord's max length of 2000
  callback(null, { ...data,
    next: {
      text: mFull,
      embed: null }
  })
}
{'fraction_non_alphanumeric': 0.14585764294049008, 'fraction_numerical': 0.010950543039224487, 'mean_word_length': 3.9257294429708223, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 7, 'https://': 6, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '72472', 'n_tokens_mistral': 3693, 'n_tokens_neox': 3570, 'n_words': 1177}