├── README.md ├── index.html ├── shadowbanned.css ├── UNLICENSE └── shadowbanned.js /README.md: -------------------------------------------------------------------------------- 1 | # Reddit shadowban test tool 2 | 3 | Test yourself: https://skeeto.github.io/am-i-shadowbanned/ 4 | 5 | ## What's a shadowban? 6 | 7 | When a user is shadowbanned they can continue to make posts and leave 8 | comments without any indication of the ban. However, *everything* they 9 | do is automatically marked as spam. No one will see any of it until 10 | the post or comment is graciously freed by a moderator, if ever. To 11 | the moderator, the post title appears will appear as strikethrough in 12 | addition to the normal red background. 13 | 14 | This tool can test whether an account is shadowbanned, normal, or 15 | non-existent. It uses [CORS Anywhere][cors] to work around the 16 | cross-domain issue. 17 | 18 | [cors]: https://cors-anywhere.herokuapp.com/ 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Am I shadowbanned? 5 | 6 | 7 | 8 | 9 |

Am I shadowbanned?

10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 | Fork me on GitHub 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /shadowbanned.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | text-align: center; 4 | background: #fef; 5 | } 6 | 7 | h1>a{ 8 | text-decoration: none; 9 | } 10 | 11 | #user { 12 | border: 1px solid black; 13 | border-radius: 10px; 14 | padding: 5px; 15 | font-size: x-large; 16 | text-align: center; 17 | } 18 | 19 | input[type="submit"] { 20 | height: 40px; 21 | vertical-align: top; 22 | border-radius: 10px; 23 | } 24 | 25 | .output { 26 | background: lightblue; 27 | padding: 15px; 28 | border-radius: 25px; 29 | width: 650px; 30 | text-align: center; 31 | font-weight: bold; 32 | font-size: x-large; 33 | margin: auto; 34 | margin-top: 20px; 35 | box-shadow: 0px 0px 3px 3px gray; 36 | } 37 | 38 | 39 | @media only screen and (max-width: 630px) { 40 | .output{ 41 | max-width:350px; 42 | margin: auto; 43 | } 44 | #user{ 45 | margin-bottom: 1em; 46 | } 47 | h1{ 48 | margin-top: 2em; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /shadowbanned.js: -------------------------------------------------------------------------------- 1 | /** 2 | * How does it work? For both deleted and shadowbanned users 3 | * /user/[user]/about.json returns a 404 error but 4 | * /api/username_available.json returns false. Non-existent users show 5 | * up as available and normal users don't return an error for 6 | * about.json. 7 | * @param {String} user the user to test 8 | * @param {Function} callback to receive the result 9 | */ 10 | function Checker(user, callback) { 11 | this.user = user; 12 | this.callback = callback; 13 | var _this = this; 14 | this.isVisible(function(visible) { 15 | if (visible) { 16 | callback(user + ' looks normal'); 17 | } else { 18 | _this.isAvailable(function(available) { 19 | if (available) { 20 | callback(user + ' does not exist'); 21 | } else { 22 | callback(user + ' is shadowbanned or deleted'); 23 | } 24 | }); 25 | } 26 | }); 27 | } 28 | /** 29 | * Fetch a URL through a CORS proxy. 30 | * @param {String} url 31 | * @param {Function} callback is passed the result 32 | * @see https://cors-anywhere.herokuapp.com/ 33 | */ 34 | Checker.prototype.fetch = function(url, callback) { 35 | var async = callback != null; 36 | var xhr = new XMLHttpRequest(); 37 | if (async) { 38 | xhr.onload = function() { 39 | callback(xhr.responseText, xhr); 40 | }; 41 | } 42 | xhr.open('GET', 'https://cors-anywhere.herokuapp.com/' + url, async); 43 | xhr.send(); 44 | return async ? xhr : xhr.responseText; 45 | }; 46 | 47 | /** 48 | * @param {Function} callback called with true if user doesn't exist 49 | */ 50 | Checker.prototype.isAvailable = function(callback) { 51 | var api = "https://www.reddit.com/api/username_available.json?user="; 52 | this.fetch(api + this.user, function(result) { 53 | callback(JSON.parse(result)); 54 | }); 55 | }; 56 | 57 | /** 58 | * @param {Function} callback called with true if user is visible 59 | */ 60 | Checker.prototype.isVisible = function(callback) { 61 | var about = 'https://www.reddit.com/user/' + this.user + '/about.json'; 62 | this.fetch(about, function(result) { 63 | callback(JSON.parse(result).error == null); 64 | }); 65 | }; 66 | 67 | function Output(node) { 68 | this.node = node; 69 | } 70 | 71 | Output.prototype.clear = function() { 72 | this.node.innerHTML = ''; 73 | }; 74 | 75 | Output.prototype.print = function(html) { 76 | this.node.textContent = html; 77 | }; 78 | 79 | window.addEventListener('load', function() { 80 | var check = document.getElementById('check'); 81 | var user = document.getElementById('user'); 82 | var output = new Output(document.getElementById('output')); 83 | 84 | function submit(event) { 85 | if (event) event.preventDefault(); 86 | location.hash = user.value; 87 | output.print('Checking ' + user.value + ' ...'); 88 | new Checker(user.value, function(result) { 89 | output.print(result); 90 | }); 91 | user.blur(); 92 | } 93 | 94 | check.addEventListener('submit', submit); 95 | 96 | user.addEventListener('input', function() { 97 | output.clear(); 98 | }); 99 | 100 | if (location.hash != '') { 101 | user.value = location.hash.slice(1); 102 | submit(); 103 | } 104 | }); 105 | --------------------------------------------------------------------------------