DarkSmile92 / devtoapiparser

DEV.to api parser for article moderation
11 stars 1 forks source link

[BUG] Found the following bad words but not show the word #4

Closed thomasbnt closed 2 years ago

thomasbnt commented 4 years ago

Expected behaviour

When I modify tags and words, the script start and show me post by post with

Found the following bad words:

But no bad words after that.

Actual behaviour

Found the following bad words:

But no bad words after that.

OS versions and console emulator

I'm not a dummy, so I've checked these

So how can we reproduce this?

Awesome :star::star::star::star::star:

Provide Screenshots, tell us the parameters you used (please exclude the API KEY!) and paste the console output.

Console output :

---- :: ---- :: ----
Found the following bad words: Rates, 

Post (319698) "Managing different SSH keys for GitLab and GitHub." from "Hridayesh Sharma" (_hridaysharma)
https://dev.to/_hridaysharma/managing-different-ssh-keys-for-gitlab-and-github-4k5g
---- :: ---- :: ----
Found the following bad words: 

Post (320567) "How to optimize a picture with no quality loss? 🤔" from "Eldar Dautović" (eldardautovic)
https://dev.to/eldardautovic/how-to-optimize-a-picture-with-no-quality-loss-cp9
---- :: ---- :: ----
Article content too short: 239 of required 275 chars. Should be checked.
---- :: ---- :: ----
Found the following bad words: 
Ad, 

Post (320532) "How to create a custom toast component with React" from "Uzochukwu Eddie Odozi" (uzochukwueddie)
https://dev.to/uzochukwueddie/how-to-create-a-custom-toast-component-with-react-21pl
---- :: ---- :: ----
Found the following bad words: 

Post (320564) "Scheduling JAMstack builds in Netlify with Github actions" from "Liam Hall" (wearethreebears)
https://dev.to/wearethreebears/scheduling-jamstack-builds-in-netlify-with-github-actions-1opj
---- :: ---- :: ----
Found the following bad words: 

Post (320584) "My Portfolio 👨🏼‍💻" from "Berat Bozkurt" (beratbozkurt0)
https://dev.to/beratbozkurt0/my-portfolio-3am8
---- :: ---- :: ----
Found the following bad words: 

Post (319771) "3 trends that I like in web design" from "wolfiton" (wolfiton)
https://dev.to/wolfiton/3-trends-that-i-like-in-web-design-721
---- :: ---- :: ----
Article content too short: 144 of required 275 chars. Should be checked.
---- :: ---- :: ----
Found the following bad words: 

Post (320606) "doWhile vs While" from "Sam" (samuelcrudge)
https://dev.to/samuelcrudge/dowhile-vs-while-4dpf
---- :: ---- :: ----
Found the following bad words: read more, 

Post (319565) "Build an Accessible React Component: Part 1 - Breadcrumbs" from "Ashlee Boyer" (ashleemboyer)
https://dev.to/ashleemboyer/build-an-accessible-react-component-part-1-breadcrumbs-259o
---- :: ---- :: ----
Found the following bad words: 

Post (320533) "Part 3 : Code Example for Formation & Destruction of Execution Stack" from "Ujala Khurram" (ujalak1812)
https://dev.to/ujalak1812/part-3-code-example-for-formation-destruction-of-execution-stack-4ll7
---- :: ---- :: ----
Found the following bad words: 

Post (319441) "Dependency Management With Go Modules" from "Pascal Ulor" (pc_codes)
https://dev.to/pc_codes/dependency-management-with-go-modules-434c
---- :: ---- :: ----
Found the following bad words: 

Post (320057) "Understanding JavaScript Scope" from "Shaon Kabir" (shaonkabir8)
https://dev.to/shaonkabir8/understanding-javascript-scope-522h
---- :: ---- :: ----
Found the following bad words: 

Post (272062) "Navigating Dev.to's Code on GitHub" from "Andrew Lee" (andyrewlee)
https://dev.to/andyrewlee/navigating-dev-to-s-code-on-github-4km4
---- :: ---- :: ----
Found the following bad words: 

Post (319685) "Betterweb List's Weekly Roundup - Week #1" from "Victor Ofoegbu" (vick_onrails)
https://dev.to/vick_onrails/betterweb-list-s-weekly-roundup-week-1-3oij
---- :: ---- :: ----
Found the following bad words: 

Post (320487) "Web Dev Week 1: HTML Basics " from "Regina Miller" (rrmiller7755)
https://dev.to/rrmiller7755/web-dev-week-1-html-basics-3b1b
---- :: ---- :: ----
Found the following bad words: 

Post (320288) "First Profile Site" from "Aaron McCollum" (aaronmccollum)
https://dev.to/aaronmccollum/first-profile-site-54m4
Got 30 articles for tag html
---- :: ---- :: ----
Found the following bad words: 

Post (320210) "Iframes and communicating between applications" from "Damien Cosset" (damcosset)
https://dev.to/damcosset/iframes-and-communicating-between-applications-31k5
---- :: ---- :: ----
Found the following bad words: 

Post (320034) "Instagram Login page 📸🖼️" from "Atul Prajapati" (atulcodex)
https://dev.to/atulcodex/instagram-login-page-4e39
---- :: ---- :: ----
Found the following bad words: 

Post (320528) "Simple Cookie Consent Using Bootstrap 4" from "w3hubs.com" (w3hubs)
https://dev.to/w3hubs/simple-cookie-consent-using-bootstrap-4-1gaj
---- :: ---- :: ----
Found the following bad words: 

Post (309880) "How to kern lettering with SVG's dx attribute." from "James Grubb" (makingthings)
https://dev.to/makingthings/how-to-kern-lettering-with-svg-s-dx-attribute-3po9
---- :: ---- :: ----
Found the following bad words: 

Post (320525) "Mithril with HTM" from "artydev" (artydev)
https://dev.to/artydev/mithril-with-htm-28p8
---- :: ---- :: ----
Article content too short: 207 of required 275 chars. Should be checked.
---- :: ---- :: ----
Found the following bad words: 

Post (320443) "The Tribute" from "Gaurav Singh" (iamgs)
https://dev.to/iamgs/the-tribute-4dpk
---- :: ---- :: ----
Found the following bad words: 

Post (320487) "Web Dev Week 1: HTML Basics " from "Regina Miller" (rrmiller7755)
https://dev.to/rrmiller7755/web-dev-week-1-html-basics-3b1b
events.js:292
      throw er; // Unhandled 'error' event
      ^

[Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: CheckedArticles.article_id
Emitted 'error' event on Statement instance at:
] {
  errno: 19,
  code: 'SQLITE_CONSTRAINT'
}

Edited : index.js (renommed to app.js) :

const VERSION = "1.0.2"
const axios = require("axios")
const chalk = require("chalk")
const log = console.log
const fs = require("fs")
const sqlite3 = require("sqlite3").verbose()
// To test, use: const db = new sqlite3.Database(":memory:")
const db = new sqlite3.Database("checkedData.db")

// PERSONAL CONFIG
const APIKEY = "my_beautiful_token_here"
const MY_TAGS = ["webdev", "html", "css", "tooling", "opensource", "raspberry", "express"]
const ONLY_MY_TAGS = false
const SITES_TO_CHECK = 10
/*
state parameter combinations:
state=fresh => checking newly published posts
state=all & username=ben => must be used with username (also 1000 articles will be returned instead of the default 30)
state=rising => will return published rising articles
*/
const CHECK_STATE = "fresh"

// BASE CONFIG
const BASEURL = "https://dev.to/api"
const BADPHRASES = [
  "read more",
  "Call free/now",
  "Additional income",
  "$$$",
  "Casino",
  "Clearance",
  "Cheap",
  "Credit card",
  "Freedom",
  "Double your",
  "Essay",
  "Luxury",
  "Investment",
  "Obligation",
  "Presently",
  "Promotion",
  "Increase sales",
  "Lowest price",
  "credit check",
  "Order now",
  "Refund",
  "Refinance",
  "Rates",
  "supplies",
  "Risk-free",
  "Take action",
  "Bargain",
  "Escort",
  "Hooker",
  "Prostitute",
  "Asshole",
  "Fuck",
  "Abuse",
  "Shop",
  "Needy",
  "Plumber",
  "Sale",
  "Escort"
]
let ADD_BAD_WORDS = []
// Load additional bad words
fs.readFile("./badstrings.txt", "utf8", function(err, data) {
  if (err) throw err
  if (data) {
    ADD_BAD_WORDS = [...data.split(",")]
  }
  //console.log(data)
})
let CONSOLE_NEED_SPLIT = false
const error = chalk.bold.red
const warning = chalk.keyword("orange")

const printSplit = () => {
  log(chalk.green.bold("---- :: ---- :: ----"))
}

const countWords = s => {
  s = s.replace(/(^\s*)|(\s*$)/gi, "") //exclude  start and end white-space
  s = s.replace(/[ ]{2,}/gi, " ") //2 or more space to 1
  s = s.replace(/\n /, "\n") // exclude newline with a start spacing
  return s.split(" ").filter(function(str) {
    return str != ""
  }).length
  //return s.split(' ').filter(String).length - this can also be used
}

const countLinks = content => {
  const matches = content.match(/href/gi)
  return matches ? matches.length : 0
}

const getArticlesPaged = async page => {
  try {
    return await axios({
      method: "get",
      url: `${BASEURL}/articles?page=${page}`,
      headers: { "Content-Type": "application/json", "api-key": APIKEY }
    })
  } catch (error) {
    log(error(error))
  }
}

const getFreshArticlesByTag = async tag => {
  try {
    return await axios({
      method: "get",
      url: `${BASEURL}/articles?state=fresh&tag=${tag}`,
      headers: { "Content-Type": "application/json", "api-key": APIKEY }
    })
  } catch (error) {
    log(error(error))
  }
}

const checkMyArticles = async (
  pagesToCheck,
  checkedArticleIds,
  db_insert_statement
) => {
  log(
    `Checking ${
      ONLY_MY_TAGS ? "only my tags" : "all articles"
    } on the latest ${pagesToCheck} pages.`
  )
  for (let page = 0; page < pagesToCheck; page++) {
    const articles = await getArticlesPaged(page + 1)
    if (articles.data) {
      log(
        `Got ${Object.entries(articles.data).length} articles at page ${page +
          1}`
      )
      // loop articles and check them
      for (const article of articles.data) {
        await checkArticle(article, checkedArticleIds, db_insert_statement)
      }
    }
  }
  db_insert_statement.finalize()
}

const checkLatest = async (checkedArticleIds, db_insert_statement) => {
  log(`Checking only my tags on the latest page.`)
  // check if my tag is affected
  for (const tagname of MY_TAGS) {
    const articlesForTag = await getFreshArticlesByTag(tagname)
    if (articlesForTag.data) {
      log(
        `Got ${
          Object.entries(articlesForTag.data).length
        } articles for tag ${tagname}`
      )
      // loop articles and check them
      for (const article of articlesForTag.data) {
        await checkArticle(article, checkedArticleIds, db_insert_statement)
      }
    }
  }
  db_insert_statement.finalize()
}

const getArticleDetails = async articleId => {
  try {
    return await axios({
      method: "get",
      url: `${BASEURL}/articles/${articleId}`,
      headers: { "Content-Type": "application/json", "api-key": APIKEY }
    });
  } catch (error) {
    // console.error(error)
    log(error(error.response.status))
    return null
  }
}

const checkContentLength = content => {
  const MIN_CHARS = 275
  if (!content || content.length <= MIN_CHARS) {
    if (CONSOLE_NEED_SPLIT) {
      printSplit()
      CONSOLE_NEED_SPLIT = false
    }
    log(
      warning(
        `Article content too short: ${
          content ? content.length : 0
        } of required ${MIN_CHARS} chars. Should be checked.`
      )
    )
    return true
  }
  return false
}

const checkLinkWordRatio = content => {
  const MAX_LINK_PCT = 20
  const numWords = countWords(content)
  const numLinks = countLinks(content)

  const linkToWordRatio = (numLinks * 100) / numWords

  if (linkToWordRatio >= MAX_LINK_PCT) {
    if (CONSOLE_NEED_SPLIT) {
      printSplit()
      CONSOLE_NEED_SPLIT = false
    }
    log(
      warning(
        `Link to word ratio too high: ${linkToWordRatio}/${MAX_LINK_PCT}. Words: ${numWords} Links: ${numLinks}`
      )
    )
    return true
  }
  return false
}

const checkBadWords = content => {
  const lwrContent = content.toLowerCase()
  let foundWords = []
  for (const badword of BADPHRASES) {
    if (badword.length > 0 && lwrContent.indexOf(badword.toLowerCase()) >= 0) {
      // console.log(`BW: ${badword}`)
      foundWords.push(badword)
    }
  }
  for (const badword of ADD_BAD_WORDS) {
    if (badword.length > 0 && lwrContent.indexOf(badword.toLowerCase()) >= 0) {
      // console.log(`BW: ${badword}`)
      foundWords.push(badword)
    }
  }
  if (foundWords && foundWords.length > 0) {
    if (CONSOLE_NEED_SPLIT) {
      printSplit()
      CONSOLE_NEED_SPLIT = true
    }
    log(warning(`Found the following bad words: ${foundWords.join(", ")}`))
    return true
  }
  return false
}

const checkStrangeTags = taglist => {
  const CHAR_THRESHOLD = 17
  let foundStrangeTags = false

  for (const tag of taglist) {
    if (tag.trim().length >= CHAR_THRESHOLD || tag.trim().indexOf(" ") >= 0) {
      log(warning(`The Tag ${tag.trim()} looks strange, please check it.`))
      foundStrangeTags = true
    }
  }
  return foundStrangeTags
}

const processChecks = async articleItem => {
  const articleDetails = await getArticleDetails(articleItem.id)
  if (!articleDetails) return false
  const details = articleDetails.data
  let mustCheck = false

  if (details.body_html) {
    if (checkContentLength(details.body_html)) {
      mustCheck = true
    }
    if (checkLinkWordRatio(details.body_html)) {
      mustCheck = true
    }
    if (checkBadWords(details.body_html)) {
      mustCheck = true
    }
    if (checkStrangeTags(details.tag_list.split(","))) {
      mustCheck = true
    }
  } else {
    if (CONSOLE_NEED_SPLIT) {
      printSplit()
      CONSOLE_NEED_SPLIT = false
    }
    log(error(`Could not get content of article ${articleItem.id}`))
  }
  return mustCheck
}

const checkArticle = async (
  article,
  checkedArticleIds,
  db_insert_statement
) => {
  const article_id = article.id
  const article_title = article.title
  const article_description = article.description
  const article_published_at = article.published_at
  const article_tags = article.tag_list
  const article_slug = article.slug
  const article_path = article.path
  const article_url = article.url
  const article_canonical_url = article.canonical_url
  const article_comments_count = article.comments_count
  const article_reactions_count = article.positive_reactions_count
  const article_published_timestamp = article.published_timestamp
  const article_user = article.user // { name, username, twitter_username, github_username, website_url, profile_image, profile_image_90 }

  CONSOLE_NEED_SPLIT = false

  if (checkedArticleIds.indexOf(article_id) >= 0) {
    return
  }
  if (ONLY_MY_TAGS) {
    // check if my tag is affected
    for (const tagname of MY_TAGS) {
      if (article_tags.indexOf(tagname) >= 0) {
        const needAttention = await processChecks(article)
        if (needAttention) {
          log(
            `Post (${article_id}) "${article_title}" from "${article_user.name}" (${article_user.username}) needs moderation from you due to the tag #${tagname}`
          )
          log(article_url)
          // Save article to DB
          db_insert_statement.run(
            article_id,
            article_url,
            article_tags.join(", "),
            article_user.username
          )
        }
      }
    }
  } else {
    const needAttention = await processChecks(article)
    if (needAttention) {
      log(
        `Post (${article_id}) "${article_title}" from "${article_user.name}" (${article_user.username})`
      )
      log(article_url)
      // Save article to DB
      db_insert_statement.run(
        article_id,
        article_url,
        article_tags.join(", "),
        article_user.username
      )
    }
  }
}

const runWithDB = () => {
  // Setup database and run code in DB context
  db.serialize(function() {
    db.run(
      "CREATE TABLE IF NOT EXISTS CheckedArticles (article_id INTEGER PRIMARY KEY, article_url TEXT NOT NULL, article_tags TEXT NULL, article_author TEXT NOT NULL)"
    )

    const stmt = db.prepare("INSERT INTO CheckedArticles VALUES (?,?,?,?)")
    const checkedArticleIds = []
    // select already checked articles to not check them again
    db.each(
      "SELECT rowid AS id, article_id FROM CheckedArticles",
      function(err, row) {
        if (checkedArticleIds.indexOf(row.article_id) < 0) {
          checkedArticleIds.push(row.article_id)
        }
      },
      () => {
        if (checkedArticleIds.length > 0) {
          log(
            chalk.magenta.bold(
              `Skipping ${checkedArticleIds.length} already checked articles.`
            )
          )
        }

        //checkMyArticles(SITES_TO_CHECK, checkedArticleIds, stmt)
        checkLatest(checkedArticleIds, stmt)
      }
    )
    // finalize in check function because of async nature
    // stmt.finalize()

    // To test written data in DB:
    /*
    db.each(
      "SELECT rowid AS id, article_id, article_url FROM CheckedArticles",
      function(err, row) {
        console.log(`${row.id}: ${row.article_id} -> ${row.article_url}`)
      }
    )
    */
  })

  // db.close()
}

log(chalk.reset.cyan.bold(`DEV.to Moderator v${VERSION}`))

if (!APIKEY || APIKEY.length === 0 || APIKEY === "") {
  log(error("No APIKEY configured! Please provide one in the file!"))
} else {
  runWithDB()
}

Edited : badstrings.txt :

0%,
0% risk,
100%,
100% free,
100% more,
100% satisfied,
2100% satisfied,
2Drastically reduced,
2Full refund,
2It’s effective,
2University diplomas,
2Visit our website,
2Your income,
4U,
4u,
50% off,
777,
99%,
99.9%,
Accept credit cards,
Acceptance,
Access,
Access for free,
Access now,
Accordingly,
Act Now,
Act immediately,
Action,
Action required,
Ad,
Additional income,
Addresses on CD,
Affordable,
Affordable deal,
All natural,
All new,
Amazed,
Amazing offer,
Amazing stuff,
Apply Online,
Apply here,
Apply now,
As seen on,
At no cost,
Auto email removal,
Avoid,
Avoid bankruptcy,
Bargain,
Be amazed,
Be surprised,
Be your own boss,
Being a member,
Believe me,
Beneficiary,
Best bargain,
Best deal,
Best offer,
Best price,
Beverage,
Big bucks,
Bill 1618,
Billing,
Billing address,
Billion,
Billion dollars,
Billionaire,
Bonus,
Boss,
Brand new pager,
Bulk email,
Buy,
Buy direct,
Buy now,
Buying judgments,
Cable converter,
Call,
Call free,
Call me,
Call now,
Calling creditors,
Can’t live without,
Cancel,
Cancel at any time,
Cancel now,
Cancellation required,
Cannot be combined with any other offer,
Cards accepted,
Cash,
Cash bonus,
Cash out,
Cashcashcash,
Casino,
Celebrity,
Cell phone cancer scam,
Cents on the dollar,
Certified,
Chance,
Cheap,
Check,
Check or money order,
Claim now,
Claim your discount,
Claims,
Claims not to be selling anything,
Claims to be in accordance with some spam law,
Claims to be legal,
Clearance,
Click,
Click below,
Click here,
Click now,
Click to get,
Click to remove,
Collect,
Collect child support,
Compare,
Compare now,
Compare online,
Compare rates,
Compete for your business,
Confidentially on all orders,
Congratulations,
Consolidate debt and credit,
Consolidate your debt,
Copy DVDs,
Copy accurately,
Costs,
Credit,
Credit bureaus,
Credit card offers,
Cures,
Cures baldness,
Deal,
Dear [email/friend/somebody],
Debt,
Diagnostics,
Dig up dirt on friends,
Direct email,
Direct marketing,
Discount,
Do it now,
Do it today,
Don’t delete,
Don’t hesitate,
Dormant,
Double your,
Double your cash,
Double your income,
Double your wealth,
Earn $,
Earn extra cash,
Earn from home,
Earn money,
Earn monthly,
Earn per month,
Earn per week,
Easy terms,
Eliminate bad credit,
Eliminate debt,
Email extractor,
Email harvest,
Email marketing,
Exclusive deal,
Expect to earn,
Explode your business,
Extra cash,
Extra income,
Extract email,
F r e e,
Fantastic,
Fantastic deal,
Fantastic offer,
Fast Viagra delivery,
Fast cash,
Financial freedom,
Financially independent,
For Only,
For free,
For instant access,
For just $ (some amount),
For just $xxx,
For just,
For you,
Free DVD,
Free Macbook,
Free access,
Free bonus,
Free cell phone,
Free consultation,
Free gift,
Free grant money,
Free hosting,
Free info,
Free information,
Free installation,
Free instant,
Free investment,
Free leads,
Free membership,
Free money,
Free offer,
Free preview,
Free priority mail,
Free quote,
Free sample,
Free trial,
Free website,
Freedom,
Friend,
Get,
Get it now,
Get out of debt,
Get paid,
Get started now,
Gift certificate,
Give it away,
Giving away,
Great deal,
Great offer,
Guarantee,
Guaranteed,
Guaranteed deposit,
Guaranteed income,
Guaranteed payment,
Have you been turned down?,
Hidden assets,
Hidden charges,
Hidden fees,
High score,
Home based,
Home based business,
Home employment,
Huge discount,
Human growth hormone,
Hurry up,
If only it were that easy,
Important information regarding,
Important notification,
In accordance with laws,
Income,
Income from home,
Increase sales,
Increase traffic,
Increase your chances,
Increase your sales,
Incredible deal,
Info you requested,
Information you requested,
Instant earnings,
Instant income,
Instagram,
Insurance,
Internet market,
Internet marketing,
Investment,
Investment decision,
Join millions,
Join millions of Americans,
Junk,
Laser printer,
Legal,
Legal notice,
Life Insurance,
Lifetime,
Lifetime access,
Lifetime deal,
Limited amount,
Limited number,
Limited offer,
Limited supply,
Limited time,
Limited time offer,
Limited time only,
Loan,
Long distance phone offer,
Lose,
Lose weight,
Lose weight spam,
Lower interest rates,
Lower monthly payment,
Lower your mortgage rate,
Lowest insurance rates,
Lowest price,
Lowest rate,
Luxury,
Luxury car,
MLM,
Mail in order form,
Maintained,
Make $,
Make money,
Marketing,
Marketing solutions,
Mass email,
Medicine,
Medium,
Meet girls,
Meet me,
Meet singles,
Meet women,
Member,
Member stuff,
Message contains,
Message contains disclaimer,
Million,
Million dollars,
Millionaire,
Miracle,
Money,
Money back,
Money making,
Month trial offer,
More Internet Traffic,
Mortgage,
Mortgage rates,
Multi-level marketing,
Name brand,
New customers only,
New domain extensions,
Nigerian,
No age restrictions,
No catch,
No claim forms,
No cost,
No credit check,
No deposit required,
No disappointment,
No experience,
No fees,
No gimmick,
No hidden,
No hidden сosts,
No hidden fees,
No interests,
No inventory,
No investment,
No investment required,
No middleman,
No obligation,
No payment required,
No purchase necessary,
No questions asked,
No selling,
No strings attached,
No-obligation,
Not intended,
Not junk,
Not scam,
Not spam,
Now only,
Number 1,
Number one,
Obligation,
Offer,
Offer expires,
Offshore,
Once in a lifetime,
Once in lifetime,
One hundred percent free,
One hundred percent guaranteed,
One time,
One time mailing,
Online biz opportunity,
Online degree,
Online income,
Online job,
Online marketing,
Online pharmacy,
Only $,
Opportunity,
Opt in,
Order,
Order now,
Order shipped by,
Order status,
Order today,
Outstanding values,
Passwords,
Pennies a day,
Per day,
Per month,
Per week,
Performance,
Phone,
Please read,
Potential earnings,
Pre-approved,
Presently,
Price,
Price protection,
Print form signature,
Print out and fax,
Priority mail,
Prize,
Problem,
Produced and sent out,
Profits,
Promise you,
Purchase,
Pure profits,
Quote,
Rates,
Real thing,
Refinance,
Refinance home,
Refund,
Removal,
Removal instructions,
Removes wrinkles,
Request now,
Request today,
Requires initial investment,
Reserves the right,
Reverses,
Reverses aging,
Risk free,
Risk-free,
Rolex,
Round the world,
S 1618,
Safeguard notice,
Sale,
Sample,
Satisfaction,
Satisfaction guaranteed,
Save $,
Save big money,
Save money,
Save now,
Save up to,
Score,
Score with babes,
Search engine listings,
Search engines,
Section 301,
See for yourself,
Sent in compliance,
Serious,
Serious cash,
Serious offer,
Serious only,
Shopper,
Shopping spree,
Sign up free today,
Social security number,
Solution,
Spam,
Special deal,
Special discount,
Special for you,
Special offer,
Special promotion,
Stainless steel,
Stock alert,
Stock disclaimer statement,
Stock pick,
Stop,
Stop calling me,
Stop emailing me,
Stop snoring,
Strong buy,
Stuff on sale,
Subject to cash,
Subject to credit,
Subscribe,
Subscribe for free,
Subscribe now,
Success,
Supplies,
Supplies are limited,
Take action,
Take action now,
Talks about hidden charges,
Talks about prizes,
Teen,
Tells you it’s an ad,
Terms,
Terms and conditions,
The best rates,
The following form,
They’re just giving it away,
They keep your money — no refund!,
This isn’t a scam,
This isn’t junk,
This isn’t spam,
This won’t last,
Thousands,
Time limited,
Traffic,
Trial,
US dollars,
Undisclosed recipient,
Unlimited,
Unsecured credit,
Unsecured debt,
Unsolicited,
Unsubscribe,
Urgent,
VIP,
Vacation,
Vacation offers,
Valium,
Viagra,
Vicodin,
Wants credit card,
Warranty,
Warranty expired,
We hate spam,
We honor all,
Web traffic,
Website visitors,
Weekend getaway,
Weight,
Weight loss,
What’s keeping you?,
What are you waiting for?,
While available,
While in stock,
While supplies last,
While you sleep,
Who really wins?,
Why pay more?,
Wife,
Will not believe your eyes,
Win,
Winner,
Winning,
Won,
Work from home,
Xanax,
You are a winner!,
You have been chosen,
You have been selected,
Your chance,
Your income,
Your status,
Zero chance,
Zero percent,
Zero risk,
DarkSmile92 commented 2 years ago

Hi @thomasbnt , sorry for not looking into the issues here for so long. As this was a quick draft, I did not account for a comma at the end of the list for badstrings.txt. If you remove the last comma, it will suppress the message if nothing is found. I will update this project and trim the whitespace to prevent it soon :)

DarkSmile92 commented 2 years ago

Fixed in https://github.com/DarkSmile92/devtoapiparser/commit/3ddf4d55be4199efaa94ca44061fc217cbe90ef1

thomasbnt commented 2 years ago

Thanks!