├── .prettierrc ├── .gitignore ├── src ├── index.js ├── main.css └── Main.elm ├── .dockerignore ├── backend ├── config.js ├── db.js ├── bot.js └── index.js ├── fly.toml ├── public └── index.html ├── elm.json ├── LICENSE ├── package.json ├── README.md └── dump └── results_ken_wheeler.json /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "avoid" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | build/ 5 | npm-debug.log* 6 | .env* 7 | *.swp 8 | .vscode/ 9 | site/ 10 | 11 | package-lock\.json 12 | 13 | # elm-package generated files 14 | elm-stuff 15 | 16 | # elm-repl generated files 17 | repl-temp-* 18 | 19 | dump\.rdb 20 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './main.css'; 2 | import { Elm } from './Main.elm'; 3 | 4 | Elm.Main.init({ 5 | node: document.getElementById('root'), 6 | flags: { 7 | apiBaseUrl: 8 | process.env.ELM_APP_API_URL || 9 | 'https://ken-wheeler-aka.herokuapp.com/api/aka/ken_wheeler' 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # flyctl launch added from .gitignore 2 | **/.DS_Store 3 | **/node_modules 4 | **/dist 5 | **/build 6 | **/npm-debug.log* 7 | **/.env* 8 | **/*.swp 9 | **/.vscode 10 | **/site 11 | 12 | **/package-lock\.json 13 | 14 | # elm-package generated files 15 | **/elm-stuff 16 | 17 | # elm-repl generated files 18 | **/repl-temp-* 19 | 20 | **/dump\.rdb 21 | fly.toml 22 | -------------------------------------------------------------------------------- /backend/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | module.exports = { 4 | twitterKeys: { 5 | consumer_key: process.env.TWITTER_CONSUMER_KEY, 6 | consumer_secret: process.env.TWITTER_CONSUMER_SECRET, 7 | access_token: process.env.TWITTER_ACCESS_TOKEN, 8 | access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET 9 | }, 10 | twitterConfig: { 11 | check: process.env.TWITTER_CHECK_RATE * 1000 * 60, 12 | enabled: process.env.TWEETING_ENABLED === 'true' || false 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml app configuration file generated for ken-wheeler-aka on 2023-06-20T01:08:58+03:00 2 | # 3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 | # 5 | 6 | app = "ken-wheeler-aka" 7 | primary_region = "arn" 8 | 9 | [build] 10 | builder = "heroku/buildpacks:20" 11 | 12 | 13 | [http_service] 14 | internal_port = 8080 15 | force_https = true 16 | auto_stop_machines = true 17 | auto_start_machines = true 18 | min_machines_running = 1 19 | 20 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Ken Wheeler's display names 12 | 13 | 14 | 15 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "elm/browser": "1.0.1", 10 | "elm/core": "1.0.0", 11 | "elm/html": "1.0.0", 12 | "elm/http": "1.0.0", 13 | "elm/json": "1.0.0", 14 | "elm-explorations/markdown": "1.0.0" 15 | }, 16 | "indirect": { 17 | "elm/time": "1.0.0", 18 | "elm/url": "1.0.0", 19 | "elm/virtual-dom": "1.0.2" 20 | } 21 | }, 22 | "test-dependencies": { 23 | "direct": {}, 24 | "indirect": {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Olavi Haapala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ken-wheeler-aka", 3 | "version": "1.0.0", 4 | "description": "What is Ken Wheeler's display name on Twitter?", 5 | "main": "backend/index.js", 6 | "scripts": { 7 | "start": "NODE_OPTIONS='--openssl-legacy-provider' node ./backend/index.js", 8 | "dev": "NODE_OPTIONS='--openssl-legacy-provider' elm-app start", 9 | "build": "echo no-op", 10 | "build:frontend": "NODE_OPTIONS='--openssl-legacy-provider' NODE_ENV=production elm-app build", 11 | "test": "test" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/olpeh/ken-wheeler-aka.git" 16 | }, 17 | "author": "Olavi Haapala", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/olpeh/ken-wheeler-aka/issues" 21 | }, 22 | "homepage": "https://github.com/olpeh/ken-wheeler-aka#readme", 23 | "dependencies": { 24 | "@koa/cors": "^2.2.1", 25 | "dotenv": "^6.0.0", 26 | "koa": "^2.5.1", 27 | "koa-bodyparser": "^4.2.1", 28 | "koa-cache-control": "^2.0.0", 29 | "koa-request": "^1.0.0", 30 | "koa-route": "^3.2.0", 31 | "mongodb": "5.6.0", 32 | "redis": "^2.8.0", 33 | "request": "^2.87.0", 34 | "twit": "^2.2.10" 35 | }, 36 | "devDependencies": { 37 | "create-elm-app": "4.0.0", 38 | "elm": "0.19.1", 39 | "elm-format": "0.8.2", 40 | "prettier": "1.18.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ken-wheeler-aka 2 | 3 | What are Ken Wheeler's display names on Twitter? 4 | 5 | ## Excuse me, what? 6 | 7 | Just a silly side project for collecting [Ken Wheelers](https://twitter.com/ken_wheeler/) display names on Twitter and displaying them on a simple web site. 8 | 9 | ## But why? 10 | 11 | Just for fun and for learning: 12 | 13 | - First time using the Twitter API for reading information 14 | - My first ever "real" application written in [Elm](http://elm-lang.org/) 15 | - Testing how deploying small prototypes using [Beaker Browser](https://beakerbrowser.com/) works 16 | 17 | ## Check it out! 18 | 19 | https://ken-wheeler-aka.vercel.app/ 20 | 21 | ## How does it work? 22 | 23 | There is a small backend application running in Heroku, which polls the Twitter API for ken_wheelers name. 24 | The names are then stored into a database and the api provides them to the UI. 25 | The UI is built using Elm, and it is based on [create-elm-app](https://github.com/halfzebra/create-elm-app) 26 | 27 | ## Development 28 | 29 | Install dependencies: `yarn` 30 | 31 | Start the backend: `npm start` 32 | 33 | Deploy the backend: `fly deploy` (Fly.io) 34 | 35 | Start the frontend: `npm run dev` 36 | 37 | Build the frontend: `npm run build` 38 | 39 | Deploy the frontend: `git push` (Vercel) 40 | 41 | ## Acknowledgements 42 | 43 | - The elm app is heavily inspired by [elm-quicks](https://github.com/ohanhi/elm-quicks/) by [ohanhi](https://github.com/ohanhi/) 44 | - This project is sponsored by [Futurice's](https://futurice.com/) [Open Source Sponsorship program](http://spiceprogram.org/oss-sponsorship) 45 | -------------------------------------------------------------------------------- /backend/db.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require('mongodb'); 2 | const mongoUrl = process.env.MONGODB_URI; 3 | 4 | const bot = require('./bot'); 5 | 6 | let db; 7 | 8 | async function setup() { 9 | const client = new MongoClient(mongoUrl); 10 | const dbName = 'ken-wheeler-aka'; 11 | await client.connect(); 12 | db = client.db(dbName); 13 | console.log('Initializing db...'); 14 | } 15 | 16 | async function getResultsForScreenName(screenName) { 17 | const collection = db.collection(`results_${screenName}`); 18 | return collection.find({}, { name: 1, _id: 0 }).toArray(); 19 | } 20 | 21 | async function getPreviousResultForScreenName(collection) { 22 | return collection.findOne({}, { name: 1, _id: 0, sort: { _id: -1 } }); 23 | } 24 | 25 | async function insertAndTweetIfChanged(screenName, result, tweetNowFn) { 26 | const collection = db.collection(`results_${screenName}`); 27 | await getPreviousResultForScreenName(collection).then(previousResult => { 28 | console.log('Found from db:', { previousResult, result }); 29 | 30 | if (!previousResult || (previousResult && previousResult.name !== result)) { 31 | // Always cast to string in order to allow "undefined" as a string etc. 32 | const data = { name: `${result}` }; 33 | collection.insertOne(data); 34 | 35 | if (screenName === 'ken_wheeler') { 36 | const tweetText = `.@ken_wheeler is now known as "${result}". See https://ken-wheeler-aka.vercel.app/`; 37 | tweetNowFn(tweetText); 38 | } 39 | } 40 | }); 41 | } 42 | 43 | module.exports = { 44 | setup, 45 | getResultsForScreenName, 46 | insertAndTweetIfChanged 47 | }; 48 | -------------------------------------------------------------------------------- /src/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | width: 100%; 4 | height: 98%; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | body { 10 | font-family: 'Source Sans Pro', 'Trebuchet MS', 'Lucida Grande', 11 | 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif; 12 | margin: 0; 13 | text-align: center; 14 | color: #293c4b; 15 | } 16 | 17 | .main { 18 | padding: 20px; 19 | padding-bottom: 50px; 20 | display: flex; 21 | flex-direction: column; 22 | justify-content: space-around; 23 | margin-bottom: 50px; 24 | } 25 | 26 | .loading { 27 | margin-top: 40px; 28 | } 29 | 30 | h1 { 31 | font-size: 30px; 32 | color: #428bc1; 33 | } 34 | 35 | .twitter-link { 36 | color: #428bc1; 37 | } 38 | 39 | .since { 40 | font-style: italic; 41 | color: #8a8a8a; 42 | font-size: 16px; 43 | } 44 | 45 | .display-names { 46 | text-align: left; 47 | margin: 0 auto; 48 | margin-top: 10px; 49 | } 50 | 51 | li { 52 | margin-top: 10px; 53 | } 54 | 55 | li.error { 56 | color: #cf0000; 57 | } 58 | 59 | .display-names li:first-of-type::after { 60 | content: ' (current)'; 61 | font-weight: bold; 62 | } 63 | 64 | footer { 65 | font-size: 16px; 66 | position: fixed; 67 | bottom: 0; 68 | padding: 6px; 69 | padding-top: 0; 70 | text-align: center; 71 | width: 100%; 72 | background: #428bc1; 73 | color: #ffffff; 74 | } 75 | 76 | footer a { 77 | color: #ffffff !important; 78 | } 79 | 80 | footer ul { 81 | list-style: none; 82 | padding: 0; 83 | margin: 0 auto; 84 | text-align: center; 85 | } 86 | 87 | footer ul li { 88 | display: inline-block; 89 | } 90 | 91 | footer li:not(:last-child)::after { 92 | content: ' | '; 93 | } 94 | -------------------------------------------------------------------------------- /backend/bot.js: -------------------------------------------------------------------------------- 1 | const Twit = require('twit'); 2 | const config = require('./config'); 3 | const db = require('./db'); 4 | 5 | const { SCREEN_NAMES } = process.env; 6 | 7 | let bot; 8 | 9 | function setup() { 10 | bot = new Twit(config.twitterKeys); 11 | console.log('Bot starting...'); 12 | runCheck(); 13 | setInterval(runCheck, config.twitterConfig.check); 14 | } 15 | 16 | const runCheck = async () => { 17 | console.log('Checking for names via the Twitter API', new Date()); 18 | const screenNames = SCREEN_NAMES.split(' '); 19 | 20 | for (const screenName of screenNames) { 21 | console.log(screenName); 22 | 23 | bot.get( 24 | 'users/show', 25 | { screen_name: screenName, include_entities: false }, 26 | async (err, data, response) => { 27 | if (err) { 28 | console.error('ERROR,', err); 29 | } else { 30 | console.log('SUCCESS: received: ', data.name); 31 | const currentName = data.name; 32 | await db.insertAndTweetIfChanged(screenName, currentName, tweetNow); 33 | } 34 | } 35 | ); 36 | } 37 | }; 38 | 39 | async function tweetNow(text) { 40 | const tweet = { 41 | status: text 42 | }; 43 | 44 | if (config.twitterConfig.enabled) { 45 | console.log('Going to try to tweet: ', text); 46 | bot.post('statuses/update', tweet, (err, data, response) => { 47 | if (err) { 48 | console.error('ERROR in tweeting!', err); 49 | } else { 50 | console.log('SUCCESS! tweeted: ', text); 51 | } 52 | }); 53 | } else { 54 | console.log('Tweeting was disabled, but would have tweeted:', { text }); 55 | } 56 | } 57 | 58 | module.exports = { 59 | setup: setup, 60 | tweetNow: tweetNow 61 | }; 62 | -------------------------------------------------------------------------------- /backend/index.js: -------------------------------------------------------------------------------- 1 | const cors = require('@koa/cors'); 2 | const koa = require('koa'); 3 | const bodyParser = require('koa-bodyparser'); 4 | const route = require('koa-route'); 5 | const app = new koa(); 6 | const redis = require('redis'); 7 | const cacheControl = require('koa-cache-control'); 8 | 9 | const bot = require('./bot'); 10 | const db = require('./db'); 11 | 12 | require('dotenv').config(); 13 | 14 | app.use(cors()); 15 | app.use(bodyParser()); 16 | 17 | const port = process.env.PORT || 8080; 18 | const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; 19 | const cacheTtlSeconds = process.env.CACHE_TTL_SECONDS || 60; 20 | app.use( 21 | cacheControl({ 22 | maxAge: cacheTtlSeconds 23 | }) 24 | ); 25 | 26 | console.log({ port, redisUrl, cacheTtlSeconds }); 27 | 28 | // create a new redis client and connect to our local redis instance 29 | const redisClient = redis.createClient({ url: redisUrl }); 30 | 31 | // if an error occurs, print it to the console 32 | redisClient.on('error', err => console.error('Error ' + err)); 33 | 34 | async function setUpApp() { 35 | await db.setup(); 36 | bot.setup(); 37 | 38 | app.use( 39 | route.get('/api/aka/:screenName', async (ctx, screenName) => { 40 | try { 41 | await new Promise(resolve => { 42 | redisClient.get(screenName, async (error, result) => { 43 | if (result) { 44 | ctx.response.statusCode = 200; 45 | ctx.response.body = { 46 | success: true, 47 | data: JSON.parse(result) 48 | }; 49 | resolve(); 50 | } else { 51 | const data = await db.getResultsForScreenName(screenName); 52 | // with an expiry of 1 minute (60s) 53 | await redisClient.setex( 54 | screenName, 55 | cacheTtlSeconds, 56 | JSON.stringify(data) 57 | ); 58 | ctx.response.statusCode = 200; 59 | ctx.response.body = { success: true, data }; 60 | resolve(); 61 | } 62 | }); 63 | }); 64 | } catch (e) { 65 | console.error(e); 66 | ctx.response.statusCode = 500; 67 | ctx.response.body = { success: false, error: e }; 68 | } 69 | }) 70 | ); 71 | 72 | console.log('app listening on port ' + port); 73 | app.listen(port); 74 | } 75 | 76 | setUpApp(); 77 | -------------------------------------------------------------------------------- /src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (ApiResponse, DisplayName, Model, Msg(..), decoder, displayNameDecoder, displayNameView, displayNamesView, errorView, footerView, handler, init, main, subscriptions, update, view) 2 | 3 | import Browser 4 | import Html exposing (Html, a, br, div, footer, h1, img, li, ol, span, text, ul) 5 | import Html.Attributes exposing (class, reversed) 6 | import Http 7 | import Json.Decode exposing (bool, list, string) 8 | import Markdown 9 | import Html.Attributes exposing (href) 10 | 11 | 12 | type Msg 13 | = GotDisplayNames (List DisplayName) 14 | | GotError Http.Error 15 | 16 | 17 | type alias Model = 18 | { displayNames : List DisplayName 19 | , error : Maybe Http.Error 20 | , loading : Bool 21 | } 22 | 23 | 24 | type alias ApiResponse = 25 | { success : Bool 26 | , data : List DisplayName 27 | } 28 | 29 | 30 | type alias DisplayName = 31 | { name : Maybe String } 32 | 33 | 34 | update : Msg -> Model -> ( Model, Cmd Msg ) 35 | update msg model = 36 | case msg of 37 | GotDisplayNames displayNames -> 38 | ( { model | displayNames = displayNames, loading = False }, Cmd.none ) 39 | 40 | GotError error -> 41 | ( { model | error = Just error, loading = False }, Cmd.none ) 42 | 43 | 44 | view : Model -> Browser.Document Msg 45 | view model = 46 | { title = "Ken Wheeler's display names" 47 | , body = 48 | [ div [] 49 | [ div [ class "main" ] 50 | [ h1 [] [ a [class "twitter-link", href "https://twitter.com/ken_wheeler"] [text "@ken_wheeler – also known as" ]] 51 | , div [ class "since" ] [ text "Since 2018-06-27 (note: missing data between 11/2020 and 06/2023)" ] 52 | , if model.loading == True then 53 | div [ class "loading" ] [ text "Loading..." ] 54 | 55 | else 56 | displayNamesView model.displayNames 57 | , errorView model.error 58 | ] 59 | , footer [] [ footerView ] 60 | ] 61 | ] 62 | } 63 | 64 | 65 | displayNamesView : List DisplayName -> Html msg 66 | displayNamesView displayNames = 67 | displayNames 68 | |> List.reverse 69 | |> List.map displayNameView 70 | |> ol [ class "display-names", reversed True ] 71 | 72 | 73 | displayNameView : DisplayName -> Html msg 74 | displayNameView displayName = 75 | case displayName.name of 76 | Just name -> 77 | li [] [ text name ] 78 | 79 | Nothing -> 80 | li [ class "error" ] [ text "Error: Invalid data – possibly \"undefined\" or \"null\"" ] 81 | 82 | 83 | errorView : Maybe Http.Error -> Html msg 84 | errorView err = 85 | case err of 86 | Just e -> 87 | case e of 88 | Http.NetworkError -> 89 | text "No connection, try again later." 90 | 91 | Http.Timeout -> 92 | text "Network timed out." 93 | 94 | Http.BadUrl _ -> 95 | text "It's not you, it's me. I have the server address wrong." 96 | 97 | Http.BadStatus _ -> 98 | text "The server didn't like the request (bad status)." 99 | 100 | Http.BadPayload _ _ -> 101 | text "Ouch, the server responded with strange contents." 102 | 103 | Nothing -> 104 | text "" 105 | 106 | 107 | footerView : Html msg 108 | footerView = 109 | Markdown.toHtml [] """ 110 | * Built by [0lpeh](https://twitter.com/0lpeh) 111 | * Repository available at [GitHub](https://github.com/olpeh/ken-wheeler-aka) 112 | * Follow [@ken_wheeler_aka](https://twitter.com/ken_wheeler_aka) 113 | """ 114 | 115 | 116 | type alias Flags = 117 | { apiBaseUrl : String } 118 | 119 | 120 | init : Flags -> ( Model, Cmd Msg ) 121 | init flags = 122 | ( { displayNames = [] 123 | , error = Nothing 124 | , loading = True 125 | } 126 | , Http.send handler (Http.get flags.apiBaseUrl decoder) 127 | ) 128 | 129 | 130 | decoder : Json.Decode.Decoder ApiResponse 131 | decoder = 132 | Json.Decode.map2 ApiResponse 133 | (Json.Decode.at [ "success" ] bool) 134 | (Json.Decode.at [ "data" ] (list displayNameDecoder)) 135 | 136 | 137 | displayNameDecoder : Json.Decode.Decoder DisplayName 138 | displayNameDecoder = 139 | Json.Decode.map DisplayName 140 | (Json.Decode.maybe (Json.Decode.field "name" Json.Decode.string)) 141 | 142 | 143 | handler : Result Http.Error ApiResponse -> Msg 144 | handler result = 145 | case result of 146 | Ok apiResponse -> 147 | GotDisplayNames apiResponse.data 148 | 149 | Err error -> 150 | GotError error 151 | 152 | 153 | subscriptions : Model -> Sub Msg 154 | subscriptions model = 155 | Sub.none 156 | 157 | 158 | main : Program Flags Model Msg 159 | main = 160 | Browser.document 161 | { view = view 162 | , init = init 163 | , update = update 164 | , subscriptions = subscriptions 165 | } 166 | -------------------------------------------------------------------------------- /dump/results_ken_wheeler.json: -------------------------------------------------------------------------------- 1 | {"_id":{"$oid":"5b33aa5b96c66fa3df51b584"},"name":"🇺🇸 UH MERICA 🇺🇲"} 2 | {"_id":{"$oid":"5b37729d96c66fa3df8ecc92"},"name":"🇺🇸 AMERIKANYE 🇺🇲"} 3 | {"_id":{"$oid":"5b38273b96c66fa3df971c0e"},"name":"🇺🇸 KANYEBRAHAM LINCOLN 🇺🇲"} 4 | {"_id":{"$oid":"5b38c06596c66fa3df9cf217"},"name":"🇺🇸 🗽🦅 UH MERICA 🍺🏹 🇺🇲"} 5 | {"_id":{"$oid":"5b3a9bb196c66fa3dfb3750f"},"name":"🇺🇸 🗽🦅 THE NAME YOU GO BY 🍺🏹 🇺🇲"} 6 | {"_id":{"$oid":"5b3aa9c296c66fa3dfb40a93"},"name":"🇺🇸 🗽🦅 AMERIKEN WHEELER 🍺🏹 🇺🇲"} 7 | {"_id":{"$oid":"5b3b995896c66fa3dfc1179c"},"name":"🇺🇸 🗽🦅 KEN WHEELER 🍺🏹 🇺🇲"} 8 | {"_id":{"$oid":"5b3c027a96c66fa3dfc75d59"},"name":"🇺🇸 🗽🦅 AMERICAN FLAG GUY 🍺🏹 🇺🇲"} 9 | {"_id":{"$oid":"5b3e6ed896c66fa3dfec8c23"},"name":"🇺🇸 OMG SO EXTRA 🇺🇲"} 10 | {"_id":{"$oid":"5b3ebfb496c66fa3dff005e8"},"name":"Ken Wheeler"} 11 | {"_id":{"$oid":"5b41639596c66fa3df217c86"},"name":"Ken's On Fuckin Vacation"} 12 | {"_id":{"$oid":"5b479dfd66e51e0014206611"},"name":"Ken Wheeler"} 13 | {"_id":{"$oid":"5b51183794074f001423a000"},"name":"HOT TAKE KENNETH"} 14 | {"_id":{"$oid":"5b5181ade2ac30001403abb3"},"name":"🔥 HOT TAKE KENNETH 🔥"} 15 | {"_id":{"$oid":"5b52807c45bfbe001434f3a5"},"name":"COLD STONE STEVE AUSTIN"} 16 | {"_id":{"$oid":"5b53554056b2b200140c5933"},"name":"GIRTH BROOKS"} 17 | {"_id":{"$oid":"5b53980e122f560014f7605a"},"name":"Ken Wheeler"} 18 | {"_id":{"$oid":"5b664c4aab769f0014740a48"},"name":"lil :wq AKA bread flintstone"} 19 | {"_id":{"$oid":"5b67cf070cdfbc0014a8f323"},"name":"ReasonML Glowing Laser Cobra"} 20 | {"_id":{"$oid":"5b683f8e34409b00144c0a52"},"name":"Notorious WIP"} 21 | {"_id":{"$oid":"5b69d05000391e001440ac46"},"name":"yung fortnite juul yeezy papi"} 22 | {"_id":{"$oid":"5b6a40cb5563c50014abd3e3"},"name":"NACHO MAN RANDY SAVAGE"} 23 | {"_id":{"$oid":"5b7016e5102b1c001479575f"},"name":"MR WORLDWIDE(WEB)"} 24 | {"_id":{"$oid":"5b793b0da37d5d0014d4a952"},"name":"SCRIPT GAME CHUCK NORRIS"} 25 | {"_id":{"$oid":"5b7f20ee4785810014127936"},"name":"FATRICK BATEMAN"} 26 | {"_id":{"$oid":"5b82185060c0ab001441b508"},"name":"K E R N W H E E L E R"} 27 | {"_id":{"$oid":"5b8369d3bc58e30014cff891"},"name":"LIVE ACTION HOMER SIMPSON"} 28 | {"_id":{"$oid":"5b84717a5400a00014ca4914"},"name":"Kenneth John"} 29 | {"_id":{"$oid":"5b8a014d83375f00140144a6"},"name":"POWDERED TOAST MAN"} 30 | {"_id":{"$oid":"5b8b1cebea309500148a3d7d"},"name":"🇺🇸 JavaScript Kanye 🇺🇸"} 31 | {"_id":{"$oid":"5b8ca44ed87c4200149ffb7d"},"name":"RACOON CITY PD"} 32 | {"_id":{"$oid":"5b91ea529971130014155c2c"},"name":"Perf Reynolds"} 33 | {"_id":{"$oid":"5b958a764858cb0014e37cc4"},"name":"WEYLAND YUTANI OFFICIAL"} 34 | {"_id":{"$oid":"5b9881d72dbe4b001489d70d"},"name":"DA BEARS"} 35 | {"_id":{"$oid":"5b991df3606dd80014477b67"},"name":"JUSTIN VIMBERLAKE"} 36 | {"_id":{"$oid":"5b99d3582dc88a0014918d1f"},"name":"Jimmy Juulpods"} 37 | {"_id":{"$oid":"5b9ab6d507c6ea001488a34d"},"name":"Totally Emily Freeman"} 38 | {"_id":{"$oid":"5b9bba2e04db270014c3870e"},"name":"One scripty boi"} 39 | {"_id":{"$oid":"5b9c12df3b5d1e0013d8866e"},"name":"LIVE ACTION LAUNCHPAD MCQUACK"} 40 | {"_id":{"$oid":"5b9c71e2a6878e00135ebea4"},"name":"Sean 'Protobuf Daddy' Combs"} 41 | {"_id":{"$oid":"5ba06ad92681c1001369f39e"},"name":"miller high life papi"} 42 | {"_id":{"$oid":"5ba368159f451d0013cc54ba"},"name":"wheezy f(baby)"} 43 | {"_id":{"$oid":"5ba6cec88b4b0800133fde2c"},"name":"GRANDMASTER FLASH PLAYER CS3"} 44 | {"_id":{"$oid":"5ba6d1208b4b0800133fde2d"},"name":"🇺🇸 DWAYNE ELIZONDO MOUNTAIN DEW CAMACHO 🇺🇸"} 45 | {"_id":{"$oid":"5baa219299dcfc00136c3466"},"name":"TIDE POD TOMMY"} 46 | {"_id":{"$oid":"5bac380b043f200013047cf8"},"name":"DAT BOI feat. DEM BOYZ"} 47 | {"_id":{"$oid":"5badb1447d55c200131680ec"},"name":"cool juul uncle"} 48 | {"_id":{"$oid":"5bb03cd8c8238700136c4285"},"name":"Ke"} 49 | {"_id":{"$oid":"5bb28b761e739b0013cc301f"},"name":"ke"} 50 | {"_id":{"$oid":"5bbbc5e0fe2f4c001330bbaa"},"name":"JAVASCRIPT KANYE"} 51 | {"_id":{"$oid":"5bc0cfd32ad67d00137c7326"},"name":"LOW INFORMATION CODER"} 52 | {"_id":{"$oid":"5bc5064f52ae5c00133dc377"},"name":"JERSEY DEV(il) aka COUNT HACKULA"} 53 | {"_id":{"$oid":"5bc508a752ae5c00133dc378"},"name":"DANKENSTEIN"} 54 | {"_id":{"$oid":"5bc67c83e137e90013ad4475"},"name":"JSON VORHEES"} 55 | {"_id":{"$oid":"5bc9bd578951a30013cd2d82"},"name":"HANNIBAL VECTOR"} 56 | {"_id":{"$oid":"5bca9ad7af8b1e0013375455"},"name":"COUNT SLACKULA"} 57 | {"_id":{"$oid":"5bcb522dc5f6810013703012"},"name":"ECTOCOOLER FLAVORED ZUULPODS"} 58 | {"_id":{"$oid":"5bcce977b3879b0013ef34bf"},"name":"JAVASCRIPT PETER VENKMAN"} 59 | {"_id":{"$oid":"5bcdbc685068720013228988"},"name":"LOCALHOSTBUSTERS"} 60 | {"_id":{"$oid":"5bce8f563833f6001378dd33"},"name":"ZUUL RUNNINGS"} 61 | {"_id":{"$oid":"5bcfba9a785dbf0013ac7f64"},"name":"SELECT * WHERE WOLF"} 62 | {"_id":{"$oid":"5bd380f4e0b2510013ea3f19"},"name":"{...}y Krueger"} 63 | {"_id":{"$oid":"5bd4d279b435cd001377d2e4"},"name":"A NIGHTMARE ON REASONML STREET"} 64 | {"_id":{"$oid":"5bdca56b873f5e00130418f5"},"name":"useKenWheeler"} 65 | {"_id":{"$oid":"5beb9e648f06d90014744d1e"},"name":"turducKEN 🦃🦆🇺🇸"} 66 | {"_id":{"$oid":"5bf54e1a0c23cb00142bd28d"},"name":"drunken thanksgiving uncle"} 67 | {"_id":{"$oid":"5bf77bea422d83001465c68b"},"name":"FANTA CLAUS"} 68 | {"_id":{"$oid":"5bf7d9aa422d83001465c68c"},"name":"Christmas Kenneth"} 69 | {"_id":{"$oid":"5bf7dad6422d83001465c68d"},"name":"yung beardless santa"} 70 | {"_id":{"$oid":"5c00d860bb7eca001406ed3f"},"name":"herr wheelenschnitzenheimer @ 🇩🇪"} 71 | {"_id":{"$oid":"5c04055b822e5e00147c0a7c"},"name":"naughty list kenneth"} 72 | {"_id":{"$oid":"5c05b74675f2f8001461a0e8"},"name":"mrs claus’ sidepiece"} 73 | {"_id":{"$oid":"5c099c886fb52a001483727f"},"name":"gnarcellus wallace"} 74 | {"_id":{"$oid":"5c14f347d4b78a001426d2b2"},"name":"colm o’driscoll"} 75 | {"_id":{"$oid":"5c1c118e80cb2d00146be566"},"name":"JAVASCRIPT HERC FROM THE WIRE"} 76 | {"_id":{"$oid":"5c1d630ca90e7800142c1b67"},"name":"WINSTON WOLF"} 77 | {"_id":{"$oid":"5c1e7280a114e40014fd7b00"},"name":"COTTON HEADED NINNYMUGGINS"} 78 | {"_id":{"$oid":"5c2373c9161c48001421027e"},"name":"2019 EDITION KENNETH"} 79 | {"_id":{"$oid":"5c26f1ed0ed6340014b74005"},"name":"GEORGE LIQUOR"} 80 | {"_id":{"$oid":"5c28436ca35ad2001490f8f9"},"name":"UNCLE DRUNKFACE"} 81 | {"_id":{"$oid":"5c2edaee81ad6f0014a14cc4"},"name":"BRAND NEW KENNETH"} 82 | {"_id":{"$oid":"5c3eace788d70d0014533514"},"name":"🇺🇸 Benjamin Danklin 🇺🇸"} 83 | {"_id":{"$oid":"5c3ffe6d4315b30014fb5625"},"name":"🇺🇸 Toxically Masculine Javascript 🇺🇸"} 84 | {"_id":{"$oid":"5c424d0bc57d830014ef74ae"},"name":"🇺🇸 BOBBY JAVASCRIPT 🇺🇸"} 85 | {"_id":{"$oid":"5c4695f47156df0014511044"},"name":"🇺🇸 YUNG WEBWORKER 🇺🇸"} 86 | {"_id":{"$oid":"5c4e458b75089600148f4917"},"name":"JS-JS-JS"} 87 | {"_id":{"$oid":"5c5badfe6b6d350014369966"},"name":"Kenny"} 88 | {"_id":{"$oid":"5c65382351a00c0014aa8e6a"},"name":"DR NIGEL CHANNING"} 89 | {"_id":{"$oid":"5c69c7ee26204c00144b7091"},"name":"THE WATERMELON WOLF"} 90 | {"_id":{"$oid":"5c6aa2799932e60014f9cd79"},"name":"DIGITAL GANGSTER"} 91 | {"_id":{"$oid":"5c6b47b38becc20015088107"},"name":"JAVASCRIPT WILLIE BEAMIN ASK YOUR BITCH"} 92 | {"_id":{"$oid":"5c6c14c3569fbb0015ac2e62"},"name":"CHITOWN KENNETH"} 93 | {"_id":{"$oid":"5c6d81a5a0b3860015c779e2"},"name":"EDGAR ALLEN DOUGH"} 94 | {"_id":{"$oid":"5c6f7476d196cc0015664783"},"name":"WHEELER BIRD RESEARCH"} 95 | {"_id":{"$oid":"5c72177ed1ea8000151d5f93"},"name":"🇺🇸 DRIP GAME CAT STEVENS 🇺🇸"} 96 | {"_id":{"$oid":"5c76ae97d591080015e7e381"},"name":"🇺🇸 PIPE GAME JOE NAMATH 🇺🇸"} 97 | {"_id":{"$oid":"5c787fc4594adf00156258a0"},"name":"GOON SHIT"} 98 | {"_id":{"$oid":"5c7c224dbb6b140015c81a50"},"name":"THICC BUNDLES"} 99 | {"_id":{"$oid":"5c7d6e93e669ae001507b036"},"name":"ITS RAINING KEN"} 100 | {"_id":{"$oid":"5c80439cf583740015ef21bd"},"name":"Ken Wheeler"} 101 | {"_id":{"$oid":"5c917055178e1f00152f40b3"},"name":"GRANDMASTER FLASH CS3"} 102 | {"_id":{"$oid":"5c9441b79c99da0015b285f1"},"name":"Dr Reginald Cox"} 103 | {"_id":{"$oid":"5c96b6779243a90015b40947"},"name":"UNPOPULAR OPINIONS"} 104 | {"_id":{"$oid":"5c9b7938cdef9d001577dc72"},"name":"SKRTT REYNOLDS"} 105 | {"_id":{"$oid":"5c9f9ffc73901200156f4440"},"name":"CRON MATTINGLY"} 106 | {"_id":{"$oid":"5ca579c0d8a13a0015923cd4"},"name":"SCRIPT GAME BRUCE WILLIS"} 107 | {"_id":{"$oid":"5cb3f46891c8150015b89354"},"name":"OL SPRINGTIME KENNETH"} 108 | {"_id":{"$oid":"5cb7b1e5a877690015b2090b"},"name":"KENNETH IN MISSISSIPPI"} 109 | {"_id":{"$oid":"5cb9e513e52ad80015b954a8"},"name":"KENNETH ON HIS WAY HOME"} 110 | {"_id":{"$oid":"5cbb891c60e8510015c86b4a"},"name":"SCRUMBAG STEVE"} 111 | {"_id":{"$oid":"5cc3c6784f899b00154a47a9"},"name":"RICH HOMIE QUANT"} 112 | {"_id":{"$oid":"5cc615165ad1c400157b5d27"},"name":"JUST KEN THINGS"} 113 | {"_id":{"$oid":"5cc623265ad1c400157b5d28"},"name":"PURPLE CITY NERD GANG"} 114 | {"_id":{"$oid":"5cd53ad2645f6900152e9ccd"},"name":"Docker, Texas Ranger"} 115 | {"_id":{"$oid":"5cde246e01c5c80015c1c4e3"},"name":"FATRICK BATEMAN"} 116 | {"_id":{"$oid":"5cdf3c5fca7aa7001544d287"},"name":"TURBOCHAD.TSX"} 117 | {"_id":{"$oid":"5ce144de6996f40015b62dae"},"name":"JONATHAN TAYLOR COMMAS"} 118 | {"_id":{"$oid":"5ce287228af63d0015e7cc43"},"name":"Ted NuGet"} 119 | {"_id":{"$oid":"5ce52c7be160a400155a222e"},"name":"JETHRO NULL"} 120 | {"_id":{"$oid":"5cebce838451f10015fa5773"},"name":"GRILL BEAST GANGSTER"} 121 | {"_id":{"$oid":"5cf09981fd71a4001568eada"},"name":"WHITE DEBO"} 122 | {"_id":{"$oid":"5cf1eaf941d1e0001523ab52"},"name":"XL TYRION LANNISTER"} 123 | {"_id":{"$oid":"5cf6e638474c1a0015848147"},"name":"THICC BUNDLES"} 124 | {"_id":{"$oid":"5d01bd0023bfef0015fee49a"},"name":"PATAGUCCI MANE"} 125 | {"_id":{"$oid":"5d1d079efa13a60015e7df14"},"name":"AMERIKEN WHEELER"} 126 | {"_id":{"$oid":"5d2a470d54175c0015759364"},"name":"kenX developer"} 127 | {"_id":{"$oid":"5d30d185071ff000151ce3c5"},"name":"ALMIGHTY BRO"} 128 | {"_id":{"$oid":"5d3677e81d36050015198a35"},"name":"UNCLE RICO BUT FOR REAL THO"} 129 | {"_id":{"$oid":"5d3a2f5276c1740015662df0"},"name":"🇺🇸 MR WHEELER 🇺🇸"} 130 | {"_id":{"$oid":"5d3e1ccd75c178001579b2f8"},"name":"🇺🇸 yung chief hopper 🇺🇸"} 131 | {"_id":{"$oid":"5d4ce26a5986c600151fa55d"},"name":"🇺🇸 pipe game emelio estevez 🇺🇸"} 132 | {"_id":{"$oid":"5d561ceab88e4100153da7df"},"name":"🇺🇸 ol saint thiccholas 🇺🇸"} 133 | {"_id":{"$oid":"5d59bd0b97fec700152963df"},"name":"🇺🇸 byte game dana white 🇺🇸"} 134 | {"_id":{"$oid":"5d5b62e8b280660015ca4428"},"name":"🇺🇸 yung al borland 🇺🇸"} 135 | {"_id":{"$oid":"5d5d2d209ce66a0015c6040a"},"name":"🇺🇸 lil SaaS x 🇺🇸"} 136 | {"_id":{"$oid":"5d601ac8975ee400153c3cab"},"name":"Ken Wheeler"} 137 | {"_id":{"$oid":"5d707aeac031690015eed22e"},"name":"ON A BREAK"} 138 | {"_id":{"$oid":"5d8835eae078e80015d5f147"},"name":"KENNETH"} 139 | {"_id":{"$oid":"5d9564e6e3931b001577549b"},"name":"LL GHOUL J"} 140 | {"_id":{"$oid":"5dacd62c3616c70015880f8b"},"name":"JSON VORHEES"} 141 | {"_id":{"$oid":"5db0c01d6523be00156dcf19"},"name":"ABSOLUTE UNIT TEST"} 142 | {"_id":{"$oid":"5dba8f6d61d0960015618db3"},"name":"HOLLA WEEN"} 143 | {"_id":{"$oid":"5dbd6d080a923f001561b1c9"},"name":"JIMMY FLAMINGO"} 144 | {"_id":{"$oid":"5dc63a894393c0001510a38a"},"name":"RICKY SMOOTH"} 145 | {"_id":{"$oid":"5dcb808893d27f00150cd118"},"name":"LAUNCHPAD MCQUACK"} 146 | {"_id":{"$oid":"5dcdea221a90ab001593a7cb"},"name":"CRON BON JOVI"} 147 | {"_id":{"$oid":"5dd33baf41a45f0015f31bbd"},"name":"THICC JAGGER"} 148 | {"_id":{"$oid":"5dd9aca9ca09e200154df4e2"},"name":"BREAD FLANDERS"} 149 | {"_id":{"$oid":"5de2e7233d52900015b27608"},"name":"Ol Saint Thicc"} 150 | {"_id":{"$oid":"5dee43ba6883870015f8ff10"},"name":"NAUGHTY LIST VETERAN"} 151 | {"_id":{"$oid":"5dfd34e27470c10015f4d600"},"name":"ELON HUSK"} 152 | {"_id":{"$oid":"5e018dfb050a0000150516ba"},"name":"LAMBDA CLAUS"} 153 | {"_id":{"$oid":"5e0d3212158046001545d6d2"},"name":"OL KENNY NOFUX"} 154 | {"_id":{"$oid":"5e1948fd0f4de600159c068c"},"name":"script game angela lansbury"} 155 | {"_id":{"$oid":"5e1cfd058ef4440015a72979"},"name":"pauly powershell"} 156 | {"_id":{"$oid":"5e23d4fc88934100151cb582"},"name":"ol dirty plastered"} 157 | {"_id":{"$oid":"5e2f5e192814a2001510bb2a"},"name":"HENNY KENNY"} 158 | {"_id":{"$oid":"5e39ad7f3bf3e200157365d1"},"name":"THE LAMBDELOREAN"} 159 | {"_id":{"$oid":"5e3dde99f27a9f0015e2a54a"},"name":"Javascript Happy Gilmore"} 160 | {"_id":{"$oid":"5e46290a45a552001501df97"},"name":"CRON STAMOS"} 161 | {"_id":{"$oid":"5e5af12cf75f6900152cdb37"},"name":"GIGACHAD DOT JPEG"} 162 | {"_id":{"$oid":"5e5ba7fe8c7bb3001512c369"},"name":"sophmore developer"} 163 | {"_id":{"$oid":"5e5c293996475400157f7b39"},"name":"PAULY PUFFBAR"} 164 | {"_id":{"$oid":"5e696018502c830015458e42"},"name":"LIL UZI PERF"} 165 | {"_id":{"$oid":"5e793222f86add0015cba0a0"},"name":"BROVID-19"} 166 | {"_id":{"$oid":"5e7fc970caa128001571b428"},"name":"JARDANI JAVASCRIPTOVICH"} 167 | {"_id":{"$oid":"5e88f5e17b14ca0015261f46"},"name":"PETER PANDEMIC"} 168 | {"_id":{"$oid":"5e8c5460b9ce860015a97347"},"name":"QUARANTEEN WOLF"} 169 | {"_id":{"$oid":"5e8fe1bf9bad4a0015d86f36"},"name":"john thicc"} 170 | {"_id":{"$oid":"5e924ee5d748f80015066b4c"},"name":null} 171 | {"_id":{"$oid":"5e92594959eaed0015b18d56"},"name":"john thicc"} 172 | {"_id":{"$oid":"5e97cac10adbf30015b4448c"},"name":"quaranteenage heartthrob"} 173 | {"_id":{"$oid":"5e9b6ae09f11a600151136d3"},"name":"Kentin Quarantino"} 174 | {"_id":{"$oid":"5eaab61c13d37700156ed420"},"name":"Lyin' Ken Wheeler"} 175 | {"_id":{"$oid":"5eaddfdf255f0c0015b93f44"},"name":"A$AP Schrader"} 176 | {"_id":{"$oid":"5ebb0edfb5bc640015521570"},"name":"yung gucci puter papi"} 177 | {"_id":{"$oid":"5ec83ddfe6464b00158f131a"},"name":"lil whiskey moufwash"} 178 | {"_id":{"$oid":"5eefd568f76cf70015383aac"},"name":"very sunburnt kenneth"} 179 | {"_id":{"$oid":"5f111cefaaa2cc001528afd1"},"name":"hot url summer"} 180 | {"_id":{"$oid":"5f384d56b9603f0015116cea"},"name":"wendy peffercorn fan club"} 181 | {"_id":{"$oid":"5f3ee4e22ec07200155a126a"},"name":"yung gucci tooth fairy"} 182 | {"_id":{"$oid":"5f447f36cee97b00156af288"},"name":"tim “the juul man” taylor"} 183 | {"_id":{"$oid":"5f592d4e4c954f00153bb48e"},"name":"patagucci papi"} 184 | {"_id":{"$oid":"5f7aa9132aaed70015d4bbf0"},"name":"halloween themed ken wheeler name"} 185 | {"_id":{"$oid":"5f8d1e14f9bcd100151fb8d1"},"name":"hotel cancelvania 3"} 186 | {"_id":{"$oid":"5f8d808106431800155355cc"},"name":"yung werewolf bite"} 187 | {"_id":{"$oid":"5f9a5b23b8e28f00154b4f42"},"name":"significantly less attractive drake"} 188 | {"_id":{"$oid":"5f9e99f8a0ddd80015865d4d"},"name":"dracula.count()"} 189 | {"_id":{"$oid":"5f9ff583d9b17a0015ed9bd0"},"name":"tech game rob corddry"} 190 | {"_id":{"$oid":"5faa2d25188baf0015884695"},"name":"holiday inn express total landscaping"} 191 | --------------------------------------------------------------------------------