[
  {
    "path": ".all-contributorsrc",
    "content": "{\n  \"files\": [\n    \"README.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"contributors\": [\n    {\n      \"login\": \"proful\",\n      \"name\": \"Proful Sadangi\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/354596?v=4\",\n      \"profile\": \"https://github.com/proful\",\n      \"contributions\": [\n        \"code\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"tr0mbl3y\",\n      \"name\": \"tr0mbl3y\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/72851843?v=4\",\n      \"profile\": \"https://github.com/tr0mbl3y\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"PuruVJ\",\n      \"name\": \"Puru Vijay\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/47742487?v=4\",\n      \"profile\": \"https://puruvj.dev\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"johnjacobkenny\",\n      \"name\": \"Kenny John Jacob\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/19240564?v=4\",\n      \"profile\": \"https://kennyj.me/\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Mira-Alf\",\n      \"name\": \"Mira-Alf\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/64691316?v=4\",\n      \"profile\": \"https://github.com/Mira-Alf\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Akshay2996\",\n      \"name\": \"Akshay Sharma\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/37118877?v=4\",\n      \"profile\": \"https://www.developeratease.com/\",\n      \"contributions\": [\n        \"code\",\n        \"design\"\n      ]\n    },\n    {\n      \"login\": \"shekhar10feb\",\n      \"name\": \"Shekhar Ranjan\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/72906055?v=4\",\n      \"profile\": \"https://codepen.io/shekhar4nov\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"tolgaerdonmez\",\n      \"name\": \"Tolga Erdönmez\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/45141388?v=4\",\n      \"profile\": \"http://tidible.app\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"UnevenCoder\",\n      \"name\": \"Ameen Shafeeq\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/49345531?v=4\",\n      \"profile\": \"https://m.youtube.com/channel/UCKmIFs7rFKdTE6t1y8bKAHQ/videos\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"SarveshKadam\",\n      \"name\": \"Sarvesh Kadam\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/66166738?v=4\",\n      \"profile\": \"https://www.sarveshkadam.com/\",\n      \"contributions\": [\n        \"code\"\n      ]\n    }\n  ],\n  \"contributorsPerLine\": 7,\n  \"projectName\": \"twindle\",\n  \"projectOwner\": \"twindle-co\",\n  \"repoType\": \"github\",\n  \"repoHost\": \"https://github.com\",\n  \"skipCi\": true\n}\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Description\n\n\n## Attach Screenshot\n\n\n> Note 2 code reviewer approval needed. Approach in twitter group & discord channel.\n"
  },
  {
    "path": ".github/TEMP_ISSUE_TEMPLATE.md",
    "content": "# Temporarily disabled\n\n\n\n<!--\nIF SUFFICIENT INFORMATION IS NOT PROVIDED VIA THE FOLLOWING TEMPLATE THE ISSUE MIGHT BE CLOSED WITHOUT FURTHER CONSIDERATION OR INVESTIGATION\n-->\n> Please provide us with the following information:\n> ---------------------------------------------------------------\n\n### This issue is for a: (mark with an `x`)\n\n- [ ] Merge conflict solution\n- [ ] feature request\n- [ ] documentation issue or request\n- [ ] regression (a behavior that used to work and stopped recently)\n\n\n### Minimal steps to reproduce\n>\n\n### Expected/desired behavior\n>\n\n### Mention any other details that might be useful\n\n> ---------------------------------------------------------------\n> Thanks!\n"
  },
  {
    "path": ".github/workflows/conflict.yml",
    "content": "name: Add label on merge conflict\non:\n  pull_request_target:\n    branches: [ main ]\njobs:\n  add-label:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Label Conflicting Pull Requests\n        uses: eps1lon/actions-label-merge-conflict@v2.0.0\n        with: \n          dirtyLabel: \"Merge Conflict\"\n          repoToken: \"${{ secrets.GITHUB_TOKEN }}\"\n          commentOnDirty: \"This pull request has conflicts, which you need to resolve! :partying_face: Don't need to worry at all, please follow the steps here - https://github.com/twindle-co/twindle/wiki/Resolving-merge-conflicts\"\n          commentOnClean: \"Conflicts have been resolved. A maintainer will review the pull request shortly.\"\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Mark stale issues and pull requests\n\non:\n  schedule:\n  - cron: \"0 0 * * *\"\n\njobs:\n  stale:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/stale@v1\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        stale-issue-message: 'This issue is getting old :older_adult:'\n        stale-pr-message: 'This PR is getting old :older_adult:'\n        stale-issue-label: 'no-issue-activity'\n        stale-pr-label: 'no-pr-activity'\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Node.js Tests\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [10.x, 12.x, 14.x]\n    steps:\n    - uses: actions/checkout@v2\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v1\n      with:\n        node-version: ${{ matrix.node-version }}\n \n    - uses: actions/cache@v2\n      with:\n        path: ~/.npm\n        key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}\n        restore-keys: |\n          ${{ runner.os }}-node-\n\n    - name: NPM Install\n      working-directory: ./twindle-cli\n      run: npm install\n    - name: Run Tests\n      working-directory: ./twindle-cli\n      run: npm run test\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\nyarn.lock\n\n# Confidential email config\nnodemailer.config.js\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and *not* Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Environment variables\n.env\n.DS_Store\nmain\n\ntwindle-web/scss/\ntwindle-web/style.scss\ntwindle-web/style.css.map"
  },
  {
    "path": "CNAME",
    "content": "www.twindle.co"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 twindle-co\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# [Twindle](https://twindle-co.github.io/twindle/.) 📖\n<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->\n[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors-)\n<!-- ALL-CONTRIBUTORS-BADGE:END -->\n\n> ### An open source project for beginners.\n>\n> ### Converting twitter threads to pdf, epub and mobi format to be read by Kindle.\n\n[![Open Source Love](https://firstcontributions.github.io/open-source-badges/badges/open-source-v1/open-source.svg)](https://github.com/firstcontributions/open-source-badges) [![Pull Requests Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n![image](https://visitor-badge.glitch.me/badge?page_id=Twindle-co.twindle)\n\n### Hangout with Twindle developer => Discord [<img src='https://cdn.vox-cdn.com/thumbor/nU3aFhQTGn1z9ImiSHXkF0bnyLk=/0x0:1600x1600/1400x1400/filters:focal(659x770:915x1026):format(jpeg)/cdn.vox-cdn.com/uploads/chorus_image/image/56262027/discord_logo.0.jpg' height=40 width=40 />](https://discord.gg/jBj2zMR)\n\n\n## Want to contribute on twindle cli\n\nPre-requsites\n\n- JavaScript\n- Node.js (basic)\n\nInitial code goes into twindle-cli\n\n```\ncd twindle-cli\nnpm install\ncp .env.example .env\n```\n*Note:* Create .env file under twindle-cli\n\nYou need Twitter Developer API bearer token ([instructions](https://github.com/twindle-co/twindle/wiki/Applying-for-Developer-Access-from-Twitter))\n\n# How to Run twindle?\n```\nnode . -i 1002103360646823936\n```\n\n## Reading sources suppoorted\n✅ Twitter Thread\n\n✅ Hacker News Comments\n\n✅ Github markdown pages\n\n✅ Any articles (URL)\n\n## Connect with us\n\n[<img src='https://www.creativefreedom.co.uk/wp-content/uploads/2017/06/Twitter-featured.png' height=45 width=55 />](https://twitter.com/twindleco)\n[<img src ='https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQ342VRbRlgLDPviYYJgxfCVEHKmtuV8LIisA&usqp=CAU'  width=50 height=50 />](https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA)\n[<img src='https://cdn.vox-cdn.com/thumbor/nU3aFhQTGn1z9ImiSHXkF0bnyLk=/0x0:1600x1600/1400x1400/filters:focal(659x770:915x1026):format(jpeg)/cdn.vox-cdn.com/uploads/chorus_image/image/56262027/discord_logo.0.jpg' height=50 width=50 />](https://discord.gg/jBj2zMR)\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/proful\"><img src=\"https://avatars2.githubusercontent.com/u/354596?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Proful Sadangi</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=proful\" title=\"Code\">💻</a> <a href=\"#ideas-proful\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n    <td align=\"center\"><a href=\"https://github.com/tr0mbl3y\"><img src=\"https://avatars2.githubusercontent.com/u/72851843?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>tr0mbl3y</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=tr0mbl3y\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://puruvj.dev\"><img src=\"https://avatars2.githubusercontent.com/u/47742487?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Puru Vijay</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=PuruVJ\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://kennyj.me/\"><img src=\"https://avatars1.githubusercontent.com/u/19240564?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Kenny John Jacob</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=johnjacobkenny\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://github.com/Mira-Alf\"><img src=\"https://avatars0.githubusercontent.com/u/64691316?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Mira-Alf</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=Mira-Alf\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://www.developeratease.com/\"><img src=\"https://avatars0.githubusercontent.com/u/37118877?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Akshay Sharma</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=Akshay2996\" title=\"Code\">💻</a> <a href=\"#design-Akshay2996\" title=\"Design\">🎨</a></td>\n    <td align=\"center\"><a href=\"https://codepen.io/shekhar4nov\"><img src=\"https://avatars0.githubusercontent.com/u/72906055?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Shekhar Ranjan</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=shekhar10feb\" title=\"Code\">💻</a></td>\n  </tr>\n  <tr>\n    <td align=\"center\"><a href=\"https://github.com/tolgaerdonmez\"><img src=\"https://avatars0.githubusercontent.com/u/45141388?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Tolga Erdönmez</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=tolgaerdonmez\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://m.youtube.com/channel/UCKmIFs7rFKdTE6t1y8bKAHQ/videos\"><img src=\"https://avatars0.githubusercontent.com/u/49345531?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Ameen Shafeeq</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=UnevenCoder\" title=\"Code\">💻</a></td>\n    <td align=\"center\"><a href=\"https://www.sarveshkadam.com/\"><img src=\"https://avatars1.githubusercontent.com/u/66166738?v=4\" width=\"100px;\" alt=\"\"/><br /><sub><b>Sarvesh Kadam</b></sub></a><br /><a href=\"https://github.com/twindle-co/twindle/commits?author=SarveshKadam\" title=\"Code\">💻</a></td>\n  </tr>\n</table>\n\n<!-- markdownlint-enable -->\n<!-- prettier-ignore-end -->\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n"
  },
  {
    "path": "misc/CONTRIBUTING.md",
    "content": "# How to contribute\n\nYou can contribute by first forking the repository and then,\n- Creating Pull Requests\n- Creating Issues\n- Reviewing Pull Requests\n\n## Contributing through PRs\n\nYou should keep these points in mind while contributing\n- Any changes in the documentation are most welcome\n- For convenience it's recommended that you don't change more than 2 or 3 files per PR\n- You should respect Twindle contributors' coding style\n- Any other obvious rules must be followed\n- Pull requests of all kind are accepted. Just make sure you provide all the changes with reason in comments after opening the PR\n\n\n## Contributing through Issues\n\n- Make sure that there is not similiar issue available already, if so please comment any other similiar issue there itself\n- No harsh language allowed.\n- Do not hesitate to ask questions.\n\n## Contributing in firstpr folder\n\n- Make sure to use lowercase letters and avoid use of numbers or special characters while naming your files.\n"
  },
  {
    "path": "misc/docs/CHEATSHEET.md",
    "content": "\n![Markdown cheatsheet](./images/markdown.png)\n\n![git cheatsheet](./images/git.png)\n\n\n![Terminal commands cheatsheet](./images/term.jpg)\n"
  },
  {
    "path": "misc/docs/CODE-REVIEW-CHECKLIST.md",
    "content": "# List down things to look for in a PR \n\n### Code Review Checklist\n\n- [x] For your first PR, please use all lower case for the file name.\n- [x] Please use `firstname.md` as file name. Replace `firstname` with your actual first name.\n- [x] In case there is already a file with same name, then use `firstname_lastname.md`.\n- [x] Using `.md` file extension is a must.\n- [x] Please add some basic bio about yourself in the first PR.\n- [x] For the second PR, you can update the same file and add more details.\n- [x]  Approve PR carefully as they may not show any merge conflict but still there can be some error.\n- [x] Check for repeated names too in `TEAM.md` as sometimes the same names are getting repeated 1-2 times.\n\n- [x] You need to create your second PR from your local machine by cloning your the forked repo and saying    few things about yourself in the doc folder. \n- [x] Then create your PR again from your local machine this time you have update your forked repo from the main repo. \n- [x] After that you state your experience whether you are a beginner ,intermediate or mentor in the TEAM.md file in doc folder.\n- [x] if you having any difficulties in creating the second and third PR click this https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA to help"
  },
  {
    "path": "misc/docs/FAQ.md",
    "content": "# FAQs\n\n1. [General](#general-)\n2. [Git](#Git-)\n3. [Github](#github-octocat)\n4. [Google Meet](#google-meet-)\n5. [Logo](#logo-)\n\n### General 👥\n\n**What is our tech stack?**\n\n- HTML, CSS & JavaScript.\n- No framework.\n- No extra dependency.\n\n**How tasks will be distributed?**\n\n- We will came up with a process to distribute tasks. 🕑\n- Basically, there will be 4 parts:\n  A) Beginner\n  B) Independent\n  C) Active\n  D) Expert\n- Tasks will be distributed accordingly in small groups.\n\n**When will we start working on this?**\n\n- We can start within a week because there are many beginners who would need assistance. Hang on with us :)\n\n**Is there any pre-requisite to be a part of Twindle?**\n\n- No, you can contribute to the project in these ways:\n  Team: Helping each other, speaking up, pair programming, making and sharing videos (for this project).\n  Github: Updating the first PR and README.md, posting questions, or discussing in the issues created.\n\n### Git\n\n**What is git branching in git?**\n\nGit branches are alternative working directory system within git repository.\nIn essence, they are the backbone of version control and code management feature provided by git.\nHere's a video explaining the git branch in short [![tutorial](https://img.youtube.com/vi/PipalhI9yWY/0.jpg)](https://youtu.be/PipalhI9yWY)\n\n### Github :octocat:\n\n**What to do next after creating a GitHub account?**\n\n- Fork the project from this [link](https://github.com/twindle-co/twindle)\n- Create the first PR.\n\n**How to make a PR(Pull Request)?**\n\n- For the first 3 tasks after you joined, you can check the [ONBOARDING page](https://github.com/twindle-co/twindle/blob/main/docs/ONBOARDING.md).\n- For a general PR, this tutorial will walk you throught using VSCode and GUI:\n\n[![tutorial](https://img.youtube.com/vi/ZQqfZt1RIpA/0.jpg)](https://youtu.be/ZQqfZt1RIpA)\n\n- PR needs 2 code reviewer in order to be approved, if you made a PR, copy the link and share it into the Twitter group so people can assign it to themselves and approve it.\n- We have a Github Team for all the members and more and more contributions are welcomed.\n\n**Tips for your PR**\n\n`close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved`\n\ne.g. `closes #102, closes #33, closes #14`\n\ne.g. `closes #102 #33 #14`\n\n### Google Meet 📹\n\n**Rules**\n\n- Mute yourself\n- Unmute only if you are speaking\n- Stay in a noise-free environment\n- If you are in a noisy environment don't speak\n\n**What will be the Google Meet timing?**\n\n- 5 pm IST\n\n**How to know what happened during that Google meet if I missed?**\n\n- Someone will take notes and share it with the team.\n- You can also watch the videos on this [list](https://youtu.be/i90UeTiEKQk) of the YT channel.\n\n### Logo 🎨\n\n**Where to make the logo from?**\n\n- May use free platforms like Canva, Gimp, or Inkspace\n- Use photoshop (if you already have the software)\n\n**How to share the logo I made?**\n\n- Comment on the issue **#4 \"We need a logo\"** raised by johnjacobkenny\n- Share in the respective twitter group you belong to.\n\n**Is it necessary to make a logo?**\n\n- No, it is just a part of the project. So, if you are not comfortable with it, its okay.\n"
  },
  {
    "path": "misc/docs/NAMING-CONVENTIONS.md",
    "content": "# File Name Conventions\n\n## Possible characters\n\n### Use dashes as delimiters\n\n- You should use dashes (-) as delimiters.\n- Periods are allowed in some cases, such as for languages and conditions.\n- Never use spaces or underscores. Spaces are converted to %20 in URLs or can break an URL when shared. Underscores are difficult to see when the file name is displayed as an underlined link. Although the use of underscores does not impact your ranking that much, [Google advices not to use underscores](https://www.youtube.com/watch?v=AQcSFsQyct8).\n\n\n**Right:**\n```\nfile-name-with-dashes.en.min.html\n```\n\n### Do not use special characters\n\nAvoid using non-alphanumeric characters in file names, such as: '*' ':' '\\' '/' '<' '>' '|' '\"' '!' '?' '[' ']' ';' '=' '+' '&' '£' '$' '€' '%' or ','. These characters can have special meaning in programming languages or can cause problems with different operating systems.\n\n### Use lowercase, never uppercase\n\nWe should always consider URLs as case-sensitive according to [W3.org](http://www.w3.org/TR/WD-html40-970708/htmlweb.html). Therefore, use lowercase to reduces errors when typing URLs.\n\n# Commit Message Naming\n\nConsist of two parts:\n- Subject: Short informative summary of the commit\n- Body: More detailed explanatory text if needed\n\n## Subject:\n- Short and descriptive (max 50 chars)\n- Capitalized\n- In imperative present tense\n- Not end with period\n\nExample:\n```\nImplement access right management\n```\n\n## Body:\n- Separated with a blank line from the subject\n- Explain what, why, etc.\n- Max 72 chars\n- Each paragraph capitalized\n\nExample:\n```\nImplement proper authorization for each service on development phase to validate during the API call.\n\nAccess right management is used to check proper authorization to access an API by an employee or the employer.\n```\n\n# Pull Request Naming\n\nConsists of two parts:\n- Title: Short informative summary of the pull request\n- Description: More detailed explanatory text describing the PR for the reviewer\n\n## Subject:\n- Short and descriptive summary\n- Start with corresponding ticket/story id (GitHub issue)\n- Should be capitalized and written in imperative present tense\n- Not end with period\n\nSuggested Format:   \n*#[Ticket_ID] PR description*\n\nExample:\n```\n#CLS-23 Add Edit on Github button to all the pages\n```\n\n## Description:\n- Separated with a blank line from the subject\n- Explain what, why, etc.\n- Max 72 chars\n- Each paragraph capitalized\n\nExample:\n``` \nThis pull request is part of the work to make it easier for people to contribute to naming convention guides. One of the easiest way to make small changes would be using the Edit on Github button.\n\nTo achieve this, we needed to:\n- Find the best Gitbook plugin which can do the work\n- Integrate it in all the pages to redirect the user to the right page on GitHub for editing\n- Make it visible on the page so users can notice it easily\n```\n"
  },
  {
    "path": "misc/docs/ONBOARDING.md",
    "content": "# I joined! What to do next?\n\nWelcome to the twindle team 🥳 , this is a beginner friendly project, please follow these steps to start!\n\n\n## Connect with the team 📱\n\nOnce you are part of the twindle team, please join us here to keep in touch.\n\n1. Follow us in twitter: [<img src='https://cdn2.iconfinder.com/data/icons/colorful-guache-social-media-logos-1/155/social-media_twitter-512.png' height=20 width=20 />](https://twitter.com/twindleco)\n\n2. Subscribe to YT channel: [<img src ='https://cdn2.iconfinder.com/data/icons/colorful-guache-social-media-logos-1/157/social-media_youtube-512.png'  width=20 height=20 />](https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA)\n\n3. Join discord [<img src ='https://cdn2.iconfinder.com/data/icons/colorful-guache-social-media-logos-1/159/social-media_discord-alt-512.png'  width=20 height=20 />](https://discord.com/invite/jBj2zMR)\n\n<br>\n\n## Start working 👩🏽‍💻👨🏽‍💻\n\n1. Fork Twindle's github [repository](https://github.com/twindle-co/twindle)\n\n2. Do your first PR\n\n**What is the first PR about?**\n- Create a new file `firstname_lastname.md` inside the `firstpr` directory, commit your change and raise your first Pull Request through Github.\n\n**How to do the first PR?**\n- Pictures of step by step [here](https://github.com/twindle-co/twindle/issues/57)\n- Video of PR steps:   \n\n[![tutorial](https://img.youtube.com/vi/bzaBiQQl6fU/0.jpg)](https://youtu.be/bzaBiQQl6fU).\n\n\n-----\n\n3. Do your second PR\n\n**What is the second PR about?** 2️⃣\n- Make few changes in the file `firstname_lastname.md` inside the `firstpr` directory, `add`, `commit` and `push` your changes through the CLI (command line interface) and raise your second Pull Request. \n\n**How to do the second PR?**\n- If you use Github Desktop, check this step by step [tutorial](https://github.com/twindle-co/twindle/issues/177).\n- If you are using git bash/command prompt, chick this step by step [tutorial](https://github.com/twindle-co/twindle/issues/163) with screenshots or this [tutorial](https://github.com/twindle-co/twindle/issues/156) which has the commands documented.\n- If you are using terminal(Mac) or command prompt(windows), watch this step by step video:\n\n[![tutorial](https://img.youtube.com/vi/7I9StcZt5cI/0.jpg)](https://youtu.be/7I9StcZt5cI).\n\n<br>\n\n[![tutorial](https://img.youtube.com/vi/bPBTumdMhyQ/0.jpg)](https://youtu.be/bPBTumdMhyQ)\n\n<br>\n\n---\n\n-----\n\n4. Do the third PR\n<br>\n\n**What is the third PR about?**\n- Make few changes in the file `TEAM.md` inside the `docs` directory, commit, through the CLI (command line interface) and raise your third Pull Request.  \n\n\n**How to do the third PR?**\n\n- [Step by Step tutorial](https://github.com/twindle-co/twindle/issues/226)\n- Video of the tutorial using vsCode and GitLens:   \n\n[![here](https://img.youtube.com/vi/U2bOwEY-vKo/0.jpg)](https://youtu.be/7I9StcZt5cI).\n\n\n---\n\n5. Do the fourth PR\n\n**What is the fourth PR about?**\n- Upload your profile image into /docs/images/team folder.\n\n- Give the name same as your first pr file name & use extension jpg, jpeg or png e.g., 'proful.png'.\n\n- Edit TEAM.md to link to your image. Someone experienced with markdown come up with an initial design (size & placement). Other follow.\n\nThis will improve your knowledge of merging. \n\n**How to do the fourth PR?**\n\n- Follow the steps for third PR and add your image to the TEAM.md\n\n<br>\n\n---\n\nFor further questions, check the [FAQ](https://github.com/twindle-co/twindle/blob/main/docs/FAQ.md) documentation.\n=======\n"
  },
  {
    "path": "misc/docs/README.md",
    "content": "# Documentation\n\n## Table of Contents\n\n- [FAQs](/docs/FAQ.md)\n- [Naming Conventions](/docs/NAMING-CONVENTIONS.md)\n- [Resources](/docs/RESOURCES.md)\n- [Teams](/docs/TEAM.md)\n- [Why Use Twindle?](/docs/TWINDLE.md)\n- *Google Meet Link* -  https://meet.google.com/gna-hvym-tfj  **Time 5 PM IST** [Time Zone Converter](https://time.is/compare/1700_in_IST)  \n"
  },
  {
    "path": "misc/docs/RESOURCES.md",
    "content": "# Resources\n\nThis page contains the list of resources that can help you get started.\n\nYou can also contribute by providing useful resources from **Youtube or Blogs** to help others learn & understand the basic concepts.\n\n### Blogs\n\n- This is a simple guide about how to start with git. **Roger Dudler.** [git - the simple guide](https://rogerdudler.github.io/git-guide/index.html)\n- Free GitHub Learning Labs A to Z of Web Development including (Git and Github)🐱‍🏍 👉[Article Link🌊](https://dev.to/krishnakakade/free-github-learning-labs-a-to-z-of-web-development-3501) by **krishnadevz** \n- Sync a GitHub Repo: How To Ensure Your GitHub Fork Is Up To Date. **Earthdatascience.org - by Leah Wasser, Jenny Palomino, Max Joseph** [Article Link](https://www.earthdatascience.org/courses/intro-to-earth-data-science/git-github/github-collaboration/update-github-repositories-with-changes-by-others/#:~:text=You%20can%20update%20your%20fork,be%20updated%20in%20your%20fork)\n- [How to publish packages to npm (the way the industry does things)](https://zellwk.com/blog/publish-to-npm/)\n\n\n### Youtube Links\n\n- Get started with the most popular Code Editor [VsCode](https://www.youtube.com/watch?v=WPqXP_kLzpo&t=382s)\n- Learn the fundamentals of [JavaScript](https://www.youtube.com/watch?v=W6NZfCO5SIk&t=1s)\n- Get started with the new features of [JavaScript](https://www.youtube.com/watch?v=hdI2bqOjy3c) like ES5, ES6 features & more.\n- Learn about [Asynchronous JavaScript](https://www.youtube.com/playlist?list=PL4cUxeGkcC9jx2TTZk3IGWKSbtugYdrlu) and its advance features.\n- Get yourself started with [Node.js](https://www.youtube.com/watch?v=fBNz5xF-Kx4)\n- Learn [Git & Github](https://www.youtube.com/watch?v=RGOj5yH7evk&t=339s)\n- How to create PDFs using Puppeteer & Handlebars with HTML, watch this [video](https://youtu.be/llkkwRABN-s)\n- Configure git bash and complete your second pull request. [Video](https://www.youtube.com/watch?v=cBigFcoAXZg)\n- Software Engineering is Overwhelming [Video](https://www.youtube.com/watch?v=MbDjrztWtX4)\n- GitHub Actions Tutorial - Basic Concepts and CI/CD Pipeline with Docker [Video](https://www.youtube.com/watch?v=R8_veQiYBjI)\n\n\n### Github Repositories for learning\n\n- [Best practices for Nodejs](https://github.com/goldbergyoni/nodebestpractices)\n- [You don't know JavaScript book for have strong fundamentals](https://github.com/getify/You-Dont-Know-JS)\n- [Best Practices for Clean Code in JavaScript](https://github.com/ryanmcdermott/clean-code-javascript)\n\n"
  },
  {
    "path": "misc/docs/TEAM.md",
    "content": "## Beginner\n\n| Image                                                                      | Name                                                         | Description                                                                                                                                                                                                                                                                                                                                                                                                |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| <img src=\"./images/team/pranav.jpg\" width=\"80px\">                          | [Pranav Goel](https://github.com/pranavgoel29)               | I'm from India, Have basic knowledge of HTML, CSS, C++ and C, I'm currently learning Javascript.                                                                                                                                                                                                                                                                                                           |\n|                                                                            | [Adewale Abiola](https://github.com/princehaybee)            | I am a Nigerian. I have basic knowledge of HTML,CSS and Javascript.                                                                                                                                                                                                                                                                                                                                        |\n| <img src=\"./images/team/ad3rinto.jpg\" width=\"80px\">                        | [Micheal Aderinto](https://github.com/ad3rinto)              | Manchester UK, Hoping this will be my first step in the world of solving world problems with code.                                                                                                                                                                                                                                                                                                         |\n| <img src=\"./images/team/kiran_don.jpg\" width=\"80px\">                       | Usha Kiran ([K!r@nD0n](https://twitter.com/ushakiran_m))     | I am a student from India. I just started my OpenSource journey with TWINDLE. I am currently learning #JavaScript.                                                                                                                                                                                                                                                                                         |\n| <img src=\"./images/team/eyram.jpg\" width=\"80px\">                           | [Eyram ](https://twitter.com/Ey_kwasi)                       | I am from Ghana and I have no experience, started learning web development some months ago. I am participating to learn and also make twindle a success.                                                                                                                                                                                                                                                   |\n| <img src=\"./images/team/michael.png\" width=\"80px\">                         | [Michael Osajeh](https://github.com/michaelcosj)             | I am from Lagos, Nigeria, second year Computer Science Undergraduate, started learning web and mobile development this year. I have basic knowledge of javascript, python, c++, dart and golang.                                                                                                                                                                                                           |\n|                                                                            | [Tushar Kandpal](https://github.com/tusharkandpal)           | From India, Contributing in making **twindle** a succees, Always a Learner :v:                                                                                                                                                                                                                                                                                                                             |\n| ![avatar](https://github.com/RafaelBatman55.png?size=80)                   | [Rafael Rodrigues](https://github.com/RafaelBatman55)        | Less than 1 yr of experience From small city in Rio de Janeiro State - Braz.I'm very excited to learn and helf in this project!                                                                                                                                                                                                                                                                            |\n|                                                                            | [Yaksh Bariya](https://www.github.com/thunder-coding)        | I am from India. A 14 year old technology lover. I create amazing things using the knowledge I have gained so far. Currently, I want to explore more of JavaScript/TypeScript.                                                                                                                                                                                                                             |\n|                                                                            | [Nishank Navelkar](https://www.github.com/nishanknavelkar)   | Im from India, Basic knoweledge of Python, C, C++, HTML, CSS, Electrical engineer, Here to learn and Contribute.                                                                                                                                                                                                                                                                                           |\n|                                                                            | [Manohar Rajaram](https://github.com/manohar52)              | I am from India but currently pursuing my Masters in computer science in US. I am quite new to Javascript. I have some experience in HTML, CSS and PHP.                                                                                                                                                                                                                                                    |\n|                                                                            | [Naveen Kala](https://github.com/naveenkala)                 | I'm an engineering student from India. Having basic knowledge of HTML, CSS and JavaScript. Currently I am learning Reactjs.                                                                                                                                                                                                                                                                                |\n|                                                                            | [Alasi Habeeb](https://github.com/Holaryeankar007)           | I'm from Nigeria, I have experience in HTML and CSS but i'm currently learning Javascript.                                                                                                                                                                                                                                                                                                                 |\n|   ![avatar](https://github.com/Sheetal777.png?size=80)                                                                        | [Sheetal Pandey](https://github.com/Sheetal777)              | A keen learner from India pursuing engineering. Good with Python and new to Web Developement.                                                                                                                                                                                                                                                                                                              |\n| <img src=\"./images/team/mumbi.jpg\" width=\"80px\">                           | [Regina Gachomba](https://github.com/MumbiGachomba04)        | I am a Kenyan undergraduate student currently based in Ankara, Turkey. During my first year in college I learnt C,C++ and python. I am currently learning Java OOP for my coursework and teaching myself HTML,CSS and Javascript. Looking forward to master front-end development at the moment, then hopefully I can begin learning back-end.                                                             |\n|                                                                            | [Swati Rao](https://github.com/SwatiRaoDataEnthusiast)       | I am from India,I am familiar with HTML,CSS and can learn Node.JS, javascript etc. This is the my first open source project TWINDLE. Really excited!!                                                                                                                                                                                                                                                      |\n|                                                                            | [Prathap](https://github.com/codeprm)                        | From India. Newbie to UI tech. CS Student, 4+ exp.                                                                                                                                                                                                                                                                                                                                                         |\n| ![avatar](https://github.com/lh536.png?size=80)                            | [Fabio Duarte](https://github.com/lh536)                     | I am from Colombia. I have some experience with HTML, CSS and JavaScript. Lately I've been learning Tailwindcss, ParcelJS, NodeJS and a first lesson of VueJS. I like listen to music (play guitar and drums), airplanes, cooking, video/photography and science; actually I'm biologist, but I love coding since I was in the school. I hope I can contribute to twindle. I'm always learning.            |\n|                                                                            | [Nahuel Alfano](https://github.com/NahuelAlfano)             | I am from Argentina, beginner at github and open source projects. I have basic knowledge of HTML, CSS, Javascript, C#, C++, currently learning Python.                                                                                                                                                                                                                                                     |\n|                                                                            | [Tr0mbley](https://github.com/tr0mbl3y)                      | learning how things works! nothing to share much                                                                                                                                                                                                                                                                                                                                                           |\n|                                                                            | [Sippeybro](https://github.com/sippeybro)                    | I'm from Maldives, Have basic knowledge of HTML, CSS, php and I'm currently learning Javascript.                                                                                                                                                                                                                                                                                                           |\n|                                                                            | [Titi Olopade](https://github.com/teckiegeek)                | I am from the United Kingdom. My background is Computing and Statistics with a MSc. in Agile Project Management and PRINCE2 certified,i SCRUM Master Certified. I have several years professional experience with few years career break. I started learning HTML and CSS coding 6months ago. I plan a change in career to be a Front-end developer. Here to learn and contribute to success of \"twindle\". |\n|                                                                            | [luli](https://github.com/ululi)                             | I'm from zambia,have some basic knowledge of html, css and js, am here to learn more about web development.                                                                                                                                                                                                                                                                                                |\n|                                                                            | [Ashish](https://github.com/kambleaa007)                     | 3+ yr of experience in .Net Java JavaScript Testing, Pune, India SoReady2Help :partying_face:                                                                                                                                                                                                                                                                                                              |\n| <img src=\"./images/team/nailah.jpg\" width=\"80px\"              >            | [Nneyen ](https://github.com/Na-ilah)                        | I am from Nigeria. I am beginner at github and open source projects. I have basic knowlege of Python, HTML and CSS. I don't remember much about programming even if I have a degree in computing. I would like to be able to contribute to the open source community and build educational solutions.                                                                                                      |\n|                                                                            | [Harsha Vardhan](https://github.com/Harsha-Ambati)           | I am a student from India, recently came into this web development field, had basic knowledge in HTML, CSS, JavaScript.                                                                                                                                                                                                                                                                                    |\n|                                                                            | [Maria Rivera](https://github.com/GorettiRivera)             | I have basic knowledge of Javascript, HTML and CSS. Currently working in different open source projects                                                                                                                                                                                                                                                                                                    |\n|                                                                            | [Neaz Mahmood](https://github.com/Neaz-Mahmood)              | I am from Dhaka, Bangladesh. I am a second year Computer Science undergraduate. I know basic html, css, javascript. I am here to learn & contribute to 'twindle'. This is my first open source project.                                                                                                                                                                                                    |\n|                                                                            | [Predrag](https://github.com/stamenkovic-dev)                | I'm from Serbia, and I have only basic knowledge of HTML and CSS, but I'm here to learn more about web development and team play at coding.                                                                                                                                                                                                                                                                |\n|    <img src=\"./images/team/franklin.jpg\" width=\"80px\">                     | [Franklin U.O. Ohaegbulam](https://github.com/frankiefab100) | I'm comfortable working with HTML and CSS, with basic knowledge of JavaScript. I'm from Nigeria and love talking about accessibility, inclusive design and web animation.                                                                                                                                                                                                                                  |\n|                                                                            | [Aravind Sivasailam](https://github.com/aravindsivasailam)   | I'm a product engineer living in US with 2 years of experience in software testing and project management. New to software development and wish to improve in HTML, CSS and JS.                                                                                                                                                                                                                            |\n|                                                                            | [Edwin Mancipe](https://github.com/efmg0325)                 | I am from Colombia. I have a bit of knowledge about HTML, CSS, Javascript, Python and SQL. I love to listeninng to Heavy Metal music. I like second life web browser games.                                                                                                                                                                                                                                |\n|                                                                            | [Edori Atiri](https://github.com/EdoriAtiri)                 | From Nigeria. Began to learn Web Development this year. I hope to learn and contribute while I'm here.                                                                                                                                                                                                                                                                                                     |\n|                                                                            | [Lorenna Leon](https://github.com/lorennaleon)               | I am Peruvian, I have knowledge in html, css and javascripts I hope to learn a lot from you                                                                                                                                                                                                                                                                                                                |\n| <img src=\"https://avatars2.githubusercontent.com/u/58055517\" width=\"80px\"> | [Fusen Ye](https://github.com/Yexiansen66)                   | I'm a beginner from China. I know HTML,CSS and JavaScript.I want to learn more advanced knowledge and contribute to twindle.                                                                                                                                                                                                                                                                               |\n|                                                                            | [OmoloG](https://github.com/geowen74)                        | From Nairobi, Kenya. Have Knowledge on HTML, CSS and some basic on javascript and Mysql. Aspiring to be a full-stack developer. Currently learning javascript.                                                                                                                                                                                                                                             |\n| <img src=\"./images/team/shekharranjan.jpg\" width=\"80px\">                   | [Shekhar Ranjan](https://github.com/shekhar10feb)            | I'm from India, have basic knowledge of Java, SQL, HTML, CSS and still learning JavaScript, want to learn more and explore advance stuffs.                                                                                                                                                                                                                                                                 |\n| <img src=\"https://github.com/chiomavera.png?size=80\">                      | [Chioma Vera Nkanmuo](https://github.com/chiomavera)         | Hi,I'm Chioma Vera Nkanmuo, Beginner Web developer from Nigeria with basic knowledge of HTML and CSS. I am currently in my penultimate level in university. Learning while contributing in making twindle a success.                                                                                                                                                                                       |\n\n---\n\n<br/>\n\n## Intermediate\n\n| Image                                                                                                                                                                                                                                                          | Name                                                      | Description                                                                                                                                                                                                                                                                                                                 |\n| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|                                                                                                                                                                                                                                                                | [Tolga Erdönmez](https://github.com/tolgaerdonmez)        | I'm from Turkey. I'm a student. I've been programming for 4 years but no proffessional experience.                                                                                                                                                                                                                          |\n|                                                                                                                                                                                                                                                                | [Ameen](https://github.com/UnevenCoder)                   | 3+ yrs of experience ,From India , born & brought up in Jeddah [ I am 15 yrs old :)], An Extrovert & A Feeler :)                                                                                                                                                                                                            |\n|                                                                                                                                                                                                                                                                | [Krypton](https://github.com/madhusudanbabar)             | I'm an engineering student from India, passionate for web technologies, especially nuxtJS. I love blogging (technical articles & CTF walkthroughs mostly as I'm also interested in cyber security).                                                                                                                         |\n|                                                                                                                                                                                                                                                                | [Rakesh](https://github.com/Rakesh-4)                     | Software Developer have 3+ years of experience in C#, JavaScript and Angular Framework                                                                                                                                                                                                                                      |\n|                                                                                                                                                                                                                                                                | [Samriddhi](https://github.com/sammjainn)                 | I'm a student from India. Here to learn and work together!                                                                                                                                                                                                                                                                  |\n| <img src=\"./images/team/praveen.jpg\" width=\"80px\">                                                                                                                                                                                                             | [PraveenKumar](https://github.com/praveen2896)            | I'm from India, having 3 years of professional experience in Backend, new to JS,HTML,CSS ,Planning to learn React in future with the help of #twindle.                                                                                                                                                                      |\n|                                                                                                                                                                                                                                                                | [Vipin Rawat](https://github.com/aesthytik)               | I'm a frontend Engineer from Punjab,India. 2+ years experience in React Native and React js. I create Logos and graphics for fun.Happy to help :)                                                                                                                                                                           |\n| <img src=\"./images/team/satyaki.jpg\" width=\"80px\">                                                                                                                                                                                                             | [Satyaki Bose](https://github.com/satyaki07)              | I'm a newbie in MERN stack from Kolkata, India with 1+ years of experience. :)                                                                                                                                                                                                                                              |\n| ![avatar](https://github.com/Mira-Alf.png?size=80)                                                                                                                                                                                                             | [Aparna Sivasailam](https://github.com/Mira-Alf)          | I'm from Bengaluru, India. Tech woman on a career break with 6 years of Java Experience. Trying to upskill myself with Javascript and UI/UX design                                                                                                                                                                          |\n|                                                                                                                                                                                                                                                                | [Rajesh](https://github.com/rkumar1904)                   | I'm a Lead frontend developer from Mumbai, India. Total 9+ years experience in front end technology. Currently working on ReactJS & Flutter. Initially, I started my career as a Programming Faculty.                                                                                                                       |\n|                                                                                                                                                                                                                                                                | [Sunny](https://github.com/SunnyDev7)                     | I am Sunny from India and a engineering student. And i am working on MERN stack and also comfortable with html, css and vanilla js and am Here to learn, collaborate and help.                                                                                                                                              |\n| <img src=\"https://avatars0.githubusercontent.com/u/37118877?v=4\" width=\"80px\">                                                                                                                                                                                 | [Akshay Sharma](https://github.com/Akshay2996)            | I'm a Frontend Developer from Bangalore, India. Total 1+ years of experience in the industry. Contributing to Twindle to make it successful.                                                                                                                                                                                |\n|                                                                                                                                                                                                                                                                | [Karan Parsnani](https://github.com/LilFatFrank)          | From Mumbai, India. Here to learn and contribute.                                                                                                                                                                                                                                                                           |\n|                                                                                                                                                                                                                                                                | [Lukman Okunade](https://github.com/lokunade)             | From Lagos, Nigeria. I am joining the twindle open source to learn and contribute.                                                                                                                                                                                                                                          |\n|                                                                                                                                                                                                                                                                | [krishnadevz](https://github.com/lokunade)                | I am krishna 4 years of experience in web-development Current skills(HTML/CSS/JS/REACTJS/C/bootstrap4) from india currrently i am final year CS gradd student and Learning MERN✡ Stack Developer SelfTaught-Dev/Opensourcer🛠 #100DaysOfCode(R2) FOSS❤ I like to build things & Write things related to Web🌐⚙ #webdev 🐱‍💻 |\n| <img src=\"https://avatars1.githubusercontent.com/u/52439373?s=400&u=9f8fd9570b808befd2d3f8bdb033375d86c2fffa&v=4\" width=\"80px\">                                                                                                                                | [Melissa Huerta](https://github.com/piratelicorne)        | Hi! I'm Systems Engineer from Perú, 3+ yrs of experience in IT Infrastructure, I have knowledge in C and Javascript. I recently started in Development as Flutter Developer, currently working on side projects 👩🏽‍💻 and blogging for #WomenWhoCodeSV                                                                         |\n|                                                                                                                                                                                                                                                                | [Abdou Masoudi](https://github.com/uplancer)              | a selft-taught Web Developer.                                                                                                                                                                                                                                                                                               |\n|                                                                                                                                                                                                                                                                | [Viraj](https://github.com/viraj-patil)                   | I'm a full stack developer from Pune, India. Total 9+ years of experience in software industry as a developer.                                                                                                                                                                                                              |\n| <img src=\"https://res.cloudinary.com/practicaldev/image/fetch/s--Lt6uKVNG--/c_fill,f_auto,fl_progressive,h_320,q_auto,w_320/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/322705/1412670d-03f2-4342-bf66-483956dde97a.jpeg\" width=\"80px\"> | [Joel Vinay Kumar](https://github.com/JoelVinayKumar)     | I'm a FullStack Developer from Hyderabad, India. Worked on building microservices, web apps and mobile apps. I learned UI design out of passion. I'm proud of any small contribution to Twindle                                                                                                                             |\n| <img src=\"https://avatars2.githubusercontent.com/u/56103269?v=4\" width=\"80px\">                                                                                                                                                                                 | [Saurabh Srivastava](https://github.com/vasudeveloper001) | From India, 18 months Software Development Experience mainly in backend PHP development. Know about JavaScript, HTML, CSS, Nodejs, Python basics as well. Learning JAVA currently and starting a YouTube Channel on coding.                                                                                                 |\n| <img src=\"./images/team/nitin_kadam.jpeg\" width=\"80px\">                                                                                                                                                                                                        | [Nitin Kadam](https://github.com/ni3-kdm)                 | Full stack developer from Pune, India. 5+ years of experience in the web development 👩🏽‍💻.                                                                                                                                                                                                                                    |\n| <img src=\"./images/team/marcus.jpg\" width=\"80px\">                                                                                                                                                                                                              | [Marcus](https://github.com/mar-code)                     | I'm a self taught frontend developer from Lagos, Nigeria with experience in HTML/CSS, Javascript, React, Redux, bootstrap and d3 data presentation. i am currently learning NodeJs.                                                                                                                                         |\n|                                                                                                                                                                                                                                                                | [Samuel Adeniyi](https://github.com/samueladeniyi)        | I am proficient in HTML, CSS and Javascript. I am comfortable with react. I intend to become a fullstack developer with the MERN stack. I am from Nigeria.                                                                                                                                                                  |\n| <img src=\"./images/team/aditya.jpg\" width=\"80px\">                                                                                                                                                                                                              | [Aditya Satpute](https://github.com/Foxtrot-1418)                     | I'm a Full Stack Web Developer From Nashik, Maharashtra, India. I've experience in HTML, CSS, JS, Jquery, Bootstrap, PHP and currently working fo r Multinational Company as AWS Developer.                                                                                                \n\n\n| <img src=\"./images/team/aditya.jpg\" width=\"80px\">                                                                                                                                                                                                                                                           | [Samuel Adeniyi](https://github.com/samueladeniyi)        | I am proficient in HTML, CSS and Javascript. I am comfortable with react. I intend to become a fullstack developer with the MERN stack. I am from Nigeria.                                                                                              \n\n---\n\n<br/>\n\n## Mentor\n\n| Image                                                    | Name                                          | Description                                                                                        |\n| -------------------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------- |\n| <img src=\"./images/team/proful.png\" width=\"80px\">        | [Proful](https://github.com/proful)           | 15 yr of experience From India, Living in Sydney Actively participating to make twindle a success. |\n| ![avatar](https://github.com/johnjacobkenny.png?size=80) | Kenny John Jacob                              | 4+ yrs of experience. From Kerala, India. Here to help :partying_face:                             |\n|                                                          | Kingsley                                      | 3+ yrs of experience. Based in Nigeria and here to help :man_technologist:                         |\n|                                                          | [Pratham](https://github.com/prasoonpratham)  | 2+ experience in JavaScript, always open to help :smile:                                           |\n|                                                          | [Sarath](http://http://www.sarathnagaraj.ca/) | 6+ experience in JavaScript, Currently working on a side project using ReactJS :man_technologist:  |\n|![Puru's profile pic](https://github.com/puruvj.png?size=80) | [Puru Vijay](https://github.com/puruvj) | 5+ experience in HTML, CSS, JS, Angular, React, Svelte, NodeJS, Python, StencilJS, Firebase, feel free to ping me for help 😁 | \n\n"
  },
  {
    "path": "misc/docs/TWINDLE.md",
    "content": "# Why should twitter users consider Twindle to read through Twitter thread?\n\nBefore we dive into what Twindle aims to solve and why to consider Twindle as the first set of choices to read through informative threads on Twitter, let's take a look at the existing problem in reading through or following a long thread on Twitter.\n\n### The Problem\n\nTwitter threads are a common way, nowadays, to share informative content about diverse subjects quickly instead of writing up a blog post on Medium or a personal website. On every clock tick, the twitter feed volume grows exponentially; thus, keeping tracking of the threads that users find useful has become a painstaking process;\n\n### Existing Solutions\n\nWidely used approaches are to track is to retweet, like the tweet revisit later by scrolling through your likes feed. \n\nAlternatively, a Twitter bot named threadreaderapp was introduced to address the pain point by allowing users to retweet on the original post and get a link that combines all the information related to the thread in one place through a URL. Non-twitter users can also access the URL.\n\n### Limitations of threadreaderapp\n\nAlthough the threadreaderapp alleviates the pain point, there are some limitations in using the threadreaderapp bot as follows.\n    - If the thread owner blocks the threadreaderapp bot, it will not access the thread content.\n    - If the thread owner is a private user, the bot will not access the thread content.\n    - The threadreaderapp bot can only access up to 3000 tweets per user\n    - Threads get deleted from the app when they are deleted by the author or Twitter because the app site is in sync with Twitter. The only way to avoid this is a premium membership which allows threads to be saved as PDF files.\n    - Finally, the reader must connect to the internet to access the URL\n    \n### Twindle: Access thread content offline as well\nIn walks Twindle, a web application allows users to convert threads on Twitter to pdf or ePub format. \n    - The users can store the pdf/ePub on a digital device \n    - The users can store the pdf/ePub on cloud services i.e, Dropbox, Google Drive and access offline,\n    - Otherwise, print a hard copy and read like a good-old-traditional-reader. "
  },
  {
    "path": "misc/docs/TWITTER.md",
    "content": "# 18 OCT 2029\n\nHow to make a PR directly from vscode\n\n@Pranavgoel_29 #twindle\n\nhttps://www.youtube.com/watch?v=ZQqfZt1RIpA\n"
  },
  {
    "path": "misc/docs/articles/Process-for-new-contributors.md",
    "content": "Hi everyone :wave:, usually when people get connected to an open-source project(which is yes, pretty much exciting :open_mouth: ), they are mostly unware about the project. Sure, they have some knowledge about the project but not enough to contribute to it. So, they must have enough knowledge about the project and the process, like to do what or where should they first start, etc. \nThat's where this article comes in handy :metal: , as suggested by @Proful to write. So, let's go and try to know every process step by step. \n\n### Step 1\nFrom the [twindle-co](https://github.com/twindle-co/twindle) to get a copy of that github account in your account.\n\n### Step 2\nDownload [Git](https://git-scm.com/) and [connect the github account to it](https://linuxize.com/post/how-to-configure-git-username-and-email/).\n\n### Step 3\nFork Twindle repository by clicking on the `Fork` button on top right corner. This will make your own copy of Twindle's code, and it will be synced up to the upstream(i.e, `twindle-co/twindle` in this case). \n\n### Step 4\nClone the git repository from your github account into your local machine using git `bash`/ terminal (Most likely, it's git bash for windows users).There are different ways to clone a repository into your local machine, like you can use HTTPS, SSH or GitHub CLI, but this below will do it for everyone.\n```bash\ngit clone https://github.com/[YOUR_USERNAME]/twindle.git\n```\n\nReplace `[YOUR_USERNAME]` with your git username.\n\nThe whole repository will be copied in that folder.\n\n> :mushroom: Tip: Always remember to work or make changes in a new branch, for this, you have to create a new branch and then, work in it like make changes or add something.\n        \nWhat is Pull Request and why should we raise one? :confused: Pull Request(PR) is just you requesting from the authority, the original upstream branch, whose application you have forked, and in that forked application, you have done some changes or added some codes. To incorporate those changes in the main file, you have to raise a pull request(pr) to the authority. So, just making some meaningful changes and save it in your account won't be beneficial until those changes are added in to the main file. That's where pull request(pr) comes into the picture. \n\n### Step 5\nMake a new branch in your fork.\n\n> :mushroom: Tip: Before raising a pr from the main branch in order to merge your changes into the main file, always update your repository by pulling latest code from upstream branch by running this 👇\n\n```bash\ngit pull upstream main\n```\nAnd you can update your github account with the parent repository by following this amazing article written by @SarveshKadam [Sync to main repo using pull request](https://github.com/twindle-co/twindle/blob/main/docs/articles/sync-to-main-repo-using-pull-request.md).\n\n### Step 6\nNow, we are ready to raise our first PR in upstream. So, we need to do this by using our github account by following the steps given below:\n   \n- Switch to our new branch\n- Go to firstpr\n- Click on the add file button(on upper right hand side)\n- Click Create new file\n- Give a name of your file like yourName.md, where .md is the name of the markdown extension\n- Now, write about yourself in the space given below. Here, you have to use markdown language.\n   \n> :mushroom: Tip: Markdown Language is a simple markup language, one can use to format any doucment. If you want to learn basic syntax, you can go here :point_right: [Markdown Language](https://www.markdownguide.org/basic-syntax/).\n\n### Step 7\nNow, we are going for the 2nd pr but this time, we are raising a pr through our local machine. For this, again update your repo first in your both local as well\nas remote account and you should follow this, `twindle/docs/Team.md` in your local branch. This is about the team members, where one has to write his/her name with \ngithub account link and some description about himself/herself using markdown language. For pushing the changes from your local machine to your github account, you need to execute this \ncommand,\n\n```bash\ngit push origin [new-branch]\n```\nwhere, `origin` is the remote name. Please go here, if you want to know more [Gt Commands](https://github.com/twindle-co/twindle/blob/main/docs/articles/git%20-github-related.md).\n\n> :mushroom: Tip: If you are facing pr commit issue then, please go through this very nice article written by @PuruVJ [PR Clean Commits](https://github.com/twindle-co/twindle/blob/main/docs/articles/pr-clean-commits.md).\n\n### Step 8\n:sound: So, you are about to raise your 3rd pr. In the 3rd pr, you will add your photo in the `Team.md`. First, you need to upload your photo here, `twindle/docs/images/team` through new branch by raising pr. After merging the changes, update your repo(it's compulsory) and then, go to `twindle/docs/TEAM.md` in your new branch and edit the page, put this code `<img src=\"./images/team/yourName.jpg\" width=\"80px\">` in the image section in front of your name, according to the markdown syntax. \n\n> :mushroom: Tip: In between doing these things, you can write article also, where you can share simple approach of doing the steps, where you feel difficulties. This wiil surely give\n you confidence to express yourself and makes it easier for people to learn new things :relaxed: .\n \n### Step 9\nNow, you can select any of the section like either you can go to twindle-cli, twindl-web or twindle-thread according to your choice.\n\nSuggestions are welcome !! :blush:\n"
  },
  {
    "path": "misc/docs/articles/README.md",
    "content": "# Articles\n\nCreate an article here and add it as a link on the main project readme file. This will show up on the website as links.\n"
  },
  {
    "path": "misc/docs/articles/creating-first-pull-request.md",
    "content": "I hope the following screenshots will help with your first pull request.\n\nFirstly create your account on github and goto twindle repository page [here](https://github.com/twindle-co/twindle)\nNow you can follow the following steps.\n\nStep 1\n![1](https://user-images.githubusercontent.com/5336488/96012183-7b689480-0e61-11eb-94b9-289c30811210.png)\n\nStep 2\n![2](https://user-images.githubusercontent.com/5336488/96012298-9a672680-0e61-11eb-8aa5-2c35611d6a77.png)\n\nStep 3\n![3](https://user-images.githubusercontent.com/5336488/96012404-b10d7d80-0e61-11eb-82d0-8a1fa147f7cd.png)\n\nStep 4\n![4](https://user-images.githubusercontent.com/5336488/96012445-bc60a900-0e61-11eb-8305-08d03a22fc85.png)\n\nStep 5\n![5](https://user-images.githubusercontent.com/5336488/96012476-c4b8e400-0e61-11eb-818d-705dafbe5cc1.png)\n\nStep 6\n![6](https://user-images.githubusercontent.com/5336488/96012512-ce424c00-0e61-11eb-8a03-a4d9d9e12d57.png)\n\nStep 7\n![7](https://user-images.githubusercontent.com/5336488/96012535-d4382d00-0e61-11eb-9b2c-319407fa7758.png)\n\nStep 8\n![8](https://user-images.githubusercontent.com/5336488/96012559-da2e0e00-0e61-11eb-8b1d-c7cfd39bccee.png)\n\nStep 9\n![9](https://user-images.githubusercontent.com/5336488/96012586-e0bc8580-0e61-11eb-906f-5577f5c2b075.png)\n\nStep10\n![10](https://user-images.githubusercontent.com/5336488/96012608-e619d000-0e61-11eb-9869-84e739b4b808.png)\n\n\n\n\n"
  },
  {
    "path": "misc/docs/articles/design-guide-for-good-logo.md",
    "content": "There are some teams and logos you see, no matter where you are in the world, and you know exactly who they are and what they mean.\n\n## Hi everyone, this is Vipin, a frontend dev from Punjab, India with good knowledge of design and UI/UX.\n\nTwindle has got a lot of overwhelming response from the dev community and and begginers have also flooded their ideas for branding and logos.\n\n\nAs we are designing a logo, some things should be taken in my mind (of course there is no hard and fast rule 😅)\n\n# 1. LOGO DESIGN MUST REFLECT YOUR BUSINESS\n\nYour logo needs to help your business stand out in a crowd, but in a way where the person looking at it can easily identify that it is your unique company. If you can incorporate into the logo key elements that your business represents, you have a better chance of people remembering it. \n\n# 2. KEEP IT SIMPLE \n\nStop with all that clutter. Trying to squeeze too much into a logo can have a negative impact.\n\n# 3. MAKE A STATEMENT IN BLACK & WHITE AND COLOUR\n\nMake certain that your logo works well - both in colour and in black and white. \n\nIf the logo loses impact in black and white, it is going to be a problem when you have to print in newspapers or utilise services that only use black ink. Ensure that a colourful logo design translates well visually into black & white.\n\n# 4. A SCALABLE SOLUTION\n\nour logo has to be scalable to be effective. \n\nYour logo might look great on your letterhead, but what happens when you have to reduce the size for a business card or online forum? The rule of thumb when it comes to scalable logos, is that your logo should look good - whether on a business card or a billboard. If you can accomplish that, you have a great logo design.\n\n# 5. KEEP IT BALANCED\n\nYour logo should be designed to stand the test of time too. Ensure that your design does not make overt use of fashion trends - or you may find your logo is quickly dated. \n\nOnce you have decided what logo is perfect for your business, be sure to have it converted in JPG, GIF, and EPS format. This way you can easily use the logo for your printed materials and website.\n\nOnce you have all these key elements in place, we come to the most important part of a successful logo design and brand statement.\n\nNow that you have your logo, and it has been broadcast to the world, it is vital you always use your logo in a consistent manner. \n\nDon't alter your logo for certain flyers or brochures. Not only will this confuse your audience, it risks looking less than professional. You can reduce and enlarge the logo for different formats, but never change the essence of the design.\n\n"
  },
  {
    "path": "misc/docs/articles/git -github-related.md",
    "content": "Hello friends :wave:  :smiley: , when we talk about **git** and **github** then, there are lot of confusion :confused: exist in our mind. Especially, \nwhen we talk about their commands. I also encountered with the same issue and mentors helped me and motivate me to face this and gave me some suggestions \nin order to overcome this. And thanks to the mentors, who suggested me to write an article about it. So, here I am writing about some concepts, which \nare related to **github** and **git** and some commands(which you can use it in _git bash_, _cmd_ or _vscode terminal_) too, which, I hope, would be very \nhelpful for everyone.\n\nSo, here we are discussing about following terms, which might be very important:\n  -  Fork\n  -  Branches\n  -  Push commit to a remote repository\n  \n### Fork    \nWhen we **fork** a repository, in our case we **fork** the **Twindle** repository then, we get a personal copy of the repository in our **github** \naccount, where we are free to make changes without affecting the main project. When we do some changes in our own repository and want to implement these changes \nin the main project then, we raise pull request(PR). The authority of the main project review the changes and if they think it is good to include this \nchange in the main project then, they add it( you everytime want this to happen ) otherwise, reject it( sorry, but it happens too ). \n\n### Branches \n**Branches** are almost like a new copy of our code at the current state( it means you forked a repository, which becomes \"master\" version in your \nrepository and then, you make a copy of your \"master\" version ), which can then be used to develop \nnew code.For example, whenever we need to create a new feature, or rewrite any of our code, it's a good thing to create a new branch so that none \nof our changes affect the \"master\" version of the code. This is important since it can be very difficult to revert code changes from memory, \nespecially in complex systems. And also there are many changes happen in the \"master\" version of the main repository like in **Twindle**, people work \non the code and raise PR requests. If we try to raise PR request from our \"master\" version then, the PR requests raised by the people will also get \nadded in our raised PR and this creates mess. So, to avoid these problems, we need to create a branch, raise PR request from that branch, this will \navoid others' PR requests and _when the PR request is approved then, that branch must be deleted._ \n\nFirst of all, when we are creating a branch using _git bash_ then, we must assure that we are in the main branch as shown below:\n\n ![git9](https://user-images.githubusercontent.com/72906055/99707614-b5f9c880-2ac2-11eb-8fdd-5a6b3bbd2175.JPG)\n \n Here, in the above photo, \"(main)\" shows that we are in the main branch. Now, there are certain commands we should know and these are as follows:\n\n - To initialize a repository for a new or existing project,\n \n                git init\n\n -  To create a branch from the current branch\n \n                git branch <branch-name>\n              \n     this makes a copy of main branch,\n     \n     ![git10](https://user-images.githubusercontent.com/72906055/99710208-4685d800-2ac6-11eb-9b6b-1f7604abc9e4.JPG)\n     \n -  To see how many branches in the git or local,\n \n                git branch\n               \n    ![git11](https://user-images.githubusercontent.com/72906055/99710273-5bfb0200-2ac6-11eb-9946-dc823f3811a2.JPG)\n    \n    By seeing the photo, we also conclude that we are in the main branch because of the * .\n\n -  To see all the branches (local as well as remote),\n \n                git branch -a\n               \n    ![git12](https://user-images.githubusercontent.com/72906055/99759139-79f15280-2b18-11eb-87d1-8a63767bc8d9.JPG)\n    \n    From the above picture, we can see the local as well as remote branches(in red color).\n\n -  To switch from current branch to another branch,\n \n                git checkout <branch-name>\n               \n    ![git13](https://user-images.githubusercontent.com/72906055/99766351-ea9f6b80-2b26-11eb-8534-63440b2c6ff0.JPG)\n    \n    Here, first we in main branch, see all branches in the local and then, we switch to another branch.\n \n - To delete the branch but first, you need to go into the master or main branch then,\n \n                git branch -d <branch-name>\n              \n   ![git14](https://user-images.githubusercontent.com/72906055/99766796-d5770c80-2b27-11eb-8bb6-cdd08c268e5f.JPG)\n   \n   And again check how many branches are in your local.\n\n    Note: if it shows error: The branch <branch-name> is not fully merged then, please use,\n    \n    \n                git branch -D <branch-name>\n\n  -  To add one or more files to staging(index) area,\n  \n                git add <file_name>\n\n  -  To list the files you have changed and those still need to add or commit,\n  \n                git status \n\n  -  To remove files from staging area(unstage),\n  \n                git rm --cached <file_name>\n\n  -  To commit changes to head(here, head means where you are),\n  \n                git commit -m \"commit message\" \n\n  -  To see the total commits you have done,\n  \n                git log\n \n### Push commit to a remote repository     \nCommit means to save your changes in our local repository. When we do commit in the local repository then, we need to push this commit in remote \nrepository or our github account. We use `git push` to push commits to do this.\n\nThe `git push` command takes two arguments:\n\n  - A remote name, for example,  `origin`\n  - A branch name, for example,  `branch1`\n\n       `git push origin branch1`\n\nthen, go to the github account where, we can see a branch of same name is created with the changes we did in the local. From that same branch that is, \nbranch1, raise pull request. _This PR would be clean and without any mess. After approval or rejection, please remove that branch from remote and local._\n\nI urge everyone to please come forward and add more commands, if possible try to add it with suitable examples so that, it gives the better idea about \nit. \n\nSuggestions are welcome :blush: !!\n \n"
  },
  {
    "path": "misc/docs/articles/handlebar-explained.md",
    "content": "### **Why do we need Handlebars?**\n\nFor converting twitter threads to PDFs, we initially need to compose HTML comprising of that twitter thread with some styling and then we can consequently convert it to PDF. \n\n> Twitter thread --> HTML --> PDF\n\nTo compose that HTML from twitter thread we cannot directly give tweet text to PDF because it would just look like a pile of text. So we will be using a template in which we can input this twitter thread content. For creating that HTML from a template and we will be using Handlebars\n\n### **What is Handlebars?**\n\nHandlebars is a simple templating language. It generates HTML from a template which contains placeholders containing Handlebars expressions. Lets see in detail.\n\nFor example, below is a template that has two two Handlebars expressions\n\n```\n<p>{{firstname}} {{lastname}}</p>\n```\nIf applied to the input object\n\n```\n{\n  firstname: \"Vijaya\",\n  lastname: \"Bhaskar\",\n}\n```\n\nthe expressions will be replaced by the corresponding properties. The result is then\n\n```\n<p>Vijaya Bhaskar</p>\n```\n\n( React JS people can already feel familiar with this by props-components thing 😃 )\n\n### **How to utilize Handlebars in Twindle project?**\n\nAlready @tolgaerdonmez has implemented Handlebars to create a HTML from a template. Lets decode his code 😄 \nFollowing is the code from the template [Thread.hbs](https://github.com/twindle-co/twindle/blob/main/playground/cli/pdf-from-html-cli/templates/Thread.hbs)\n\n```\n<html>\n   <body>\n      <h1 id=\"title\">Twindle Thread</h1>\n      <ul>\n         {{!-- uses the twit mock object from mock API --}}\n         {{#each thread}}\n         <li>\n            <div class=\"tweetContainer\">\n               <div class=\"header\">\n                  <img src=\"{{image}}\" alt=\"{{twitterHandle}}\" />\n                  <div>\n                     <h3>{{name}} - <span>@{{twitterHandle}}</span></h3>\n                     <p><span>{{createdAt}}</span></p>\n                  </div>\n               </div>\n               <p>{{tweet}}</p>\n            </div>\n         </li>\n         {{/each}}\n      </ul>\n   </body>\n</html>\n```\n\nIn the above code we can see expressions like {{image}} {{twiiterHandle}} {{tweet}} where we input content using a JSON here. This template when given the JSON input of twitter thread data generates HTML.\n\n\nNow, lets see how the template rendering is done.\nFollowing is the code from [render-template.js](https://github.com/twindle-co/twindle/blob/main/playground/cli/pdf-from-html-cli/render-template.js) \n\n```\nconst hbs = require(\"handlebars\");\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\n// renders the html template with the given data and returns the html string\nfunction renderTemplate(data, templateName) {\n\tconst html = fs.readFileSync(path.join(__dirname, `templates/${templateName}.hbs`), {\n\t\tencoding: \"utf-8\",\n\t});\n\n\t// creates the Handlebars template object\n\tconst template = hbs.compile(html);\n\n\t// renders the html template with the given data\n\tconst rendered = template(data);\n\treturn rendered;\n}\n\nmodule.exports = renderTemplate;\n```\nIn the above code there are three main blocks, lets see one by one.\n<br>\n\nBlock 1: We are primarily are reading the file contents of the template here by giving the template name.\n```\nconst html = fs.readFileSync(path.join(__dirname, `templates/${templateName}.hbs`), {\n\t\tencoding: \"utf-8\",\n\t});\n```\n<br>\n\nBlock 2: Compiling the template so it can be executed immediately.\n```\nconst template = hbs.compile(html);\n```\n<br>\n\nBlock 3: When given input data it would generate the HTML using the template.\n```\nconst rendered = template(data);\n\treturn rendered;\n```\n<br>\n\nAnd finally in [index.js](https://github.com/twindle-co/twindle/blob/main/playground/cli/pdf-from-html-cli/index.js), we are inputting \"../mock/twit-thread.json\" data to \"Thread.hbs\" Handlebars template to generate required html.\n\n```\nconst htmlContent = renderTemplate({ thread: mockData }, \"Thread\");\n```\n<br>\nThanks to @tolgaerdonmez for the amazing code. <br/>Hope you understood Handlebars and how to render HTML using a template. <br/>\nLet me know  :thumbsup: or :thumbsdown:\n"
  },
  {
    "path": "misc/docs/articles/javascript-code.md",
    "content": "Hello everyone :wave: here, I am writing some questions and try to give the answer( if you think you have better answer then, you are welcome!! ) regarding \n**JavaScript** codes (thanks to **Kenny** :grinning:, who encouraged me to do this), which beginners(like me) mostly face problems to understand and \nwill add new questions/answers in future and I highly suggest everyone to come forward and add your questions/answers :love_you_gesture:. \n\n---\n\n## Problem no. 1\n\nWe are here to understand how the code in the last lines are working, which is in the link [here](https://github.com/twindle-co/twindle/blob/7315f9fa05ef55a2278ee73c1634392653fea635/twindle-web/team_details/team_details.js)\n, as shown below:\n \n\n    async function pushToDom() {\n   \n    let fetchData = await fetch('https://raw.githubusercontent.com/twindle-co/twindle/main/twindle-web/team_details/data.json');\n   \n    let data = await fetchData.json();\n   \n    let users = data.users;\n   \n    let html = '';\n   \n    users.forEach(info => html += generateCard(info)\n   \n      )\n      \n    cards.innerHTML = html;\n    \n    }\n   \n\n\n    pushToDom();\n    \n\nI asked about this code to **@UnevenCoder**. He hepled me in understanding the code by writing this code again with *comments* \nto understand in a better way. Thank you **Ameen** :grinning:.\n\nFirstly, we need to understand about **async** and **await** as both are used for **promise**:\n\n   - Async makes code to execute in a unserialized manner.\n   - Javascript runs moves the next line without ending the firsts , so to stop it at a line we use awaits , we use it in \n    an async function.\n    \n---\nAnd now we are ready to understand the code:\n\n    async function pushToDom() {\n  \nMakes the function asynchronous \n\n    let fdata = await fetch('https://raw.githubusercontent.com/UnevenCoder/twindle/main/twindle-web/team_details/data.json');\n \nThis is the raw data that the api sends , like it will be in all quotations\n\n    let data = await fdata.json();\n  \nNow converting raw data in json , that can be read and worked on by us easily [ await here because we first need the data to run this ]\n\n    let users = data.users;\n  \nFrom an api you receive tons of data so you are saying the thing you want\n\n    let str = \" \"; \n  \nEmpty string\n\n    users.forEach(data => str += generateCard(data));\n      \nlooping through each item of an array , generate card is a function that returns a card component in string format like \n  \n    <h1>hi</h1>\n    cards.innerHTML = str; \n  \nafter str has all the cards we just add / inject it in DOM [ basically the website ]\n   \n   } \n\n ---\n\nFrom here, we are getting **data.users**:\n\n\n    {\n    \"users\": [{\n      \"id\": 1,\n      \"name\": \"Akshay Sharma\",\n      \"img\": \"https://avatars0.githubusercontent.com/u/37118877?v=4\",\n      \"links\": {\n        \"website\": \"https://www.developeratease.com/\",\n        \"linkedin\": \"https://www.linkedin.com/in/akshay-sharma-7962ab13a/\",\n        \"github\": \"https://github.com/Akshay2996\",\n        \"twitter\": \"https://twitter.com/AkshayS2909\"\n      },\n      \"title\": \"FrontEnd Developer\",\n      \"location\": {\n        \"city\": \"Bangalore\",\n        \"state\": \"Karnataka\",\n        \"country\": \"India\"\n      }\n    }, {\n      \"id\": 2,\n      \"name\": \"Satyaki Bose\",\n      \"img\": \"https://avatars1.githubusercontent.com/u/25426670?s=400&u=7cc72ca148ae88bf19a10b8f36b21806504ffe34&v=4\",\n      \"links\": {\n        \"website\": \"\",\n        \"linkedin\": \"https://www.linkedin.com/in/satyaki07/\",\n        \"github\": \"https://github.com/satyaki07\",\n        \"twitter\": \"https://twitter.com/satyaki_07\"\n      },\n      \"title\": \"Developer\",\n      \"location\": {\n        \"city\": \"Kolkata\",\n        \"state\": \"West Bengal\",\n        \"country\": \"India\"\n      }\n    }, {\n      \"id\": 3,\n      \"name\": \"Rafael Rodrigues\",\n      \"img\": \"https://avatars3.githubusercontent.com/u/46648727?v=4\",\n      \"links\": {\n        \"website\": \"\",\n        \"linkedin\": \"https://www.linkedin.com/in/rafaelrodrigues55/\",\n        \"github\": \"https://github.com/RafaelBatman55\",\n        \"twitter\": \"https://twitter.com/rafa_55\"\n      },\n      \"title\": \"FrontEnd Developer\",\n      \"location\": {\n        \"city\": \"Resende\",\n        \"state\": \"Rio de Janeiro\",\n        \"country\": \"Brazil\"\n      }\n    },  {\n      \"id\": 4,\n      \"name\": \"Ameen Shafeeq\",\n      \"img\": \"https://avatars0.githubusercontent.com/u/49345531?v=4\",\n      \"links\": {\n        \"website\": \"https://ameencodes.tech/\",\n        \"linkedin\": \"\",\n        \"github\": \"https://github.com/unevencoder\",\n        \"twitter\": \"https://twitter.com/crafter_coder\"\n      },\n      \"title\": \"Student\",\n      \"location\": {\n        \"city\": \"Jeddah\",\n        \"state\": \"\",\n        \"country\": \"Saudi Arabia\"\n      }\n    }]\n    }\n\n---\n\n## **Problem no. 2**\n\n Could you please explain what is the *function* of these brackets{} in the below code?\n\n    module.exports = { createPdf };\n \nIn *JavaScript*, we can create *objects* using brackets like\n\n   `{ name: \"Twindle\" }`\n   \nIt is an *object*. Suppose we save this *object* into a *variable* me\n\n   `const me = { name: \"Twindle\" }`\n   \nThen, we will be able to print this in the *console*\n\n   `console.log(me.name);`\n   \nBecause there's an *object* called \"me\" and in the *object*, there is a *property* called \"name\". There is a concept in *JavaScript*. Here we did,\n\n   `{ name: \"Twindle\" }`\n   \nInstead, if we already have something like this\n\n   `const name = \"Twindle\";`\n   \nThen, we can use that *variable* to create an *object*\n\ne.g. `const me = { name: name };`\n\n(Please pay special attention here, we have already created \"name\" *variable* and we are assigning that \"name\" *variable* as \nvalue in the \"name\" *property* in the *object*.) Then, you will still be able to print this in the *console*, :golfing:\n\n   `console.log(me.name);`\n   \nAnd it will work the same way. So, now we have\n\n   `const me = { name: name };`\n   \nIn the above code, `name:name` looks odd. So, *JavaScript* made the syntax shorter.\n\ne.g. `const me = { name };`\n\nThis means find the existing \"name\" *variable*. And store that with the same *variable* name and the *variable* value. \nThen, we will be still able to do this\n\n      console.log(me.name);\n\nWe will get the same result. So, we have to write the same name as the *variable* or you need to create the *variable* name as needed in the *object*.\n\nSuggestions are welcome :blush:!!\n"
  },
  {
    "path": "misc/docs/articles/pr-clean-commits.md",
    "content": "Take a look at the image below\n![A PR](./images/fork-ahead-upstream-header-image.png)\n\nIt's a PR I opened recently. I made only 2 commits related to this PR, but as you can see, this includes a lot of commits (19 to be exact). The thing is that it was 15 commits in the previous PR, and around 13 in the one before. As you can see, these commits are just adding on. The next PR I make would have all these included plus the new commits.\n\nSo, how do you make these disappear?\n\nRead on!\n\n# Why does it happen?\n\nIt happens primarily when you make a PR from your `main` branch, or just push commits from it.\n\nA word:\n\n> Never push changes from main. Your `main` exists only to sync latest changes from the upstream branch. Always make a new branch, edit code there, and make the PR from that branch only.\n\n# Yeah, but tell me how to get rid of those stowaway commits!\n\nRight! All you have to do is run these commands\n\n```bash\ngit checkout main\ngit reset --hard upstream/main\ngit push --force\n```\n\nThese above should do it. But remember! You shouldn't have any changes in your working repo, as they might get deleted. Better make a new branch, then go back to `main` and run these commands then.\n"
  },
  {
    "path": "misc/docs/articles/puppeteer-explained.md",
    "content": "### **What is Puppeteer?**\n\n<img src=\"https://user-images.githubusercontent.com/5336488/96694886-a2195480-13a6-11eb-9311-29ce9a6680e4.png\" alt=\"Puppeteer\" width=\"174\" height=\"253\">\n\nFrom the documentation\n\n> Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol.\n\nIn other words, it is a Node library that we can use to control a headless Chrome instance. That means we are actually using Chrome, but programmatically using JavaScript. And \"headless Chrome\" is Chrome without the graphical user interface.\n\n(OK, thats alot of Chromes :hammer: :grinning: )\n\n### **Why do we need Puppeteer?**\n\nWe know that using Twindle we convert long twitter threads to readable PDFs/ePubs. So in that process we create HTML from Twitter thread content and then convert that HTML to PDFs\n\n> Twitter thread --> HTML --> PDF\n\n[Previously](https://github.com/twindle-co/twindle/issues/415) we have seen how we can use Handlebars to compose HTML from Twitter thread content and will now convert that HTML to PDF with the help of Puppeteer ( atleast for now :grinning: )\n\n> We need Puppeteer to render the HTML content in the headless browser so that we can perform print operation to convert the content to PDF.\n\n### **How to utilize Puppeteer in Twindle project?**\n\nAlready @tolgaerdonmez has implemented Puppeteer to generate PDF from HTML. Lets dissect the code :grinning: :hocho: \nFollowing is the code from [create-pdf.js](https://github.com/twindle-co/twindle/blob/main/playground/cli/pdf-from-html-cli/create-pdf.js)\n\n```\nconst puppeteer = require(\"puppeteer\");\nconst fs = require(\"fs\");\n\n// Creates a pdf document from htmlContent and saves it to outputPath\nasync function createPdf(outputPath, htmlContent) {\n\t// launchs a puppeteer browser instance and opens a new page\n\tconst browser = await puppeteer.launch();\n\tconst page = await browser.newPage();\n\n\t// sets the html of the page to htmlContent argument\n\tawait page.setContent(htmlContent);\n\n\t// Prints the html page to pdf document and saves it to given outputPath\n\tawait page.emulateMediaType(\"print\");\n\tawait page.pdf({ path: outputPath, format: \"A4\" });\n\n\t// Closing the puppeteer browser instance\n\tawait browser.close();\n}\n\nmodule.exports = createPdf;\n```\n\nIn the code above we are basically using headless Chrome browser to print content to PDF.\nThough the code is self explanatory, let me explain it ( atleast for the sake of this issue :grimacing: )\n\nFirstly we are including modules:\n\npuppeteer -- for rendering HTML and printing to PDF.\nfs -- Node.js file system module to work with the file system on your computer.\n\nAnd we are creating an async function here with await as we have to perform each step after execution of the previous one.\nAs simple as we cannot print content without first loading the content. Lets see the process in 3 simple steps.\n\nStep 1: Launching a new browser instance and then opening a new page.\n\n```\nconst browser = await puppeteer.launch();\n\tconst page = await browser.newPage();\n```\n<br>\nStep 2: Setting the page content to our HTML content.\n<br><br>\n\n\n```\n\tawait page.setContent(htmlContent);\n```\n<br>\nStep 3: Setting media type to print that enables the page to be in print mode and next step would be printing the page in given \"outputPath\" and format of the print \"A5\". At the end, closing the browser.\n<br> <br>\n\n```\nawait page.emulateMediaType(\"print\");\n\tawait page.pdf({ path: outputPath, format: \"A5\" });\n\tawait browser.close();\n```\n<br>\n\nAnd finally in [index.js](https://github.com/twindle-co/twindle/blob/main/playground/cli/pdf-from-html-cli/index.js), we are creating PDF using below code that will generate Twindle.pdf with the twitter thread content.\n\n```\nawait createPdf(\"Twindle.pdf\", htmlContent);\n```\n\n\n### **What are Pros, Cons and Alternatives for Puppeteer?**\n**Pros**\n\n- As Puppeteer is a Chrome automation tool written in NodeJS, the generated PDF is by Chrome itself and its hard to find a better PDF generation tool for web than this.\n- Fully functional and robust.\n- Popular and maintained by Chrome DevTools team and used internally by Google.\n\n**Cons**\n- The only disadvantage is that using Puppeteer means we are installing a full Chrome browser in server and it might have a larger footprint. Though there is \"puppeteer-core\" which is a lightweight version of Puppeteer, we need a browser installation already or a remote one for connecting which again might not fully relieve from server load.\n\n**Alternatives for Puppeteer**\n\n- [PhantomJS](https://github.com/ariya/phantomjs) is the only alternative to Puppeteer with ~ 28k stars on Github but it has been [archived](https://github.com/ariya/phantomjs/issues/15344) and its not maintained.\n\n\nThanks to @tolgaerdonmez  for the amazing code.\nHope you understood Puppeteer and how to generate PDFs using rendered HTML.\nLet me know 👍 or 👎\n"
  },
  {
    "path": "misc/docs/articles/setup-prettier-vscode.md",
    "content": "# Setting up auto-formatting in VSCode in 5 minutes\n\n> Originally published on [puruvj.dev](https://puruvj.dev/blog/setup-prettier-vscode)\n\n![Ugly vs Clean](https://puruvj.dev/media/prettier-setup-ugly-vs-clean/large.jpg)\n\nlook at the image above ☝. What do you see?\n\nOn the left, there is a very clean table chair, and very orderly. It's the kind of place where people would wanna sit and have a chat together. It's place where this 👇 happens\n\n![Tony and Cap Shake hand](https://puruvj.dev/media/tony-cap-handshake-endgame.gif)\n\nOn the right, it's a mess. A total mess. You simply can't expect to collaborate with anyone in here. You'd probably scare them off! No good conversation, no collaboration, nothing, NADA!!\n\n![Tony punch Cap in his perfect teeth](https://puruvj.dev/media/tony-punch-cap-perfect-teeth.gif)\n\nThis is the same situation with well-formatted code, and unformatted code.\n\nA little letter to those who have written unformatted code/still write them:\n\n> The code may look ugly, but it's still **your** code. You wrote it with the sweat of your brows and strength of your fingers. It's your child, as Scott Tolinsky would say. Having written unformatted code doesn't make you a bad developer.\n\nIf you're the only one involved in the project, sure go ahead and write unformatted code. Nobody minds(And ignore those who do).\n\nBut if you are in a team or in some situation where multiple developers are collaborating on the code, you need to have properly formatted code, whether by yourself by hand, or by having it auto-formatted by the tooling.\n\nAnd it's better if it's auto-formatted.\n\nAnd it's good to have formatted code, even when you're on your own. Higher readability, Higher productivity.\n\nAnd this is where Prettier comes in.\n\n# What is Prettier?\n\nPrettier is basically a tool that automatically formats the code you've written, saving you the headache of fixing spaces and brackets positioning yourselves.\n\n## So it's a VSCode extension?\n\nOff course, as the title says 😋.\n\nIt's a very good extension that saves you a lot of trouble. All you have to know is one shortcut key combo to automatically format the code, and boom, you're done.\n\nBut it's more than an extension. it's actually an <mark>NPM package</mark> as well as a <mark>CLI</mark> originally, on top of which the VSCode extension has been written. It can be run as easily as\n\n```powershell\nprettier --write filename.js\n```\n\nor, to format all kinds of files all at once\n\n```powershell\nprettier --write \"src/**/*.{html,css,js,json,jsx,tsx}\"\n```\n\n## Why should I bother with the NPM package?\n\nVSCode extension is much easier and better in terms of Developer Experience, but it can only format one file at a time. If you're adding prettier in a pre-existing large-enough project, you simply can't bother with opening every single file yourselves, and hitting the magic keys. You need to <mark>bulk-format</mark> them. That's where Prettier Node CLI comes in.\n\nThere's also the fact that VSCode is a memory hog, and adding extensions to it makes everything slower. You don't need to bother with this point if you have a 8 GB RAM, i5 CPU with blazing-fast SSD laptop, it can take any extension (These specs are just my laptop's specs, these are not hard limits in any way).\n\nBut if you have a slow system and don't wanna add any extensions, No Problem 👍. You'll just need to learn to use the CLI. And it's simple enough. I'll cover that below.\n\nNow let's cut to the chase.\n\n# Setting up the VSCode extension\n\nAll right, open up your VSCode\n\n1. Hit `Ctrl+P` (Or `Cmd+P` if on MacOS)\n2. Type `ext install esbenp.prettier-vscode` in the input box that opens up, and hit enter.\n3. This will install the extension. If it opens up a dialog box about syncing this extension, choose your sync preference for this extension. If you're not sure what that means, I recommend just choosing `Yes`. That should be good enough\n\nThere, now you have Prettier installed as an extension in VSCode\n\n## How to use it?\n\nEasy. Open up any file you wish to format. Any JavaScript file for example.\n\nRight click anywhere in the code, and you'll see the <mark>Format Document</mark> option in the context menu. Click on it, and your document will be auto-formatted, like this 👇\n\n![VSCode right click to format](https://puruvj.dev/media/prettier-setup-format-option-vscode.gif)\n\nMagic, right?\n\n## Shortcut keys\n\nThis is a tricky one. You see, the shortcut keys are different for different Operating Systems and sometimes even for different environments.\n\nYou'll have to see for yourself. Look at the GIF above. You can the see the shortcut key combo right next to the <mark>Format Document</mark> option. It's <mark>Alt+Shift+F</mark> for me.\n\n## Optional\n\nYou can modify your settings to format the document whenever you hit `Ctrl+S`.\n\nJust open up your VSCode settings, search for `\"Format on Save\"`, and check the option as true.\n\n![Format on Save](https://puruvj.dev/media/format-on-save.gif)\n\n> Note: If you have autosave turned on, beware, for whenever you type, your code may jump around like crazy, as prettier tries to format your document at the instant you're typing.\n\n# Using it with the CLI\n\nThis section is dedicated to how to format using the Prettier CLI\n\n## Installing the CLI\n\nThere 2 ways to install the CLI 👉\n\n### Global\n\nIt can be installed globally:\n\n```powershell\nnpm install -g prettier\n```\n\nThis approach is fine. However, if you're a new linux user and getting permission errors, don't worry for now. Just read the next section to install prettier locally.\n\n### Local\n\nLocal here means you install prettier in the folder you're working on right now, and run it locally. This is actually the recommended way, and better than the global approach, in my opinion ¯\\\\\\_(ツ)\\_/¯\n\n```powershell\nnpm install --save-dev prettier\n```\n\n## Running it\n\n```powershell\nnpx prettier --write file.js\n```\n\nThis is for the locally installed prettier. For global, remove `npx`:\n\n```powershell\nprettier --write file.js\n```\n\nYou can tell it to run prettify all JS files directly in the directory\n\n```powershell\nnpx prettier --write \"./src/*.js\"\n```\n\nOr recursively prettify all the JS files, no matter how deeply embedded in folders\n\n```powershell\nnpx prettier --write \"./src/**/*.js\"\n```\n\nYou can target multiple file formats too\n\n```powershell\nnpx prettier --write \"./src/**/*.{js,ts,html,css,json}\"\n```\n\nThe possibilities are endless.\n\n# .prettierrc\n\nLastly, you can add your own `.prettierrc` file to the your workspace. It is basically a little config file telling prettier how it should handle the formatting.\n\nHere's mine:\n\n```json\n{\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\",\n  \"printWidth\": 100\n}\n```\n\nAs you see, this file is tiny. And that's the best thing about prettier. Prettier comes with very sensible defaults, and will get you very good formatting even without the config file\n\n# Conclusion\n\nHoped this article helped 😁\n"
  },
  {
    "path": "misc/docs/articles/solving-forked-commit-ahead.md",
    "content": "## Scenario\n\n> - You fetch the upstream/main to your local repo\n> - You pull the upstream/main to your local repo\n> - You push the changes to origin/main\n> - Now your origin/main is few commits ahead of upstream/main even though they are both same.\n\n## Solution\n\n```console\n$ > git checkout main\n$ > git fetch upstream\n$ > git reset --hard upstream/main\n$ > git push --force\n```\n\n> This will sync your origin/main with upstream/main and resolve the commit ahead issue.\n"
  },
  {
    "path": "misc/docs/articles/sync-to-main-repo-using-git-bash.md",
    "content": "---\n\nI assume you already forked the main repo and clone into your local\n\nInitially it has a default remote called origin,that points to your fork on GitHub\n\n     git remote -v\n   \n        \n# Need to do\n  - Fetch the changes from main Repo to local \n  - Merge the changes in your local\n  - push the merged changes to you forked Repo\n\n**Step 1:** - To keep track of the original repo, you need to add another remote named upstream\n\n        git remote add upstream <main_repo>\n \n**Step 2:** - Fetch the changes from main Repo to local     \n    \n        git fetch upstream\n \n **Step 3:** - Merge the changes in your local \n \n        git merge upstream/main\n \n **Step 4:** - push the merged changes to you forked Repo\n    \n        git push origin\n"
  },
  {
    "path": "misc/docs/articles/sync-to-main-repo-using-pull-request.md",
    "content": "In this post, you will learn how to use Github pull request to synchronize your forked repository with the parent repository by following below steps.\n\n1️⃣ Open and navigate to your forked repo on GitHub and generate a pull request\n\n![image](https://user-images.githubusercontent.com/66166738/99892638-daa99800-2c9c-11eb-9e0b-0c3fb256287c.png)\n<br>\n2️⃣ Enter your fork repo as base repo\n\n![image](https://user-images.githubusercontent.com/66166738/99892736-05482080-2c9e-11eb-8e3b-7900e93b4870.png)\n<br>\n3️⃣ Once you enter the base as your fork repo, you will be navigated to the \"Comparing changes\" section, just click \"compare across forks\"\n\n![image](https://user-images.githubusercontent.com/66166738/99892706-8ce15f80-2c9d-11eb-9de0-f9a879a5b3b5.png)\n<br>\n4️⃣ Enter the original/main repo as the head repository and click on \"create pull request\".\n\n![image](https://user-images.githubusercontent.com/66166738/99892752-4a6c5280-2c9e-11eb-8674-336b7f7ac675.png)\n<br>\n5️⃣ Enter the title of your pull request and press again on the \"create pull request\".\n\n![image](https://user-images.githubusercontent.com/66166738/99893151-8dc8c000-2ca2-11eb-9ec2-ca2be173eeea.png)\n<br>\n6️⃣ It will perform some checks, and once all the tests are passed, click on the Merge pull request.\n\n![image](https://user-images.githubusercontent.com/66166738/99892833-3e34c500-2c9f-11eb-8b6b-1090b7539bfb.png)\n<br>\n7️⃣ It's going to ask again to confirm the merger. Just press the \"confirm merge\" button\n\n![image](https://user-images.githubusercontent.com/66166738/99892887-12fea580-2ca0-11eb-90d7-994dcaa9fc18.png)\n<br>\n8️⃣ Now you've successfully merged the file\n\n![image](https://user-images.githubusercontent.com/66166738/99892927-77216980-2ca0-11eb-89d4-4e92a869da32.png)\n<br>\n9️⃣ Now we need to update the local clone repository as we sync our fork repo.\nNavigate to the clone directory on your local machine and make sure you have opened the same branch you built the PR with, for me it was 'main'  and execute the command 'git pull'\n\n![image](https://user-images.githubusercontent.com/66166738/99893023-80f79c80-2ca1-11eb-910c-93539ce2135c.png)\n<br>\nYou've now updated your local clone with the changes you merged from the original GitHub repository into your fork.\n\n"
  },
  {
    "path": "misc/docs/articles/sync-to-main-repo.md",
    "content": "Here, I am assuming that you have already connected your **GitHub** account with the **Visual Studio Code**.\n\n    \n**Step 1:** - First, Open the folder in **Visual Studio Code**, which is connected to your **GitHub** account.\n     \n![Screen1](https://user-images.githubusercontent.com/72906055/96881417-e1759d00-149b-11eb-932f-16e1fd2844c1.JPG)\n     \n    \n**Step 2:** - Then, click on the **Source Control** button\n    \n![Screen2](https://user-images.githubusercontent.com/72906055/96881465-eb979b80-149b-11eb-91f0-dc08aa8db16b.JPG)\n\n\n**Step 3:** - After clicking the **Source Control** Button, you will see **Three dots**, click it then a **Dropdown** will appear like shown below,\n    \n![Screen3](https://user-images.githubusercontent.com/72906055/96881506-f81bf400-149b-11eb-9fd2-939b3aff9626.png)\n\n\n**Step 4:** - Click **Pull, Push** button then another **Dropdown** will appear where you have to click **Pull from...** button\n\n![Screen4](https://user-images.githubusercontent.com/72906055/96881545-023df280-149c-11eb-800f-f634c1856065.png)   \n    \n    \n**Step 5:** - Here, you will see a **Box** with two options and you have to select **Upstream** option because **Origin** option is what \n     you set it from your **GitHub** account, where you forked it from the main branch. Click **Upstream** option two times and wait \n     for some times. Now, your local repository is synced with main repo.\n    \n![Screen5](https://user-images.githubusercontent.com/72906055/96881590-0ec24b00-149c-11eb-95d5-6e6b8adb2b7a.png)\n"
  },
  {
    "path": "misc/docs/images/README.md",
    "content": "store document related image in this folder\n"
  },
  {
    "path": "misc/firstpr/abdu_masoudi.md",
    "content": "## Hello :)\r\nMy name is *Abdu* and I'm a self-taught Web Developer.\r\n"
  },
  {
    "path": "misc/firstpr/abhilash_s_s.md",
    "content": "### Hey 👋🏽, \n##### I'm [Abhilash S S](https://github.com/itsmeAB) from Kerala, India. I am looking forward with this project to learn and explore more.\n"
  },
  {
    "path": "misc/firstpr/ad3rinto.md",
    "content": "******************************ad3rinto****************\nBased in manchester , UK\ntwitter/ad3rinto\nCodenewbie\n"
  },
  {
    "path": "misc/firstpr/adewale.md",
    "content": "I am Adewale. I am a beginner with basic knowledge of HTML, CSS and Javascript. \n\nI don't stop acquiring new knowledge. \n\nCheck me out  on [GitHub]  https://github.com/princehaybee. \n\nThis is just my 2nd PR. Trying to see if it goes well. \n\nI am now working on the third PR.  \n\nthis is still my 2nd PR. \n\n"
  },
  {
    "path": "misc/firstpr/aditya.md",
    "content": "🤓 Aditya Satpute From Nashik, Maharashtra, India. I'm having 2+ years of experience in Web Dev. And Trying to learn javascript to work as Full Stack Dev.\n\n💻 Also working as a freelancer.\n\n🖥 AWS Cloud Developer"
  },
  {
    "path": "misc/firstpr/aditya20233.md",
    "content": "Aditya Lodhi-Python Begginer\n\nsecond PR,changes from github"
  },
  {
    "path": "misc/firstpr/aditya786.md",
    "content": ""
  },
  {
    "path": "misc/firstpr/akshay.md",
    "content": "# Hi! I'm [Akshay Sharma](https://github.com/Akshay2996) 👋\n\n- ☯ I'm a **Developer / Thinker / Creator / Learner**\n- 👨‍💻 FrontEnd Developer contributing to my first **Open Source Project**.\n- 🔖 I would love to make some new & strong connections with people.\n- 🌱 Learning new things everyday.\n\n### Languages & Tools:\n\n<img align=\"left\" alt=\"Visual Studio Code\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/visual-studio-code/visual-studio-code.png\" />\n\n<img align=\"left\" alt=\"HTML5\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/html/html.png\" />\n\n<img align=\"left\" alt=\"CSS3\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/css/css.png\" />\n\n<img align=\"left\" alt=\"Javascript\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/javascript/javascript.png\" />\n\n<img align=\"left\" alt=\"React\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/react/react.png\" />\n\n<img align=\"left\" alt=\"Python\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/python/python.png\" />\n\n<img align=\"left\" alt=\"SQL\" width=\"26px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/sql/sql.png\" />\n\n<img align=\"left\" alt=\"Git\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/git/git.png\" />\n\n<img align=\"left\" alt=\"Markdown\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/markdown/markdown.png\" />\n\n<img align=\"left\" alt=\"Excel\" width=\"30px\" width=\"30px\" src=\"https://raw.githubusercontent.com/Akshay2996/Akshay2996/master/assets/excel.png\" />\n"
  },
  {
    "path": "misc/firstpr/alejandra_pinto.md",
    "content": "### Hi there 👋\n\nMy name is Alejandra, I´m Developer in process...\n----\n\n- 🔭 I’m currently working on Projects startup\n- 🌱 I’m currently learning Dart, Flutter and Phyton\n- 👯 I’m looking to collaborate on Mobile Apps.\n- 🤔 I’m looking for help with Haskell\n- 💬 Ask me about Java, c++ and Git.\n"
  },
  {
    "path": "misc/firstpr/alexis.md",
    "content": "I'm Alexis Rivas from México, a just graduated Computer Systems Engineer and a newbie in the open source development, I hope learn and contribute everything what I can. \n"
  },
  {
    "path": "misc/firstpr/ali.md",
    "content": "Ali Ka from Sénégal. CS student, comfortable with HTML/CSS bootstrap and javascript.\n"
  },
  {
    "path": "misc/firstpr/andrei.md",
    "content": "Andrei Dan\nFrom Romania\nbeginner to html, css and javvascript\n"
  },
  {
    "path": "misc/firstpr/anujshukla.md",
    "content": "Hey this is Anuj  from Kushinagar India, I am currently an undergrad at VIT Vellore, India. I love the frontend development and I aspire to become a full stack developer. Other than code and coffee I really like traveling and reading books\n"
  },
  {
    "path": "misc/firstpr/aparna.md",
    "content": "Aparna a tech woman on break living in Bangalore with five years of experience in Java and Java EE. Trying to get into ES6 Javascript and building projects with it.\nMy second PR update.\n"
  },
  {
    "path": "misc/firstpr/aravind.md",
    "content": "My name is Aravind. I'm a product engineer living in US with 2 years of experience in software testing and project management. New to software development and wish to improve in HTML, CSS and JS.\n"
  },
  {
    "path": "misc/firstpr/arthurbuhl.md",
    "content": "### Hi there 👋\n\nI am Arthur Buhl. Web developer with basic knowledge of HTML, CSS and JS.\n\n### Learning PR\n\n- This is my second PR using terminal.\n"
  },
  {
    "path": "misc/firstpr/ashish.md",
    "content": "Hello I'm ashish,\n\nA former QA engineer, now a front-end developer\nLooking forward to contribute on twindle.\n"
  },
  {
    "path": "misc/firstpr/baijanath.md",
    "content": "Hey, I am Baijanath Tharu from Nepal. I have been learning MERN stack for few months. I am comfortable with HTML, CSS, JavaScript, ReactJs and NodeJs. I am really excited to make some contributions to this project.\n"
  },
  {
    "path": "misc/firstpr/bill.md",
    "content": "# Hello 👋\n\nI am Bill from Indonesia. Aspired to become a front-end web developer.\n\nI learned web programming for the first time in the early August 2020.\n\nI have learned HTML, CSS, JavaScript, and React so far.\n\nLooking forward to make meaningful contribution in this project ‼"
  },
  {
    "path": "misc/firstpr/blessing.md",
    "content": "Olaleye Blessing, student. Little bit comfortable with HTML, CSS and JS\n"
  },
  {
    "path": "misc/firstpr/can.md",
    "content": "Can Koçman,\nFrom Turkey,\nStudent and Developer.\nlocal PR\n"
  },
  {
    "path": "misc/firstpr/chimdie.md",
    "content": "_That really felt good once again._\n"
  },
  {
    "path": "misc/firstpr/codekumar.md",
    "content": "Ashish Kamble, ( kambleaa007.github.io ) Full Stack Developer - 3+ Years of experience in coding\n"
  },
  {
    "path": "misc/firstpr/daniel.md",
    "content": "Hello! I'm Daniel, currently a CS student and a web developer.\n"
  },
  {
    "path": "misc/firstpr/davabalan.md",
    "content": "Davabalan \n"
  },
  {
    "path": "misc/firstpr/deepak.md",
    "content": "Deepak - From Barh, Patna, language : C > HTML > JS : Beginner\n"
  },
  {
    "path": "misc/firstpr/deepak1_try2.md",
    "content": "Deepak- i have no experience in coding except C at college level and HTML & JS as hobby ( Self taught). \n"
  },
  {
    "path": "misc/firstpr/edori.md",
    "content": "Hello, I am Edori from Nigeria. I started learning how to use HTML, CSS and Javascript this year.\nThis is the second PR\n"
  },
  {
    "path": "misc/firstpr/edwin.md",
    "content": "My name is Edwin Mancipe. \nI am from Colombia. \nI am 43 years old. \nI have some experience with javascript and python.\nI am beginner\nTwindle rocks!\n2ndPR take 3 :|\n\n\n"
  },
  {
    "path": "misc/firstpr/eyram.md",
    "content": "hi, Eyram is name from Ghana.i started learning web develop some months ago.\nSecond pr."
  },
  {
    "path": "misc/firstpr/fabio.md",
    "content": "#### Fabio Duarte\nI'm from Colombia.\nI'm a beginner with some experience in HTML, CSS and JavaScript. Lately I've been using Tailwindcss.\n\nI can code a simple webpage from scratch but I don't know how to connect it with backend. I really appreciate all your advices and experience in order to improve my knowledge."
  },
  {
    "path": "misc/firstpr/franklinulrich.md",
    "content": "Hello I'm [Franklin](https://twitter.com/frankiefab100), a multidisciplinary Designer and UI Developer from a Biological sciences background.\n"
  },
  {
    "path": "misc/firstpr/fusen.md",
    "content": "I am Fusen Ye from China.\nA beginner of HTML,CSS and JavaScript.\nI want to contribute to the project.\nThis my first pr.\nif I make some mistakes,please forgive me.\nI like hot weather.\nI want to make a little progress every day."
  },
  {
    "path": "misc/firstpr/habeeb.md",
    "content": "I am Alasi. I have basic knowledge of HTML and CSS but currently learning Javascript. \n"
  },
  {
    "path": "misc/firstpr/harsha.md",
    "content": "## Hello World! \n<img src=\"https://raw.githubusercontent.com/iampavangandhi/iampavangandhi/master/gifs/Hi.gif\" width=\"30px\"></h2>\n\n<a href=\"https://twitter.com/harsha0x01\">\n  <img align=\"left\" alt=\"Twitter\" width=\"22px\" src=\"https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/twitter.svg\" />\n</a>\n<a href=\"https://www.linkedin.com/in/harsha-ambati\">\n  <img align=\"left\" alt=\"Linkdein\" width=\"22px\" src=\"https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/linkedin.svg\" />\n</a>\n<a href=\"https://github.com/Harsha-Ambati\">\n  <img align=\"left\" alt=\"Github\" width=\"22px\" src=\"https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/github.svg\" />\n</a>\n<a href=\"https://t.me/Bugbounty1\">\n  <img align=\"left\" alt=\"Telegram\" width=\"22px\" src=\"https://cdn.jsdelivr.net/npm/simple-icons@v3/icons/telegram.svg\" />\n</a>\n</br>\n\n### I am Harsha Vardhan\n\n- An Cyber security enthusiast\n- A Computer Engineering Undergraduate Student. \n- Currently working on some of my cool side projects based on Web Development and Security.\n- I'm currently looking for opportunities. I love to learn and contribute in any and every possible way.\n\n⭐️ From [harsha](https://github.com/harsha-ambati)\n-----\n"
  },
  {
    "path": "misc/firstpr/ibrahim.md",
    "content": "Hi, this is ibrahim from jordan\n"
  },
  {
    "path": "misc/firstpr/jahid.md",
    "content": "# I am Jahid Hasan, from Bangladesh.\n# I am an independent learner and coder without no prior work experience.\n"
  },
  {
    "path": "misc/firstpr/jaki.md",
    "content": "Mohamad Arif Mujaki as Jaki From Jakarta, Indonesia with less 1 year of development experience. I still newbie.\n"
  },
  {
    "path": "misc/firstpr/jesulayomi.md",
    "content": "Adetola Jesulayomi, Law Student - a bit of experience in coding\n"
  },
  {
    "path": "misc/firstpr/jilva.md",
    "content": "Hi everyone, I am Jilva Sheth from India. I am a Java developer and interested to learn more about HTML, CSS and Javascript.\n\nsecond pr changes from git bash terminal."
  },
  {
    "path": "misc/firstpr/joel_vinay_kumar.md",
    "content": "I'm Joel Vinay Kumar, a software engineer in Auzmor Inc.\n\nFind all about me in my [website](http://joel.swecha.io)\n"
  },
  {
    "path": "misc/firstpr/karan.md",
    "content": "Hello I am Karan Oza from Pune, India. I have 1 year experience in Angular Front-End development.\n"
  },
  {
    "path": "misc/firstpr/karan_khosla.md",
    "content": "Karan Khosla\nI'm from India\nI've over 9 years of experience but not in front end domain\n"
  },
  {
    "path": "misc/firstpr/kenny.md",
    "content": "Hi everyone, Kenny here\n"
  },
  {
    "path": "misc/firstpr/kingsley_victor.md",
    "content": "[Kingsley Victor](https://github.com/kingsley-einstein)\n\nFull-stack software engineer proficient in Java, Javascript and Typescript. Observed a national diploma programme in computer science at Yaba College Of Technology in Lagos, Nigeria. Presently in first year at Federal University Of Technology, Akure. Studying software engineering.\n"
  },
  {
    "path": "misc/firstpr/kiran_don.md",
    "content": "## Hello! This is [Usha Kiran](https://twitter.com/ushakiran_m) from Andhra Pradesh, India.\n### I am a second year computer science student, not having much experience in open source, projects and contribution things.\n### This is my first ever pull request.\n\n## Second PR\nAdded this line using git CLI locally... Hope it goes well!\n"
  },
  {
    "path": "misc/firstpr/krishnadevz.md",
    "content": "<h1 align=\"center\">Hello World👋</h1>\n<p align=\"center\">\n  <a href=\"https://krishnadevz.github.io/\">Website</a> •\n  <a href=\"https://twitter.com/krishnadevz\">Twitter</a> •\n  <a href=\"https://www.linkedin.com/in/krishnakakade/\">Linkedin</a>\n</p>\n\n* SelfTaught-Dev/Opensourcer🛠 I like to build things & Write things related to Web🌐🐱‍👤. \n* I'm ready for discussions and making new projects.I like to Contribute to the Opensource projects.🌠 \n* Check it out my latest blogs/articles here on 👉 [Dev.to](https://dev.to/krishnakakade)\n* Find More details about me :-[Website](https://krishnadevz.github.io)\n* You can reach me at [krishnadevz@protonmail.com](mailto:krishnadevz@protonmail.com).\n\n**Languages:**  \n\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/javascript/javascript.png\"></code>\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/react/react.png\"></code>\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/nodejs/nodejs.png\"></code>\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/html/html.png\"></code>\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/css/css.png\"></code>\n<code><img height=\"20\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/c/c.png\"></code>\n\n\n\n  \n\n \n[![Top Langs](https://github-readme-stats.vercel.app/api/top-langs/?username=krishnadevz&layout=compact&theme=dracula)](https://github.com/anuraghazra/github-readme-stats)\n![krishna's github stats](https://github-readme-stats.vercel.app/api?username=krishnadevz&show_icons=true&theme=dracula)\n<!--[![HitCount](http://hits.dwyl.com/krishnadevz/krishnadevz.svg)](http://hits.dwyl.com/krishnadevz/krishnadevz)-->\n\n\n[![trophy](https://github-profile-trophy.vercel.app/?username=krishnadevz&theme=onedark)](https://github.com/krishnadevz/github-profile-trophy)<h1 align=\"center\">Trophies</h1>\n"
  },
  {
    "path": "misc/firstpr/krypton.md",
    "content": "\n### Hi there 👋\n\n##### I'm Kryptöñ,\nAn engineering student with great passion for UI/UX designing, i love to create beautiful user interfaces. i also have interest in cyber security, competitive programming, and embedded systems.\n\n\n- 🔭 I’m currently working on UI/UX\n- 🌱 I’m currently learning nuxtJS.\n- 📫 How to reach me: \n  - LinkedIn:-https://www.linkedin.com/in/madhusudan-babar/\n  - medium:- https://medium.com/@madhusudanbabar\n  - Instagram:- https://instagram.com/madhu_babar\n  - Portfolio:- https://madhusudan.live/\n  - blog:- https://blog.madhusudan.live\n"
  },
  {
    "path": "misc/firstpr/lara_noomene.md",
    "content": "I am a junior developer student, I am following some programming courses and wish to learn more through this project.\n"
  },
  {
    "path": "misc/firstpr/layomi.md",
    "content": "Adetola Jesulayomi, Law Student -  a bit of experience in coding\n"
  },
  {
    "path": "misc/firstpr/lilfatfrank.md",
    "content": "Karan Parsnani\nExperience with React and JavaScript.\n\nMumbai, India.\n"
  },
  {
    "path": "misc/firstpr/liviza.md",
    "content": "am luli, a newBie web-developer, this is my second PR.\n"
  },
  {
    "path": "misc/firstpr/lorennale.md",
    "content": "### Hello World!\n\nMy name is Lorenna Leon, I'm a Software Developer in process but I have a lot of desire to learn everything I can\nI´m from Lima.Perú\n\n----\n\n   :telescope: I am a bootcamp student.   \n   :seedling: I’m currently learning react,javascript, html and css .  \n   :mag_right: I’m looking to collaborate with open source projects.   \n   :thinking: I’m looking for help with React.   \n   :smile: Pronouns: she/her   \n   :zap: Fun fact: I would like to have a web development company :computer:   \n   :mailbox: How to reach me:       <a href=\"https://www.linkedin.com/in/lorenna-le%C3%B3n-huam%C3%A1n-232351157/\">\n<img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/in-24.png\" width=\"20px\"></a> &nbsp;\n<a href=\"https://www.youtube.com/channel/UCYhBpwaeWH3GGqm7yRN330Q?view_as=subscriber\">\n<img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/yt-24.png\" width=\"20px\"></a>&nbsp;\n<a href=\"https://twitter.com/LowyEv\">\n<img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/tw-24.png\" width=\"20px\"></a>\n\n----\n\n![](https://github-readme-stats.vercel.app/api?username=lorennaleon&show_icons=true&theme=buefy)\n![](https://github-readme-stats.vercel.app/api/top-langs/?username=lorennaleon&hide=jupyter%20notebook&layout=compact)\n"
  },
  {
    "path": "misc/firstpr/lukmanokunade.md",
    "content": "Lukman Okunade, from Nigeria. I am proficient using HTML, CSS and JavaScript. I can work with NodeJs and React too\n"
  },
  {
    "path": "misc/firstpr/luli.md",
    "content": "#hi! am luli , a beginner with some basics in html5,css3 and js\n"
  },
  {
    "path": "misc/firstpr/manohar.md",
    "content": "**Manohar, new developer, looking forward to learn from this project**\n"
  },
  {
    "path": "misc/firstpr/manuel_alejandro.md",
    "content": "I'm Manuel , from Cuba. I have knowledge in HTML,CSS, JS and a little bit of React. This is my first experience in source project.\nHello, i made some change for the second PR."
  },
  {
    "path": "misc/firstpr/marcus.md",
    "content": "Marcus Ojetunde from Lagos, Nigeria with less than 1 year experience in web development\nI use React as my Js framework\n"
  },
  {
    "path": "misc/firstpr/maria_rivera.md",
    "content": "Hello, My name is Maria and I'm a Software Engineer student at 42 Silicon Valley. \n"
  },
  {
    "path": "misc/firstpr/mbui.md",
    "content": "**Githaka Mbui - Software engineer**\n"
  },
  {
    "path": "misc/firstpr/melissa_huerta.md",
    "content": "### Hello World!\n\nMy name is Melissa H, I'm a Game/Software Developer in process...  \n\nPreviously worked in the IT Infrastructure department but I came to **THE DARK SIDE**. (Development)\n\n----\n\n   🔭 I’m currently working on 'Printf' (42 project), WebSite built with React(WWC Project).  \n   🌱 I’m currently learning C#, Unity.  \n   👯 I’m looking to collaborate on Mobile Apps, Gaming Apps.  \n   🤔 I’m looking for help with C#, Unity.\n   💬 Ask me about C, Javascript, Git & Flutter 🤓.  \n   😄 Pronouns: she/her  \n   ⚡ Fun fact: Sometimes I make decisions based on my 8 Ball. 🎱  \n   📫 How to reach me:       <a href=\"https://www.linkedin.com/in/melissahuertamn/\"><img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/in-24.png\" width=\"20px\"></a> &nbsp;\n<a href=\"https://www.youtube.com/channel/UCbTXsfGiE_PU32_krMQeusA\"><img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/yt-24.png\" width=\"20px\"></a>&nbsp;\n<a href=\"https://www.twitter.com/piratelicorne\"><img src=\"https://github.com/piratelicorne/piratelicorne/blob/master/icons/tw-24.png\" width=\"20px\"></a>"
  },
  {
    "path": "misc/firstpr/michael.md",
    "content": "My name is Michael from Nigeria and I love coding.\n"
  },
  {
    "path": "misc/firstpr/mumbi.md",
    "content": "**Basic Intro**\n\nName : Regina Mumbi Gachomba\n\nI am a kenyan student currently studying in Turkey for my undergrad degree in computer engineering\nProficient in C,C++ and python.\nCurrently doing Java OOP as part of my course work.\nI have basic skills in HTML,CSS and javascript from an online course that I am currently doing.\nMain reason for joining this open source project is to get an idea of how these projects work and to learn from senior developers.\n"
  },
  {
    "path": "misc/firstpr/nahuel.md",
    "content": "Hi everyone! I am Nahuel from Argentina, I am 24 years old. \nI am a beginner and this is my first time working in a project and using GitHub, have basic knowledge of c++, c #, html5, css and javascript.\nI apologize if my english is not very good, I will do my best to help.\nthis will help me practice the language.\n\nI modified this from my local repo! so this will be the 2nd PR\n\nThanks for the opportunity!\n"
  },
  {
    "path": "misc/firstpr/nailah.md",
    "content": "Nneyen Umana from Nigeria, With a few months of developing experience. \nThis is my attempt at a PR... I messed up the first one and deleted the entire fork.\n\nUpdate : This is to test out my second PR.... let's see how it goes. \n\n"
  },
  {
    "path": "misc/firstpr/naveen.md",
    "content": "I am Naveen, an engineering student. Begineer in html , CSS, javascript and React."
  },
  {
    "path": "misc/firstpr/neaz_mahmood.md",
    "content": "I couldnot my 1st pr file named neaz.md.\nMaybe It was not marged yet. So I made this file. I am using git bash for this pr."
  },
  {
    "path": "misc/firstpr/nishank.md",
    "content": "Nishank , Beginner , Basics of Python, C, C++, I can do basic HTML, CSS by searching and getting things done by internet search .\n\nSecond PR, Changes implemented from Github Desktop\n"
  },
  {
    "path": "misc/firstpr/nitin_kadam.md",
    "content": "### Hi there 👋\n\nNitin Kadam\nFrom Pune, India. \n5+ years of experience in the web development.\nModerate level in the Front End Technologies like JavaScript, HTML, CSS.\n"
  },
  {
    "path": "misc/firstpr/nivetha.md",
    "content": "Hi, this is Nivetha from India.\n\n**Things about me**\n\n- I am a Computer Science graduate and looking for a job opportunity.\n- I am currently learning JavaScript and ReactJS.\n- Contact me at nivetharath@gmail.com\n- [Resume](https://drive.google.com/file/d/1w34GFk5Y_nEz6Jz5yBo76fZHFQ6RuXwJ/view?usp=sharing)\n\nSecond PR, Changes implemented from GitHub Desktop\n"
  },
  {
    "path": "misc/firstpr/omolo.md",
    "content": "Passionate about technology and currently learning to be a full-stack developer. Am based in Nairobi, Kenya\n"
  },
  {
    "path": "misc/firstpr/paniagua.md",
    "content": "I'm from Dominican Rep. I have knowledge in HTML,CSS, JS and a little bit of React. I'm kind of new here in git and this will be my first contribution in an open source proyect!\n"
  },
  {
    "path": "misc/firstpr/pavan.md",
    "content": "# Hi! I'm Pavan Kallati \n\n- I'm a **Developer / Problem Solver / learner**\n- Trying to be a better Full Stack Developer using JS.\n- Learning new things everyday.\n\n### Languages & Tools:\n<img align=\"left\" alt=\"HTML5\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/html/html.png\" />\n\n<img align=\"left\" alt=\"CSS3\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/css/css.png\" />\n\n<img align=\"left\" alt=\"Javascript\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/javascript/javascript.png\" />\n\n<img align=\"left\" alt=\"React\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/react/react.png\" />\n\n<img align=\"left\" alt=\"MongoDB\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/mongodb/mongodb.png\" />\n\n<img align=\"left\" alt=\"MongoDB\" width=\"30px\" src=\"https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/express/express.png\" />\n"
  },
  {
    "path": "misc/firstpr/pr_m.md",
    "content": "#Prathap\n\nCS Student. Now a working professional for a big bank.\n\nI never got a chance to work on open source or UI dev project. So, trying to improve my skills through this project.\n\n"
  },
  {
    "path": "misc/firstpr/prajwal.md",
    "content": "Prajwal Pradhan From Nepal,  2 years of developement experience. Specialise in React.js. \n"
  },
  {
    "path": "misc/firstpr/pranav.md",
    "content": "## Pranav Goel\nFrom Uttar Pradesh, India\n\n### Skills\nHTML, CSS, C++, C\n- I am just starting with javascript."
  },
  {
    "path": "misc/firstpr/prasanna.md",
    "content": "Prasanna from Tamil Nadu , India with 5 years of development experience in .Net\n"
  },
  {
    "path": "misc/firstpr/praveen.md",
    "content": "Praveen from TamilNadu Having 3 years of experience as a Backend developer\n"
  },
  {
    "path": "misc/firstpr/pravin.md",
    "content": "Pravinraj - Sofware Engineer focused on frontend technologies\n"
  },
  {
    "path": "misc/firstpr/predrag_stamenkovic.md",
    "content": "Predrag Stamenkovic,a\nHTML and CSS beginner\n\nMy name is Predrag Stamenkovic from Belgrade - Serbia.\nI work as Purchasing Manager in Jokey company, but very determined to become web developer in near future.\nCurrently learning HTML and CSS.\n\nYou can reach me:\n\n- mail: pedja_stamenkovic@yahoo.com\n- twiter: @Stamenkovic_P\n\n#twindle\n"
  },
  {
    "path": "misc/firstpr/prem.md",
    "content": "Hi all, This is Prem (MrVoicer) from India. I create WordPress Website and Speed optimize them to load fast\n"
  },
  {
    "path": "misc/firstpr/proful.md",
    "content": "Proful Sadangi\nFrom India, Living in Sydney\n15 yr of developement experience\n"
  },
  {
    "path": "misc/firstpr/puru.md",
    "content": "Puru Vijay from Jaipur, Rajasthan, India. 5 years of experience.\n\nHas worked in HTML, CSS, JS, StencilJS, React, Svelte, Angular.js, Angular, JQuery, Bootstrap, PHP, NodeJS, Python, Django, Firebase"
  },
  {
    "path": "misc/firstpr/rafael.md",
    "content": "I'm Rafael Rodrigues from Brazil. I'm a beginner in the programming world. I started to learn HTML+CSS+JavaScript about 5 months ago. I hope I can help and learn a lot in this project!\n"
  },
  {
    "path": "misc/firstpr/rajesh_prajapati.md",
    "content": "### Hi there 👋\n\n- 🔭 I’m currently working on ...#React & #Flutter\n- 🌱 I’m currently learning ...Javascript & Dart\n- 👯 I’m looking to collaborate on ...Github People 👨🏻‍💻\n- 🤔 I’m looking for help with ... space 🛰\n- 💬 Ask me about ...#coding ✅\n- 📫 How to reach me: ...@rkumar1904\n- 😄 Pronouns: ...RjLooper\n- ⚡ Fun fact: ...<RjLooper props={[Love 💜, Code 👨🏻‍💻, Deploy 🚀 } />\n\n![Rajesh's github stats](https://github-readme-stats.vercel.app/api?username=rkumar1904&show_icons=true&count_private=true)\n"
  },
  {
    "path": "misc/firstpr/rakesh.md",
    "content": "### Hey Internet,\n\n<br/>\n\nI'm [Rakesh!](https://github.com/Rakesh-4) Software Developer have 3+ years of experience in C#, JavaScript and Angular Framework\n\n---\n\n<br/>\n\n> Apart from coding, I am an introvert.(So I like to keep my profile low)\n"
  },
  {
    "path": "misc/firstpr/rohitsawai.md",
    "content": "Hello Everyone, This is Rohit Sawai, from Beed, Maharashtra. I'm Software Engineer and like to develop such products which will save time my time and the time of my client by making manual task automatic.\n"
  },
  {
    "path": "misc/firstpr/sachin.md",
    "content": "Sachin from India, 6 months of experience in JavaScript, Html, Css, Node, React.\n"
  },
  {
    "path": "misc/firstpr/samriddhi.md",
    "content": "## Hi!\nI'm Samriddhi, a 3rd year CSE undergrad 👨‍🎓\n\nI am currently learning MERN stack 💻📚\n\nGlad to be here ✨\n\nFind me on [GitHub](https://github.com/sammjainn)"
  },
  {
    "path": "misc/firstpr/samueladeniyi.md",
    "content": "﻿hello, i am samuel adeniyi from nigeria, i am comfortable with HTML, CSS, Javascript, React amd Node"
  },
  {
    "path": "misc/firstpr/sarvesh.md",
    "content": "Sarvesh Kadam from Mumbai,India. QA Engineer currently learning Web Developement\n"
  },
  {
    "path": "misc/firstpr/sasmita.md",
    "content": "\nI am web Designer love to create web page in figma and photoshop and also ready to learn .I am beginner stage.\n"
  },
  {
    "path": "misc/firstpr/satyaki.md",
    "content": "# Hi, I'm Satyaki !\nI am a MERN stack developer.\n\nFind me on [Github](https://github.com/satyaki07)\n"
  },
  {
    "path": "misc/firstpr/saurabh_srivastava.md",
    "content": "# **Namaste to all of you** <img src=\"https://raw.githubusercontent.com/MartinHeinz/MartinHeinz/master/wave.gif\" width=\"30px\"> \n\nMy name is Saurabh Srivastava\n\nB.TECH is CSE. Graduate of 2018. From India, Uttar Pradesh. Have 18 months Software Development Experience using LAMP Stack.\n\nKnow Front End Technologies like JavaScript, HTML5, CSS3 in moderation, enough to get the task done.\n\n## **Skills**\n* HTML5 & CSS3\n* JavaScript, jQuery\n* Basic Nodejs working\n* PHP and MySQL\n\n## **Contact me here**\n<a href=\"https://github.com/vasudeveloper001\">Github</a> | <a href=\"https://twitter.com/saudev001\">Twitter</a> | <a href=\"https://www.linkedin.com/in/saurabh-srivastava-b62330109/\">LinkedIn</a>\n\n✉ vasudeveloper001@gmail.com \n"
  },
  {
    "path": "misc/firstpr/scott.md",
    "content": "I am Scott  a frontend developer from johannesburg ,south africa. \n i am self taught with some knowledge of css,html, javascript , python , and node js.. \n \n \n\n"
  },
  {
    "path": "misc/firstpr/sheetal.md",
    "content": "### Hey there! :)\r#### I am Sheetal Pandey, an engineering student from India.\rI basically work with Python but I am also good with HTML5 and CSS3. Though I am yet to learn JavaScript.\rHappy to collaborate and learn together.\r"
  },
  {
    "path": "misc/firstpr/shekharranjan.md",
    "content": "Shekhar Ranjan - Learning JavaScript and a beginner in GitHub. I have basic knowledge of Java and SQL.This is 2nd PR and I am doing this from GitHub Desktop."
  },
  {
    "path": "misc/firstpr/simrin_joshi.md",
    "content": "I am a passionate professional focussed on full stack developement. I have 1.5 years of experience in Angular, HTML, CSS , JS,  Java and MySQL.  I am looking forward to actively contribute to Twindle."
  },
  {
    "path": "misc/firstpr/sippeybro.md",
    "content": "Hi im sippey bro"
  },
  {
    "path": "misc/firstpr/sravan.md",
    "content": "sravan-html,css andjs enthusiast.Open to learn new things.\nThis is my second pr from my local machine....\n"
  },
  {
    "path": "misc/firstpr/sunny.md",
    "content": "### hey there\n**second pr**\n\n\ni am sunny, from India and a Engineering student..\nand am very much comfortable in HTML, CSS and JavaScript.\nAnd am currently Working on MERN Stack.\n\nYou can reach me out on @sunny_dev_01 is my twitter handle.!!!\n"
  },
  {
    "path": "misc/firstpr/suraj.md",
    "content": "### Hi folks, I'm Suraj! :sun_with_face:\n\n\n<a href=\"https://in.linkedin.com/in/surajdc\">\n    <img align=\"left\" alt=\"Suraj Chandgude | Linkedin\" width=\"24px\" src=\"https://github.com/iamsurajdc/iamsurajdc/blob/master/assets/Linkedin.svg\" />\n  </a>\n  <a href=\"https://twitter.com/iamsurajdc\">\n    <img align=\"left\" alt=\"Suraj Chandgude | Twitter\" width=\"26px\" src=\"https://github.com/iamsurajdc/iamsurajdc/blob/master/assets/Twitter.svg\" />\n  </a>\n  <a href=\"https://www.instagram.com/curiousneuron/\">\n    <img align=\"left\" alt=\"Suraj Chandgude | Instagram\" width=\"24px\" src=\"https://github.com/iamsurajdc/iamsurajdc/blob/master/assets/Instagram.svg\" />\n  </a>\n  <a href=\"mailto:surajchandgude0304@gmail.com\">\n    <img align=\"left\" alt=\"Suraj Chandgude | Gmail\" width=\"26px\" src=\"https://github.com/iamsurajdc/iamsurajdc/blob/master/assets/Gmail.svg\" />\n  </a>\n<br />\n<br />\n\nHi, I'm Suraj Chandgude, a passionate sapien who writes computer programs and resides on earth for now!\n\n- :100: &nbsp; Take a look at my portfolio site: https://iamsurajdc.js.org :globe_with_meridians:\n- 🌱 &nbsp; I’m currently diving into Vue.js\n- 💬 &nbsp; Give me shoutout [here](https://twitter.com/iamsurajdc)\n- :page_facing_up: &nbsp; yes, I read sometime.\n\n#### Cheers!"
  },
  {
    "path": "misc/firstpr/swatirao.md",
    "content": "Hi, I am swati from mumbai. Know basics of html,css.\nexcited to learn something new\n"
  },
  {
    "path": "misc/firstpr/teckiegeek.md",
    "content": "Titi Olopade from United Kingdom. My background is computing with few years career break.\nI came back and got PRINCE2 certification (project management) and started learning coding less than a year ago.\nMy HTML and CSS are at an intermediate level. \nHere is how I created my second PR.\nUse GIT Bash for Microsoft Windows environment to run GIT from my commandline.\nFrom the twindle repository page on GitHub, click the green button labeled Clone or download.\nIn the “Clone with HTTPs” section, copy the URL for the repository.\nOn your laptop, open your GIT bash shell.\nType 'cd desktop' to change to to desktop directory where you want to install the cloned repository\nNext, type 'git clone (URL for the repository)'\nOnce completed, it will display 'done'\nType 'ls -a' to view all the files in the repository (Do not edit any file from the command line).\nNow go back and make some changes and save.\nThen commit on your laptop by typing 'git commit'\nThen push to your fork and then on your fork you make pull request, just like for 1st PR."
  },
  {
    "path": "misc/firstpr/therealjimoh.md",
    "content": "Abdullah Jimoh, from Nigeria. I am a beginner in Front-end development.\n"
  },
  {
    "path": "misc/firstpr/titi_olopade.md",
    "content": "Titi Olopade from United Kingdom. My background is computing with few years career break.\nI came back and got PRINCE2 certification (project management) and started learning coding less than a year ago.\nMy HTML and CSS are at an intermediate level. \nHere is how I created my second PR.\nUse GIT Bash for Microsoft Windows environment to run GIT from my commandline.\nFrom the twindle repository page on GitHub, click the green button labeled Clone or download.\nIn the “Clone with HTTPs” section, copy the URL for the repository.\nOn your laptop, open your GIT bash shell.\nType 'cd desktop' to change to to desktop directory where you want to install the cloned repository\nNext, type 'git clone (URL for the repository)'\nOnce completed, it will display 'done'\nType 'ls -a' to view all the files in the repository (Do not edit any file from the command line).\nNow go back and make some changes and save.\nThen commit on your laptop by typing 'git commit'\nThen push to your fork and then on your fork you make pull request, just like for 1st PR."
  },
  {
    "path": "misc/firstpr/tolga.md",
    "content": "Tolga Erdönmez,\nFrom Turkey,\nStudent and developer :)\nI like javascript, go, python,\nBuilding web & desktop apps, automating stuff, CLIs\n"
  },
  {
    "path": "misc/firstpr/trombley.md",
    "content": "I am trombley!just making a beginning\n#update one\nThis is a second PR"
  },
  {
    "path": "misc/firstpr/tushar.md",
    "content": "Tushar Kashyap from India, good with HTML, CSS, JS and React."
  },
  {
    "path": "misc/firstpr/tusharkandpal.md",
    "content": "\n# **Hey Folks!** <img src=\"https://raw.githubusercontent.com/MartinHeinz/MartinHeinz/master/wave.gif\" width=\"30px\">\n\n\nI'm Tushar Kandpal 👨‍💻!\n\nAn Electrical Engineer from Uttarakhand, India. Also a beginner in Front-End field.\n***\n\n## **Skills**\n* HTML, CSS, Java\n* Familiar with JavaScript, C++, DS, MySql\n* **Frameworks:** Bootstrap\n* **Technologies:** Git, VS Code\n***\n\n## **Catch Me Here**\n<a href=\"https://github.com/tusharkandpal\">Github</a> | <a href=\"https://twitter.com/tushar_kandpal\">Twitter</a> | <a href=\"https://www.linkedin.com/in/tushar-kandpal/\">LinkedIn</a>\n\n✉ kandpal.tushar@gmail.com\n"
  },
  {
    "path": "misc/firstpr/varad.md",
    "content": "Varad Karpe from India\nRecently graduated as a Computer Science Engineer\nExperience:\nI have worked on a few Java projects during my internships(2).\nSkills:\nJava, HTML, CSS, Python, Javascript, C, C++, SQL"
  },
  {
    "path": "misc/firstpr/vera_nkanmuo.md",
    "content": "Hi,I'm Chioma Vera Nkanmuo, Beginner Web developer from Nigeria with basic knowledge of  HTML and  CSS. I am currently in my penultimate level in university.\nAside  from coding, I love to  read alot.\nFUN FACT: I am a fitness ethusiast.\n          I love to teach (because i  believe in sharing knowlege, you gain knowlege), so you may reach out to me for help, and I will be delighed  to help.\nConnect with me:  verachioma39@yahoo.com / verayahoo5@gmail.com\nfollow me instagram and twitter; @dev_chivee\n"
  },
  {
    "path": "misc/firstpr/vijaya.md",
    "content": "Vijaya Bhaskar from Hyderbad, India with 9 years of development experience.\nThis is a second pull request.\n"
  },
  {
    "path": "misc/firstpr/vipin.md",
    "content": "Hi i am vipin, A frontend developer from Punjab mostly working on react native trying to learn advance javascript and nodejs.\n\n![Vipin's github stats](https://github-readme-stats.vercel.app/api?username=aesthytik)\n\n"
  },
  {
    "path": "misc/firstpr/viraj_patil.md",
    "content": "** Second PR **\n\nHi, I'm Viraj Patil, with 9 years of experience in software development.\n\nCurrently working as fullstack developer.\n\nAside  from coding, I love to  read alot.\n\nYou can reach me out on:\ne-mail: mr.virajpatil@gmail.com\ntwitter: @mr_viraj\n"
  },
  {
    "path": "misc/firstpr/wamuyuwanjohi",
    "content": "Wamuyu Wanjohi : Kenyan Junior Data NAalyst who enjoys a bit of DS\n"
  },
  {
    "path": "misc/firstpr/yash.md",
    "content": "Yash Gupta from India.\nWeb developer and currently in Final year of college.\nSkill-HTML, CSS, Javascript, React, NodeJs.\nMy first pull request to twindle"
  },
  {
    "path": "misc/playground/cli/README.md",
    "content": "# Folder Naming Convention\n> Place your code inside a folder\n### Use dashes(-) as delimiters\n### Use lowercase letters\n### Create a readme file explaining the steps to run the code\n"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/code.js",
    "content": "const fetch = require(\"node-fetch\");\n\nasync function searchFor(searchTerm) {\n  const queryResult = [];\n  const url = `http://hn.algolia.com/api/v1/search?query=${searchTerm}&tags=story`;\n  const response = await fetch(url);\n\n  if (!response.ok) {\n    throw new Error(`HTTP error! status: ${response.status}`);\n  }\n\n  const result = await response.json();\n  queryResult.push(result);\n\n  return queryResult[0].hits;\n}\n\nasync function captureCommentsId(keyword) {\n  const elements = await searchFor(`${keyword}`);\n  const Time = [];\n  const Titles = [];\n  const Urls = [];\n  const StoryIds = [];\n  for (const [k] of elements.entries()) {\n    Time.push(elements[k].created_at);\n    Titles.push(elements[k].title);\n    Urls.push(elements[k].url);\n    StoryIds.push(elements[k].objectID);\n  }\n  return [Time, Titles, Urls, StoryIds];\n}\n\nasync function searchForStoriesComments(keyword) {\n  const [Time, Titles, Urls, StoryId] = await captureCommentsId(`${keyword}`);\n  const commentsDump = [];\n  for (const [, value] of StoryId.entries()) {\n    const api = `https://hn.algolia.com/api/v1/search?tags=comment,story_${value}`;\n    const response = await fetch(api);\n    const Page = await response.json();\n    commentsDump.push(Page);\n  }\n  return [commentsDump, Titles, Urls, Time];\n}\n//searchForStoriesComments()\n\nasync function dumpEverything(keyword) {\n  const [comments, Titles, Urls, Time] = await searchForStoriesComments(\n    `${keyword}`\n  );\n  const commentsText = [];\n  for (const [, element] of comments.entries()) {\n    //console.log(`${id} and ${element}`)\n    const single = element.hits;\n    for (const [, val] of single.entries()) {\n      //console.log(`${key} and ${val}`)\n      let commentval = val.comment_text;\n      commentsText.push(commentval);\n    }\n  }\n  return [commentsText, Titles, Urls, Time];\n}\n//dumpEverything()\n\nasync function constructObjectArray(keyword) {\n  const [Comments, Titles, Urls, Time] = await dumpEverything(`${keyword}`);\n  const arrayOfStories = [];\n  for (let i = 0; i < Titles.length; i++) {\n    if (i != 0) {\n      spin(Comments, 20);\n    }\n\n    arrayOfStories.push({\n      Story: Titles[i],\n      Date: Time[i],\n      Website: Urls[i],\n      discussion: Comments.slice(0, 20),\n    });\n  }\n  //console.log(arrayOfStories);\n  return arrayOfStories;\n}\n\n//constructObjectArray();\n\nfunction spin(array, window) {\n  const gone = array.splice(0, window);\n  return [...array, ...gone];\n}\n\nmodule.exports = { constructObjectArray };\n"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/code2.js",
    "content": "const fetch = require(\"node-fetch\");\n//only story ids\nasync function searchFor() {\n  const frontStoryIDs = [];\n  const url = `https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty`;\n  const response = await fetch(url);\n  const result = await response.json();\n  const sliced = result.slice(0, 4);\n  frontStoryIDs.push(sliced);\n\n  return frontStoryIDs;\n}\n//response from all storyids\nasync function StoriesOnFrontPage() {\n  const [frontStoryids] = await searchFor();\n  const StoryIDs = [];\n  const kidos = [];\n  const Title = [];\n  const Url = [];\n  const requests = frontStoryids.map((x) =>\n    fetch(`https://hacker-news.firebaseio.com/v0/item/${x}.json?print=pretty`)\n  );\n  const response = await Promise.all(requests);\n  const filter = await Promise.all(response.map((res) => res.json()));\n  for (const [k, v] of filter.entries()) {\n    StoryIDs.push(v.id);\n    kidos.push(v.kids);\n    Url.push(v.url);\n    Title.push(v.title);\n  }\n  return [StoryIDs, kidos, Title, Url];\n}\n\n//front comment +id  with parentid=storyid\nasync function frontComments() {\n  const CommentsText = [];\n  const OwnId = []; //frontcomments own id\n  const ParentId = []; //or storyid\n  const Ownkids = []; //front comment kids\n  const kidolen = [];\n  const [, kidos, Title, Url] = await StoriesOnFrontPage();\n\n  const allkids = [].concat(...kidos);\n\n  const requests = allkids.map((x) =>\n    fetch(`https://hacker-news.firebaseio.com/v0/item/${x}.json?print=pretty`)\n  );\n  const response = await Promise.all(requests);\n  const filter = await Promise.all(response.map((res) => res.json()));\n\n  for (const [k, v] of filter.entries()) {\n    if (v.deleted !== true) {\n      CommentsText.push(v.text);\n      OwnId.push(v.id);\n      ParentId.push(v.parent);\n      Ownkids.push(v.kids);\n    } else {\n      OwnId.push(v.id);\n      ParentId.push(v.parent);\n    }\n  }\n\n  return [OwnId, ParentId, Ownkids, CommentsText, kidolen];\n  //OwnId:frontcomments own id\n  //ParentId:or storyid\n  //Ownkids:kids of front comment\n}\n//all frontend Comments are here in commentText();\n\nasync function midcomments() {\n  const [\n    OwnIdorSecondPartid,\n    firstparentid,\n    Ownkidsid,\n    ,\n  ] = await frontComments();\n  const subsequentComments = [];\n  const ourkids = [];\n  const subparentid = [];\n  const ownid2 = [];\n  //const Ownkidlen = [];\n\n  const furthercomments = [].concat(...Ownkidsid).filter((e) => e !== undefined);\n\n  //fetch childrens of first comment\n  const requests = furthercomments.map((x) =>\n    fetch(`https://hacker-news.firebaseio.com/v0/item/${x}.json?print=pretty`)\n  );\n  const response = await Promise.all(requests);\n  const filter = await Promise.all(response.map((res) => res.json()));\n  for (const [k, v] of filter.entries()) {\n    if (v === null) {\n      console.log(\"null val\");\n    } else if (v.deleted !== true) {\n      subsequentComments.push(v.text);\n      ownid2.push(v.id);\n      subparentid.push(v.parent);\n      ourkids.push(v.kids);\n    } else {\n        console.log(\"undefined\");\n        subparentid.push(v.parent)\n        ownid2.push(v.id)\n    }\n    //return [ourkids,subparentid,subsequentComments]\n    //subsequentcomments:childrens of front comments\n    //subparent:frontcomments\n  }\n  return [ownid2, ourkids, subparentid, subsequentComments];\n}\nmidcomments();\n\nasync function lastlayercomments() {\n  const [, ourkids, ,] = await midcomments();\n  const lastlayerComments = [];\n  const ownid3 = [];\n  const ownparentid = [];\n  const secondlayer = [].concat(...ourkids).filter((e) => e !== undefined);\n  const requests = secondlayer.map((x) =>\n    fetch(`https://hacker-news.firebaseio.com/v0/item/${x}.json?print=pretty`)\n  );\n  const response = await Promise.all(requests);\n  const filter = await Promise.all(response.map((res) => res.json()));\n  for (const [k, v] of filter.entries()) {\n    lastlayerComments.push(v.text);\n    ownid3.push(v.id);\n    ownparentid.push(v.parent);\n  }\n  return [ownid3, ownparentid, lastlayerComments];\n}\nlastlayercomments();\n\nasync function constructObjectArray() {\n  const everything = [];\n  const [StoryIDs, , Title, Url] = await StoriesOnFrontPage();\n  const [OwnId, ParentId, , CommentsText] = await frontComments();\n  const [ownid2, , subparentid, subsequentComments] = await midcomments();\n  const [ownid3, ownparentid, lastlayerComments] = await lastlayercomments();\n  //StoryIDs==ParentId(parent of 1st level comments)\n  //Ownid(means 1st commment)==subparentid(parent of mid comments):means first comments are parent of midlevel ones\n  //ownid2(mid comment)==ownparentid(parent of last layer) :\n \n //creating some key-value relationship between using parent and child ID\n //to align comments \n \n  const objfirstlevel = {};\n  for (let i = 0; i < CommentsText.length; i++) {\n    objfirstlevel[CommentsText[i]] = ParentId[i];\n  }\n\n  const attachfisttomiddle = {};\n  for (let i = 0; i < OwnId.length; i++) {\n    attachfisttomiddle[OwnId[i]] = ownid2[i];\n  }\n\n  const objmidlevel = {};\n  for (let i = 0; i < subsequentComments.length; i++) {\n    objmidlevel[subsequentComments[i]] = subparentid[i];\n  }\n\n  const attachmidtolast = {};\n  for (let i = 0; i < ownid2.length; i++) {\n    attachmidtolast[ownid2[i]] = ownid3[i];\n  }\n\n  const objlastlevel = {};\n  for (let i = 0; i < lastlayerComments.length; i++) {\n    objlastlevel[lastlayerComments[i]] = ownparentid[i];\n  }\n}\n\nconstructObjectArray();\n"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/mainindex.js",
    "content": "const { constructObjectArray } = require(\"./code\");\nconst Renderer = require(\"./renderer\");\n\nasync function news(keyword) {\n  const data = await constructObjectArray(`${keyword}`);\n  const outputFilePath = `${__dirname}/news.pdf`;\n  await Renderer.render(data, outputFilePath);\n  console.log(`your pdf saved to ${outputFilePath}`);\n}\nnews(\"medicine\");\n"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/renderer/index.js",
    "content": "//const { generateEpub } = require(\"./epub/index\");\nconst { generatePDF } = require(\"./pdf\");\n//const spinner = require(\"../spinner\");\n\nconst render = async (tweets, outputFilePath) => {\n  \n    \n      return generatePDF(tweets, outputFilePath);\n    /*case \"epub\":\n      return generateEpub(tweets, outputFilePath);*/\n    //default:\n      //spinner.fail(\"Error: This renderer is not implemented yet\");\n    //console.error(\"Error: This renderer is not implemented yet\");\n  \n};\n\nmodule.exports = {\n  render,\n};"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/renderer/pdf/createpdf.js",
    "content": "// @ts-check\nconst puppeteer = require(\"puppeteer\");\n\nfunction footerMarkup() {\n  return `\n  <div class=\"footer\" style=\"width: 100%;font-size: 10px !important;display: flex;justify-content: center;\">\n    <span>\n      <span style=\"font-size: 10px !important;\" class=\"pageNumber\"></span>\n        of\n      <span style=\"font-size: 10px !important;\" class=\"totalPages\"></span>\n    </span>\n  </div>\n  `;\n}\n\n/**\n * Creates a pdf document from htmlContent and saves it to outputPath\n * @param {string} outputPath\n * @param {string} htmlContent\n */\nasync function createPdf(outputPath, htmlContent) {\n  try {\n    // launches a headless puppeteer browser instance and opens a new page\n    const browser = await puppeteer.launch({\n      args: [\"--no-sandbox\"],\n      headless: true,\n    });\n\n  const page = await browser.newPage();\n\n\n    // sets the html of the page to htmlContent argument\n    await page.setContent(htmlContent);\n\n    // Prints the html page to pdf document and saves it to given outputPath\n    await page.emulateMediaType(\"print\");\n\n    await page.pdf({\n      path: outputPath,\n      format: \"A5\",\n      margin: {\n        bottom: 52, // minimum required for footer msg to display\n        left: 20,\n        right: 20,\n        top: 10,\n      },\n      printBackground: true,\n      displayHeaderFooter: true,\n      footerTemplate: footerMarkup(),\n      headerTemplate: \"<div></div>\",\n    });\n\n    // Closing the puppeteer browser instance\n    await browser.close();\n  } catch (error) {\n    console.error(error);\n  }\n}\n\nmodule.exports = { createPdf };\n"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/renderer/pdf/index.js",
    "content": "const { renderTemplate } = require(\"../render-template\");\nconst { createPdf } = require(\"./createpdf\");\n\n\nasync function generatePDF(data, outputPath) {\n\n  const parameter ={Story: data};\n  // creates the html content\n  const htmlContent = await renderTemplate(\n    parameter,\n    \"template\"\n  );\n\n  // creates the pdf from html and saves it to Twindle.pdf\n   await createPdf(outputPath, htmlContent);\n\n  return;\n}\n//generatePDF()\nmodule.exports = { generatePDF };"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/renderer/render-template.js",
    "content": "const { readFile, writeFile } = require(\"fs\").promises;\nconst hbs = require(\"handlebars\");\nconst { tmpdir } = require(\"os\");\nconst { join } = require(\"path\");\n\n/**\n * Renders the html template with the given data and returns the html string\n * @param {CustomTweetsObject} data\n * @param {string} templateName\n */\nasync function renderTemplate(data, templateName) {\n  const html = await readFile(`${__dirname}/template/${templateName}.hbs`, \"utf-8\");\n\n  // creates the Handlebars template object\n  const template = hbs.compile(html, {\n    strict: true,\n  });\n\n  // renders the html template with the given data\n  const rendered = template(data);\n\n  const tmpPath = join(tmpdir(), \"hello.html\");\n  await writeFile(tmpPath, rendered, \"utf-8\");\n  await writeFile(tmpdir() + \"/x.json\", JSON.stringify(data, null, 2), \"utf-8\");\n  console.log(\"rendered saved to \", tmpPath);\n  return rendered;\n  \n}\nmodule.exports = { renderTemplate};"
  },
  {
    "path": "misc/playground/cli/spike/HackerNews/renderer/template/template.hbs",
    "content": "<html>\n\n<head>\n    <meta charset=\"UTF-8\">\n\n</head>\n\n<body>\n    <h1 id=\"title\">\n        <center>Stories from Hacker News</center>\n    </h1>\n\n    <ul>\n        {{!-- discussion forum HN --}}\n        {{#each Story }}\n        <div class=\"StoryHeader\">\n            <h1><a href={{Website}}>{{Story}}</a></h1>\n            <div><span>{{Date}}</span></div>\n\n        </div>\n        <div class=\"Discussion\" {{#if discussion}} discussion: {{{discussion}}} html-escaped: {{discussion}} </div>\n            {{/if}}\n        {{/each}}\n    </ul>\n</body>\n\n</html>"
  },
  {
    "path": "misc/playground/cli/spike/README.md",
    "content": "# Spike or POC\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-epub/.gitignore",
    "content": "#node modules\nnode_modules/\n\n#ebook files\n*.mobi\n*.pdf\n*.epub"
  },
  {
    "path": "misc/playground/cli/spike/cli-epub/epub.js",
    "content": "import epub from 'epub-gen';\n\nconst options = {\n   title: 'The Hello World',\n   author: 'Hello MacWorld',\n   content: [\n     {\n       title: 'Chapter 1: First Hello',\n       data: `<p>\n       Lorem Ipsum dolor sit amet, consectetur adipsicing elite. Got it? Neither did we. In the publishing and design industries, “lorem ipsum” is used as dummy text in visual designs. Using placeholder copy like this helps designers and clients alike focus on layout, imagery, typography, and design rather than on the actual wording of content.\n\n       Dummy copy is great, but what’s with all the Latin? Turns out, the original Lorem Ipsum comes from bits and pieces of Cicero’s De Finibus bonorum et Malorum (On the Ends of Goods and Evils).\n       </p>`\n     }\n   ]\n };\n\n export function generateEpub (directory) {\n    return new epub(options, directory)\n }"
  },
  {
    "path": "misc/playground/cli/spike/cli-epub/index.js",
    "content": "import yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { generateEpub } from \"./epub.js\";\n\nconst options = yargs(hideBin(process.argv))\n   .usage(\"Usage: -o <file format> -n <filename>\")\n   .option({\n\to: {\n\t\talias: \"output\",\n\t\tdemandOption: false,\n      describe: \"Output file format\",\n\t\tchoices: [\"mobi\", \"epub\", \"pdf\"],\n      type: \"string\",\n      default: \"epub\",\n\t},\n\tn: {\n\t\talias: \"filename\",\n\t\tdemandOption: true,\n\t\tdescribe: \"Filename for the output file\",\n\t\ttype: \"string\",\n\t},\n}).argv;\n\nswitch (options.output) {\n\tcase 'epub':\n\t\tgenerateEpub(`./${options.filename}.epub`);\n\t\tbreak;\n\tcase 'pdf':\n\t\tconsole.log('Sorry this format is not supported yet')\n\t\tbreak;\n\tcase 'mobi':\n\t\tconsole.log('Sorry this format is not supported yet')\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-epub/package.json",
    "content": "{\n  \"name\": \"cli-epub\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Generate cli from text input\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"billgufran\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"epub-gen\": \"^0.1.0\",\n    \"yargs\": \"^16.1.0\"\n  },\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-epub/readme.md",
    "content": "To start\r\n\r\n`node index -o <file format> -n <filename>`\r\n\r\nOptions:\r\n```\r\n      --help        Show help\r\n      --version     Show version number                                  \r\n  -o, --output      Output file format\r\n                     [string] [choices: \"mobi\", \"epub\", \"pdf\"] [default: \"epub\"]\r\n  -n, --filename  Filename for the output file               [string] [required]\r\n ```\r\n\r\nIt only support epub for now.\r\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-example-esm/.gitignore",
    "content": "#node modules\nnode_modules/\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-example-esm/index.js",
    "content": "import axios from 'axios';\n\nconst getBreeds = async () => {\n  try {\n    return await axios.get('https://dog.ceo/api/breeds/list/all')\n  } catch (error) {\n    console.error(error)\n  }\n}\n\nconst countBreeds = async () => {\n  const breeds = await getBreeds()\n\n  if (breeds.data.message) {\n    console.log(`Got ${Object.entries(breeds.data.message).length} breeds`)\n  }\n}\n\ncountBreeds()\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-example-esm/package.json",
    "content": "{\n  \"name\": \"cli-example-esm\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Example of how to use ES6+ syntax to run node scripts\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"start\": \"node -r esm index.js\"\n  },\n  \"keywords\": [],\n  \"author\": \"Aravind\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"axios\": \"^0.20.0\",\n    \"esm\": \"^3.2.25\"\n  }\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/cli-example-esm/readme.md",
    "content": "### Intro\nThis playground script is to give proof of concept idea to show we can write ES6+ syntax in JS files and run with Node.\n\nThis script has:\n- Import statement\n- Async/Await syntax instead of a regular promise.\n\n### Instructions\nTo start\n\n`npm start` or `yarn start`\n\nPackage.json has a start script that's defined as:\n`node -r esm index.js`\n\nFor more options on esm, read here: https://www.npmjs.com/package/esm#options\n\n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/Readme.md",
    "content": "# Theme\n\n - Simple nodejs CLI application for convert the JSON files to PDF\n\n# Requirements\n\n - Node.js 10.x.x\n\n# workflow\n  - Convert JSON to HTML\n  - Convert HTML to PDF\n\n# npm modules used\n\n  - # create-html\n        used to convert content to html file\n  - # mustache\n         Mustache is a logic-less template syntax. It can be used for HTML, config files, source code - anything\n  - # html-to-pdf\n        used to convert a html file to pdf\n\n### Installation\n\nInstall the dependencies:\n- npm i\n- npm start\n\n### Options:  \nOptions:  \n\n--version: Show version number  \n\n-o, --option: twindle command line options, choices ['pdf', 'epub', 'mobi', 'md'] \n\n-f, --file path: file path of the json that is to be converted  \n\n-h, --help: Show help        \n\n# Usage\n`node script -f <file path of the json> -o <file type>`\n\n# Example:\n`node script -f ./twit_thread.json -o pdf`  \nPDF Succesfully saved in F:\\Projects\\JS\\Twindle\\fetch-json-data\\output.pdf\n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/output.css",
    "content": "#title {\n  text-align: center;\n}\n\nul li {\n  list-style: none;\n}\n\n.tweetContainer {\n  display: flex;\n  flex-direction: column;\n  margin-bottom: 30px;\n  border-style: solid;\n  padding: 20px;\n}\n\n.tweetContainer * {\n  margin: 0;\n  padding: 0;\n}\n\n.tweetContainer .header {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  margin-bottom: 10px;\n}\n\n.tweetContainer .header img {\n  width: 75px;\n  height: 75px;\n  border-radius: 50%;\n  margin-right: 10px;\n}\n\n.tweetContainer .header > div {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: center;\n}\n\n.tweetContainer .header > div p {\n  font-size: 14px;\n}\n\n.tweetContainer .header > div span {\n  font-style: italic;\n  color: rgb(45, 45, 45);\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/output.html",
    "content": "<!doctype html>\n<html lang=\"en\" dir=\"ltr\">\n<head>\n<title>Twindle</title>\n<meta charset=\"utf-8\">\n\n\n<link rel=\"stylesheet\" href=\"output.css\">\n\n\n</head>\n<body>\n<div>\n<h1 id=\"title\">Twindle</h1>\n    <ul>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https:&#x2F;&#x2F;pbs.twimg.com&#x2F;profile_images&#x2F;1256841238298292232&#x2F;ycqwaMI2_400x400.jpg\" alt=\"Pranav\" />\n                    <div>\n                        <h3>Pranav - <span>@Pranav</span></h3>\n                        <p><span></span></p>\n                    </div>\n                </div>\n                <p>How to Get Rich (without getting lucky) in the world:</p>\n            </div>\n        </li>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https:&#x2F;&#x2F;pbs.twimg.com&#x2F;profile_images&#x2F;1256841238298292232&#x2F;ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span></span></p>\n                    </div>\n                </div>\n                <p>Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.</p>\n            </div>\n        </li>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https:&#x2F;&#x2F;pbs.twimg.com&#x2F;profile_images&#x2F;1256841238298292232&#x2F;ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span></span></p>\n                    </div>\n                </div>\n                <p>Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.</p>\n            </div>\n        </li>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https:&#x2F;&#x2F;pbs.twimg.com&#x2F;profile_images&#x2F;1256841238298292232&#x2F;ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span></span></p>\n                    </div>\n                </div>\n                <p>Ignore people playing status games. They gain status by attacking people playing wealth creation games.</p>\n            </div>\n        </li>\n\n    </ul>\n</div>  \n\n\n</body>\n</html>\n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/package.json",
    "content": "{\n  \"name\": \"fetch-json-data\",\n  \"version\": \"1.0.0\",\n  \"description\": \"convert json data to pdf\",\n  \"main\": \"script.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"PraveenKumar\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"create-html\": \"^4.1.0\",\n    \"html-to-pdf\": \"^0.1.11\",\n    \"jquery\": \"^3.5.1\",\n    \"mustache\": \"^4.0.1\",\n    \"yargs\": \"^16.1.0\"\n  }\n}"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/script.js",
    "content": "const Mustache = require(\"mustache\");\nconst fs = require(\"fs\");\nconst createHTML = require(\"create-html\");\nconst htmlToPdf = require(\"html-to-pdf\");\nconst yargs = require(\"yargs\");\nconst { resolve } = require(\"path\");\n\nconst args = yargs\n  .options({\n    o: {\n      alias: \"option\",\n      describe:\n        \"twindle command line options\\n choices 'pdf', 'epub', 'mobi', 'md'\",\n      demandOption: true,\n    },\n    f: {\n      alias: \"file path\",\n      describe: \"file path of json to be converted to pdf\",\n      demandOption: true,\n    },\n  })\n  .help()\n  .alias(\"help\", \"h\")\n  .usage(\"node script -f <file name> -o <file type>\").argv;\n\nif (args[\"o\"] === \"pdf\" && fs.existsSync(resolve(args[\"f\"]))) {\n  console.log(\"JSON file is available in the path \" + resolve(args[\"f\"]));\n  main();\n} else {\n  console.log(\"PDF only supported \" + args[\"o\"]);\n}\n\nfunction main() {\n  data = require(\"./twit_thread.json\");\n  //console.log(data);\n  let tweetData = {\n    tweets: data,\n  };\n  getHtml(tweetData);\n}\n\nfunction getHtml(tweetData) {\n  //console.log(\"TweetData\", tweetData);\n  //get external mustache template\n  fs.readFile(\"tweet_template.mustache\", \"utf8\", function (err, data) {\n    if (err) {\n      console.log(err);\n      return;\n    }\n    //render the View data in the Mustache template\n    let info = Mustache.render(data, tweetData);\n    //create an HTMLContent\n    let htmlContent = createHTML({\n      title: \"Twindle\",\n      body: info,\n      css: \"output.css\", //external css\n    });\n    getPdf(htmlContent);\n  });\n}\n\nfunction getPdf(htmlContent) {\n  //console.log(\"HTML \",htmlContent);\n  //create html file and write the htmlcontent which we are getting from Mustache template\n  fs.writeFile(\"output.html\", htmlContent, function (err) {\n    if (err) {\n      console.log(err);\n      return;\n    }\n  });\n  //convert the HTML Content/HTML Page to PDF\n  htmlToPdf.convertHTMLFile(\"output.html\", \"output.pdf\", function (error) {\n    if (error) {\n      console.log(\"Error Occured \", error);\n      return;\n    }\n    console.log(\"PDF Succesfully saved in \" + process.cwd() + \"\\\\output.pdf\");\n  });\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/tweet_template.mustache",
    "content": "<div>\n<h1 id=\"title\">Twindle</h1>\n    <ul>\n        {{!-- uses the twit mock object from mock API --}}\n        {{#tweets}}\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"{{image}}\" alt=\"{{twitterHandle}}\" />\n                    <div>\n                        <h3>{{name}} - <span>@{{twitterHandle}}</span></h3>\n                        <p><span>{{createdAt}}</span></p>\n                    </div>\n                </div>\n                <p>{{tweet}}</p>\n            </div>\n        </li>\n        {{/tweets}}\n\n    </ul>\n</div>  \n"
  },
  {
    "path": "misc/playground/cli/spike/json-to-pdf-cli/twit_thread.json",
    "content": "[\n  {\n    \"name\": \"Pranav\",\n    \"twitterHandle\": \"Pranav\",\n    \"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n    \"createAt\": \"2018-05-31\",\n    \"tweet\": \"How to Get Rich (without getting lucky) in the world:\"\n  },\n  {\n    \"name\": \"Naval\",\n    \"twitterHandle\": \"naval\",\n    \"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n    \"createAt\": \"2018-05-31\",\n    \"tweet\": \"Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.\"\n  },\n  {\n    \"name\": \"Naval\",\n    \"twitterHandle\": \"naval\",\n    \"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n    \"createAt\": \"2018-05-31\",\n    \"tweet\": \"Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.\"\n  },\n   {\n    \"name\": \"Naval\",\n    \"twitterHandle\": \"naval\",\n    \"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n    \"createAt\": \"2018-05-31\",\n    \"tweet\": \"Ignore people playing status games. They gain status by attacking people playing wealth creation games.\"\n  }\n]"
  },
  {
    "path": "misc/playground/cli/spike/markdown/index.js",
    "content": "const markdown = (name , tweet , url , username)=>{\nreturn(\n  '### ' + tweet + '\\n'+'![img]('+url+')'+'\\n'+name+' | '+username\n)\n}\nconsole.log(\n  markdown('naval','How to Get Rich (without getting lucky)','https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg','@naval')\n)\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/README.md",
    "content": "# Creating PDFs from HTML using Puppeteer\n\n## This simple module creates pdf documents using tweet threads\n\n### To Run\n\n```shell\nyarn start\n```\n\noutputs `Twindle.pdf`\n\n### How it works\n\n-   First, handlebars module creates the html with the given tweets\n\n-   Then, we convert and save the html file as pdf document using puppeteer.\n\n### Example documents\n\nAnd example pdf document and the static version of the handlebar template can be found in `examples`\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/create-pdf.js",
    "content": "const puppeteer = require(\"puppeteer\");\nconst fs = require(\"fs\");\n\n// Creates a pdf document from htmlContent and saves it to outputPath\nasync function createPdf(outputPath, htmlContent) {\n\t// launchs a puppeteer browser instance and opens a new page\n\tconst browser = await puppeteer.launch();\n\tconst page = await browser.newPage();\n\n\t// sets the html of the page to htmlContent argument\n\tawait page.setContent(htmlContent);\n\n\t// Prints the html page to pdf document and saves it to given outputPath\n\tawait page.emulateMediaType(\"print\");\n\tawait page.pdf({ path: outputPath, format: \"A4\" });\n\n\t// Closing the puppeteer browser instance\n\tawait browser.close();\n}\n\nmodule.exports = createPdf;\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/examples/Twindle.html",
    "content": "<style type=\"text/css\">\n    * {\n        font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Open Sans\",\n            \"Helvetica Neue\", sans-serif;\n    }\n\n    body {\n        margin: 5% auto;\n    }\n\n    #title {\n        text-align: center;\n    }\n\n    ul {\n        list-style-type: none;\n        display: flex;\n        align-items: center;\n        flex-direction: column;\n    }\n\n    ul li {\n        margin: 20px;\n    }\n\n    .tweetContainer {\n        display: flex;\n        flex-direction: column;\n    }\n\n    .tweetContainer * {\n        margin: 0;\n        padding: 0;\n    }\n\n    .tweetContainer .header {\n        display: flex;\n        flex-direction: row;\n        align-items: center;\n        margin-bottom: 10px;\n    }\n\n    .tweetContainer .header img {\n        width: 75px;\n        height: 75px;\n        border-radius: 50%;\n\n        margin-right: 10px;\n    }\n\n    .tweetContainer .header>div {\n        display: flex;\n        flex-direction: column;\n        align-items: flex-start;\n        justify-content: center;\n    }\n\n    .tweetContainer .header>div p {\n        font-size: 14px;\n    }\n\n    .tweetContainer .header>div span {\n        font-style: italic;\n        color: rgb(45, 45, 45);\n    }\n</style>\n<html>\n\n<body>\n    <h1 id=\"title\">Twindle Thread</h1>\n\n    <ul>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span>2018-05-31</span></p>\n                    </div>\n                </div>\n                <p>How to Get Rich (without getting lucky):</p>\n            </div>\n        </li>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span>2018-05-31</span></p>\n                    </div>\n                </div>\n                <p>How to Get Rich (without getting lucky):</p>\n            </div>\n        </li>\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\" alt=\"naval\" />\n                    <div>\n                        <h3>Naval - <span>@naval</span></h3>\n                        <p><span>2018-05-31</span></p>\n                    </div>\n                </div>\n                <p>How to Get Rich (without getting lucky):</p>\n            </div>\n        </li>\n\n    </ul>\n</body>\n\n</html>"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/index.js",
    "content": "const renderTemplate = require(\"./render-template\");\nconst createPdf = require(\"./create-pdf\");\n// const fs = require(\"fs\");\nconst mockData = require(\"../../mock/twit-thread.json\");\n\nasync function main() {\n\t// creates the html content\n\tconst htmlContent = renderTemplate({ thread: mockData }, \"Thread\");\n\t// creates the pdf from html and saves it to Twindle.pdf\n\tawait createPdf(\"Twindle.pdf\", htmlContent);\n\n\t// example line for saving the html version\n\t// fs.writeFileSync(\"Twindle.html\", htmlContent);\n}\n\nmain();\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/package.json",
    "content": "{\n\t\"name\": \"pdf-from-html-cli\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Creates pdf from html using puppeteer and handlebars template\",\n\t\"main\": \"index.js\",\n\t\"author\": \"Tolga Erdönmez\",\n\t\"license\": \"MIT\",\n\t\"scripts\": {\n\t\t\"start\": \"node .\"\n\t},\n\t\"dependencies\": {\n\t\t\"handlebars\": \"^4.7.6\",\n\t\t\"puppeteer\": \"^5.3.1\"\n\t}\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/render-template.js",
    "content": "const hbs = require(\"handlebars\");\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\n// renders the html template with the given data and returns the html string\nfunction renderTemplate(data, templateName) {\n\tconst html = fs.readFileSync(path.join(__dirname, `templates/${templateName}.hbs`), {\n\t\tencoding: \"utf-8\",\n\t});\n\n\t// creates the Handlebars template object\n\tconst template = hbs.compile(html);\n\n\t// renders the html template with the given data\n\tconst rendered = template(data);\n\treturn rendered;\n}\n\nmodule.exports = renderTemplate;\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-html-cli/templates/Thread.hbs",
    "content": "<style type=\"text/css\">\n    * {\n        font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Open Sans\",\n            \"Helvetica Neue\", sans-serif;\n    }\n\n    body {\n        margin: 5% auto;\n    }\n\n    #title {\n        text-align: center;\n    }\n\n    ul {\n        list-style-type: none;\n        display: flex;\n        align-items: center;\n        flex-direction: column;\n    }\n\n    ul li {\n        margin: 20px;\n    }\n\n    .tweetContainer {\n        display: flex;\n        flex-direction: column;\n    }\n\n    .tweetContainer * {\n        margin: 0;\n        padding: 0;\n    }\n\n    .tweetContainer .header {\n        display: flex;\n        flex-direction: row;\n        align-items: center;\n        margin-bottom: 10px;\n    }\n\n    .tweetContainer .header img {\n        width: 75px;\n        height: 75px;\n        border-radius: 50%;\n\n        margin-right: 10px;\n    }\n\n    .tweetContainer .header>div {\n        display: flex;\n        flex-direction: column;\n        align-items: flex-start;\n        justify-content: center;\n    }\n\n    .tweetContainer .header>div p {\n        font-size: 14px;\n    }\n\n    .tweetContainer .header>div span {\n        font-style: italic;\n        color: rgb(45, 45, 45);\n    }\n</style>\n<html>\n\n<body>\n    <h1 id=\"title\">Twindle Thread</h1>\n\n    <ul>\n        {{!-- uses the twit mock object from mock API --}}\n        {{#each thread}}\n        <li>\n            <div class=\"tweetContainer\">\n                <div class=\"header\">\n                    <img src=\"{{image}}\" alt=\"{{twitterHandle}}\" />\n                    <div>\n                        <h3>{{name}} - <span>@{{twitterHandle}}</span></h3>\n                        <p><span>{{createdAt}}</span></p>\n                    </div>\n                </div>\n                <p>{{tweet}}</p>\n            </div>\n        </li>\n        {{/each}}\n\n    </ul>\n</body>\n\n</html>"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-json/Readme.md",
    "content": "# How to run\n\nTo run this file,\nclone or download this folder.\n\nIn command line, after navigating to this folder, run:\n\n`npm i`\n\nand then\n\n`node index.js`\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-json/index.js",
    "content": "// can import this from json\nconst data = [\n  {\n    name: 'Naval',\n    twitterHandle: 'naval',\n    image:\n      'https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg',\n    createAt: '2018-05-31',\n    tweet: 'How to Get Rich (without getting lucky):'\n  }\n];\n\n// setting available fonts\nconst fonts = {\n  Courier: {\n    normal: 'Courier',\n    bold: 'Courier-Bold',\n    italics: 'Courier-Oblique',\n    bolditalics: 'Courier-BoldOblique'\n  },\n  Helvetica: {\n    normal: 'Helvetica',\n    bold: 'Helvetica-Bold',\n    italics: 'Helvetica-Oblique',\n    bolditalics: 'Helvetica-BoldOblique'\n  },\n  Times: {\n    normal: 'Times-Roman',\n    bold: 'Times-Bold',\n    italics: 'Times-Italic',\n    bolditalics: 'Times-BoldItalic'\n  },\n  Symbol: {\n    normal: 'Symbol'\n  },\n  ZapfDingbats: {\n    normal: 'ZapfDingbats'\n  }\n};\n\nconst PdfPrinter = require('pdfmake');\nconst printer = new PdfPrinter(fonts);\nconst fs = require('fs');\n\n// document\nconst docDefinition = {\n  content: [\n    {\n      columns: [{ text: 'Name: ', bold: true }, data[0].name]\n    },\n    {\n      columns: [\n        { text: 'Twitter Handle: ', bold: true },\n        `@${data[0].twitterHandle}`\n      ]\n    },\n    {\n      columns: [{ text: 'Tweet: ', bold: true }, data[0].tweet]\n    }\n  ],\n  image: data[0].image,\n  defaultStyle: {\n    font: 'Helvetica'\n  }\n};\n\nconst pdfDoc = printer.createPdfKitDocument(docDefinition);\npdfDoc.pipe(fs.createWriteStream('./tweet.pdf'));\npdfDoc.end();\n\n// console.log(data);\n"
  },
  {
    "path": "misc/playground/cli/spike/pdf-from-json/package.json",
    "content": "{\n  \"name\": \"pdf\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"fs\": \"0.0.1-security\",\n    \"jspdf\": \"^2.1.1\",\n    \"pdfmake\": \"^0.1.68\"\n  }\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/api/index.js",
    "content": "const { default: Axios } = require(\"axios\");\n\nclass API {\n static async getTwitterThread(req, res) {\n  try {\n   // Obtain variables injected into request alongside the request body. These injected variables are made available by the custom Twitter middleware.\n   const { twitterUrl, twitterApiKey, opts, body } = req;\n   const response = await Axios.get(twitterUrl + \"/2/tweets/\" + body.id, {\n    headers: {\n     ...opts.headers,\n     \"Authorization\": `Bearer ${twitterApiKey}`\n    }\n   });\n   res.status(200).json({\n    statusCode: 200,\n    response: response.data.data\n   });\n  } catch (error) {\n   // console.log(error);\n   res.status(500).json({\n    statusCode: 500,\n    response: error.message\n   });\n  }\n }\n}\n\nmodule.exports.API = API;\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/config/index.js",
    "content": "const express = require(\"express\");\nconst { loggingMiddleware, twitterMiddleware } = require(\"../middlewares\");\nconst { pages, api } = require(\"../router\");\n\nmodule.exports = (app) => {\n // Use middlewares\n app.use(express.json());\n app.use(express.urlencoded({\n  extended: false\n }));\n app.use(loggingMiddleware);\n app.use(twitterMiddleware);\n app.use(\"/pages\", pages);\n app.use(\"/api\", api);\n\n return app;\n};\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/environment.js",
    "content": "const Env = require(\"dotenv\");\n\nEnv.config();\n\n// Inject the Twitter api endpoint from the environment\nmodule.exports.TWITTER_API_URL = process.env.TWITTER_API_URL;\n\n// Inject Twitter api key or token from the environment\nmodule.exports.TWITTER_API_KEY = process.env.TWITTER_API_KEY;\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/index.js",
    "content": "const express = require(\"express\")\nlet app = express();\nconst log = require(\"debug\")(\"app\");\nconst config = require(\"./config\");\n\nconst port = parseInt(process.env.PORT || \"5000\");\n\napp = config(app);\n\napp.listen(port, () => log(`Express server is running on port: ${port}`));\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/middlewares/index.js",
    "content": "module.exports.twitterMiddleware = require(\"./twitter\").http;\nmodule.exports.loggingMiddleware = require(\"./request-logger\").logger;\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/middlewares/request-logger.js",
    "content": "const log = require(\"debug\")(\"requests\");\n\nmodule.exports.logger = (req, res, next) => {\n // Log request details and response code\n res.on(\"finish\", () => {\n  log(`${req.method}: ${req.path} ====== ${res.statusCode}`);\n });\n next();\n};\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/middlewares/twitter.js",
    "content": "const { TWITTER_API_URL, TWITTER_API_KEY } = require(\"../environment\");\n\nmodule.exports.http = (req, res, next) => {\n try {\n  // Inject the Twitter api url into every request \n  req.twitterUrl = TWITTER_API_URL;\n\n  // Inject the Twitter api key into every request\n  req.twitterApiKey = TWITTER_API_KEY;\n\n  // console.log(TWITTER_API_URL);\n\n  // Inject options into every request. These options would be made available as configuration parameters to axios\n  req.opts = {\n   headers: {\n    \"Content-Type\": \"application/json\"\n   }\n  };\n\n  // Pass control to the next function\n  next();\n } catch (error) {\n  res.status(500).json({\n   statusCode: 500,\n   response: error.message\n  });\n }\n};\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/package.json",
    "content": "{\n  \"name\": \"server\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"start\": \"cross-env DEBUG=app,requests node index.js\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"axios\": \"^0.20.0\",\n    \"cross-env\": \"^7.0.2\",\n    \"debug\": \"^4.2.0\",\n    \"dotenv\": \"^8.2.0\",\n    \"express\": \"^4.17.1\"\n  }\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/router/api/index.js",
    "content": "const { Router } = require(\"express\");\nconst { API } = require(\"../../api\");\n\nconst router = Router();\n\nrouter.post(\"/convert\", API.getTwitterThread);\n\nmodule.exports.apiRouter = router;\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/router/index.js",
    "content": "module.exports.api = require(\"./api\").apiRouter;\nmodule.exports.pages = require(\"./pages\").pagesRouter;\n"
  },
  {
    "path": "misc/playground/cli/spike/phase2-server/router/pages/index.js",
    "content": "const { Router } = require(\"express\");\n\nconst router = Router();\n\nrouter.get(\"/*\", (req, res) => {\n res.send({\n  message: \"This is the homepage\"\n });\n});\n\nmodule.exports.pagesRouter = router;\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/README.md",
    "content": "# What this does ?\n\n- convert JSON files to pdf\n\n# Requirements\n\n- a Browser on your PC or try it with any online IDEs within the browser\n- ensure that all thes files are in same folder when you run the html file on pc\n\n# Dependency\n\n- html+css+javscript no other dependency\n\n# why NO Dependency ?\n\n- you are free to use one! but why use it when you can do that job without it\n\n# way to do it\n\n- paste the code and you are good to go!\n- There is a button provided to print and you can select the format in which you want to save the print like pdf\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/json2pdf.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width\">\n    <title> TWEETS IN PDF FORMAT </title>\n    <link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\" />\n    <script defer src=\"script.js\"></script>\n  </head>\n<body>\n<div id=\"output\">\n    <table id=\"tweetsTable\">\n        <tr>\n            <th>Name</th><th>TwitterHandle</th><th>image</th><th>createdAt</th><th>tweet</th>\n        </tr>\n    </table>\n</div>\n  </body>\n</html>"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/json2pdf.js",
    "content": "//data\nconst data =[\n  {\nname: 'Naval',\ntwitterHandle: 'naval',\nimage: 'https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg',\ncreateAt: '2018-05-31',\ntweet: 'How to Get Rich (without getting lucky):'\n   },\n  {\nname: 'Naval',\ntwitterHandle: 'naval',\nimage: 'https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg',\ncreateAt: '2018-05-31',\ntweet: 'How to Get Rich 1(without getting lucky):'\n  }\n];\n\nfunction displayTable(tweets){\n// accessing table element\nconst table = document.getElementById('tweetsTable');\n//loop over the tweets\ntweets.forEach(function (currentTweet)\n{\n\nconst row =document.createElement('tr'); //creating row\nconst k =['name','twitterHandle','image','createAt','tweet']; //key(k) of tweets \n//loop over them and add them to cells and later add cells to rows\nfor(let j=0;j<k.length;++j){\nlet cell = document.createElement('td')\ncell.innerHTML=currentTweet[k[j]];\nrow.appendChild(cell);\n}\ntable.appendChild(row)\n})\n\n}\ndisplayTable(data);\nwindow.print();\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/simple-pdf-to-json/newjson2pdf.css",
    "content": "@media print {\n  #myButton {\n    display: none;\n  }\n}\n#myButton {\n  box-shadow: inset 0px 0px 15px 3px #23395e;\n  background: linear-gradient(to bottom, #2e466e 5%, #415989 100%);\n  background-color: #2e466e;\n  border-radius: 17px;\n  border: 1px solid #1f2f47;\n  /*display:inline-block;*/\n  cursor: pointer;\n  color: #ffffff;\n  font-family: Arial;\n  font-size: 15px;\n  padding: 6px 13px;\n  text-decoration: none;\n  text-shadow: 0px 1px 0px #263666;\n}\n#myButton:active {\n  position: relative;\n  top: 1px;\n}\n/*just inspiration from  Puppeteer*/\n* {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n    Ubuntu, Cantarell, \"Open Sans\", \"Helvetica Neue\", sans-serif;\n}\n\nbody {\n  margin: 5% auto;\n}\n\n#title {\n  text-align: center;\n}\n\nul {\n  list-style-type: none;\n  display: flex;\n  align-items: center;\n  flex-direction: column;\n}\n\nul li {\n  margin: 20px;\n}\n\n.tweetContainer {\n  display: flex;\n  flex-direction: column;\n}\n\n.tweetContainer * {\n  margin: 0;\n  padding: 0;\n}\n\n.tweetContainer .header {\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  margin-bottom: 10px;\n}\n\n.tweetContainer .header img {\n  width: 75px;\n  height: 75px;\n  border-radius: 50%;\n\n  margin-right: 10px;\n}\n\n.tweetContainer .header > div {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: center;\n}\n\n.tweetContainer .header > div p {\n  font-size: 14px;\n}\n\n.tweetContainer .header > div span {\n  font-style: italic;\n  color: rgb(45, 45, 45);\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/simple-pdf-to-json/newjson2pdf.html",
    "content": "<html>\n  <header>\n    <link href=\"newjson2pdf.css\" rel=\"stylesheet\" type=\"text/css\" />\n    <script defer src=\"newjson2pdf.js\"></script>\n  </header>\n\n  <body>\n    <h1 id=\"title\">Your Tweet Thread</h1>\n    <ul></ul>\n    <button id=\"myButton\">Tweet in Pdf</button>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/simple-pdf-to-json/newjson2pdf.js",
    "content": "//'{\"name\": \"Naval\",\"twitterHandle\": \"naval\", \"city\":\"New York\",\"image\":\"abs.jpg\",\"tweet\": \"Tweet1\",\"createAt\": \"2018-05-31\"}')\n\n//parse json before using it as data\nvar data = [\n  {\n    name: \"Adam \",\n    twitterHandle: \"@horse\",\n    createdAt: \"2018-05-31\",\n    tweet: \"How to Get Rich (without getting lucky) 1\",\n    image: \"http://placekitten.com/g/200/300\",\n  },\n  {\n    name: \"Kate \",\n    twitterHandle: \"@cat\",\n    createdAt: \"2018-05-31\",\n    tweet: \"How to Get Rich (without getting lucky) 2\",\n    image: \"http://placekitten.com/g/200/300\",\n  },\n  {\n    name: \"Jenny \",\n    twitterHandle: \"@squirrel\",\n    createdAt: \"2018-05-31\",\n    tweet: \"How to Get Rich (without getting lucky) 3\",\n    image: \"http://placekitten.com/g/200/300\",\n  },\n];\nfunction display(_tweet) {\n  let myUl = document.querySelector(\"ul\");\n  data.forEach(function (obj) {\n    //This is just creating the skeleton\n    const myList = document.createElement(\"li\");\n    //first division creation\n    const div1 = document.createElement(\"div\");\n    div1.className = \"tweetContainer\";\n    myList.appendChild(div1);\n    //second divison creation\n    const div2 = document.createElement(\"div\");\n    div2.className = \"header\";\n    div1.appendChild(div2);\n    let p1 = document.createElement(\"p\"); //sibling of div2\n    p1.textContent = obj.tweet;\n    div1.insertBefore(p1, div1.firstElementChild.nextSibling); //insertafter() property is not there\n    //sibling of div2 attached\n\n    let image = document.createElement(\"img\");\n    image.src = obj.image;\n    div2.appendChild(image);\n    const div3 = document.createElement(\"div\");\n    div2.insertBefore(div3, div2.firstElementChild.nextSibling);\n    const h3 = document.createElement(\"h3\");\n    div3.appendChild(h3);\n    let p2 = document.createElement(\"p\");\n    div3.insertBefore(p2, div3.firstElementChild.nextSibling);\n    let span1 = document.createElement(\"span\");\n    span1.textContent = obj.twitterHandle;\n    h3.textContent = obj.name;\n    h3.appendChild(span1);\n    let span2 = document.createElement(\"span\");\n    span2.textContent = obj.createdAt;\n    p2.appendChild(span2);\n    myUl.appendChild(myList);\n  });\n}\ndisplay(data);\n//printing function\nlet button = document.querySelector(\"#myButton\");\nbutton.addEventListener(\"click\", function () {\n  document.querySelector(\"ul\");\n  window.print();\n});\n"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/simple-pdf-to-json/old codes/oldjson2pdf.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width\">\n    <title> TWEETS IN PDF FORMAT </title>\n    <link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\" />\n    <script defer src=\"script.js\"></script>\n  </head>\n<body>\n<div id=\"output\">\n    <table id=\"tweetsTable\">\n        <tr>\n            <th>Name</th><th>TwitterHandle</th><th>image</th><th>createdAt</th><th>tweet</th>\n        </tr>\n    </table>\n</div>\n  </body>\n</html>"
  },
  {
    "path": "misc/playground/cli/spike/simple-pdf-to-json/simple-pdf-to-json/old codes/oldjson2pdf.js",
    "content": "//data\nconst data =[\n  {\nname: 'Naval',\ntwitterHandle: 'naval',\nimage: 'https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg',\ncreateAt: '2018-05-31',\ntweet: 'How to Get Rich (without getting lucky):'\n   },\n  {\nname: 'Naval',\ntwitterHandle: 'naval',\nimage: 'https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg',\ncreateAt: '2018-05-31',\ntweet: 'How to Get Rich 1(without getting lucky):'\n  }\n];\n\nfunction displayTable(tweets){\n// accessing table element\nconst table = document.getElementById('tweetsTable');\n//loop over the tweets\ntweets.forEach(function (currentTweet)\n{\n\nconst row =document.createElement('tr'); //creating row\nconst k =['name','twitterHandle','image','createAt','tweet']; //key(k) of tweets \n//loop over them and add them to cells and later add cells to rows\nfor(let j=0;j<k.length;++j){\nlet cell = document.createElement('td')\ncell.innerHTML=currentTweet[k[j]];\nrow.appendChild(cell);\n}\ntable.appendChild(row)\n})\n\n}\ndisplayTable(data);\nwindow.print();\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-cli-node/README.md",
    "content": "Simple nodejs CLI application following the format from issue #288.\n\n# Options:  \nOptions:  \n\n--version: Show version number  \n\n-o, --option: twindle command line options, choices ['pdf', 'epub', 'mobi', 'md'] \n\n-t, --thread-id: thread id of twitter thread that is to be converted  \n\n-h, --help: Show help        \n\n# Usage\n`node twindle --thread-id <thread/conversation id> -o <file type>`\n\n# Example:\n`node twindle --thread-id 1279940000004973111 -o pdf`  \ngenerates a pdf document from the text"
  },
  {
    "path": "misc/playground/cli/spike/twindle-cli-node/package.json",
    "content": "{\n  \"name\": \"twindle_cli_node\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"twindle.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"yargs\": \"^16.1.0\"\n  }\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-cli-node/twindle.js",
    "content": "//yargs is a npm module that makes passing command line arguments easy\n//require the module in your package to use it\nconst yargs = require(\"yargs\");\n\n\n//create a variable that'll contain the yargs object and pass in its methods that you'll need\nconst args = yargs.options({\n    \"o\": {\n        alias: \"option\",\n        describe: \"twindle command line options\\n choices 'pdf', 'epub', 'mobi', 'md'\",\n        demandOption: true\n    },\n    \"t\": {\n        alias: \"thread-id\",\n        describe: \"thread id of twitter thread that is to be converted\",\n        demandOption: true\n    }\n}).help().alias(\"help\", \"h\")\n.usage(\"node twindle --thread-id <thread id> -o <file type>\").argv;\n\n/* \n--YARGS METHODS--\n    -options() creates the command line options for the application\n    -help() parameter logs a usage string and the options to console and exits the app,\n        without parameters --help option will be used\n    -alias() sets an alternate name for an option\n    -usage() creates usage string that'll be logged when --help is called\n*/\n\n\n//if/elif block to check what options were passed and log a message\nif (args.option === \"pdf\") {\n    console.log(\"pdf generated from thread \" + args[\"t\"]);\n} else if (args.option === \"epub\") {\n    console.log(\"epub document generated from thread \" + args[\"t\"]);\n} else if (args.option === \"md\") {\n    console.log(\"markdown document generated from thread \" + args[\"t\"]);\n} else if (args.option === \"mobi\") {\n    console.log(\"mobi document generated from thread \" + args[\"t\"]);\n} else{\n    console.error(\"incorrect choise\")\n}"
  },
  {
    "path": "misc/playground/cli/spike/twindle-hello-world-pdf/package.json",
    "content": "{\n  \"name\": \"twindle_hello_world_pdf\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"pdf.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"pdfjs\": \"^2.4.1\"\n  }\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-hello-world-pdf/pdf.js",
    "content": "//pdfjs is a pdf generation npm module that is easy to use and has alot of features\nconst pdf = require('pdfjs')\n\n//fs is the default node js filesystem module\nconst fs = require('fs')\n\n//create a new pdf document object and pass in some options\nconst doc = new pdf.Document({\n    font:    require('pdfjs/font/Helvetica'),\n    padding: 10,\n    fontSize: 20,\n    properties: {\n        title: \"Hello World\",\n        author: \"Michaelcosj@Twindle\"\n    }\n  })\n\n  //pipe means to pass information from one process to another\n  //here the newly created write stream is passed to the doc object\n  doc.pipe(fs.createWriteStream('helloworld.pdf'));\n\n  //pass a text to the doc object, the link property makes the text link to a url\n  doc.text(\"Hello World!\", {link: \"https://github.com/twindle-co/twindle\"});\n  \n  //finish writing the pdf document\n  doc.end()\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-thread/Readme.md",
    "content": "node twindle thread-id -o pdf\n\naaa\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-thread/script.js",
    "content": "main();\nfunction main(){\n    var myData;\n    myData=require(\"./twit_thread.json\");\n    console.log(\"Tweets\",myData);  \n}\n\n\n"
  },
  {
    "path": "misc/playground/cli/spike/twindle-thread/twit_thread.json",
    "content": "[\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"How to Get Rich (without getting lucky):\"\n\t},\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"How to Get Rich 1(without getting lucky):\"\n\t}\n]\n"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/README.md",
    "content": "Making REST Calls to  Twitter Endpoints - output being written to the console right now\n\nCode Structure: playground -> cli -> a new folder called twitter-api has been included\nThis is a NodeJS module but except for node-fetch no other module has been used\nThere are three scripts:\n\n> script-version2-conversation.js: Contains hard-coded Twitter Endpoints for version 2. Process - using a sample Tweet URL, extract the tweet id and twitter handle name, make a Tweet lookup call to get the first tweet's details and particularly the conversationId. If the first tweet is older than 7 days I am logging an error - then make a second call to get the tweets on the conversation using the conversationId but also made by the same user(this will exclude replies) - from the response, find the direct replies one by one\n\n> script-version1-usertimeline.js: Contains hard coded Twitter Endpoints for getting the user's Twitter timeline. Process - using a sample Tweet URL, extract the tweet id and twitter handle name, make a Tweet lookup call to get the first tweet's details. If the first tweet is older than 7 days I am logging an error. Since there is no way to track a particular conversation's thread(no conversationId), we hit the user timeline endpoint and ask for all tweets made by the user since the first tweet. Now from the response, we filter with a field called in_reply_to_id_str pointing to the parent tweet. Thus we construct the chain of responses. Caveat: Only 200 max tweets can be returned and so all replies are not guaranteed - particularly if the user is super active.\n\n> script-version1-searchendpoint.js: Contains hard coded Twitter Search Endpoints. Process - using a sample Tweet URL, extract the tweet id and twitter handle name, make a Tweet lookup call to get the first tweet's details. If the first tweet is older than 7 days I am logging an error. Since there is no way to track a particular conversation's thread(no conversationId), we hit the search endpoint by constructing a standard query using the operators from:twitterHandle+to:twitterHandle - this will only return replies made by the user to himself - from the response we filter with a field called in_reply_to_id_str pointing to the parent tweet. Thus we construct the chain of responses. Caveat: Only 200 max tweets can be returned and so all replies are not guaranteed - particularly if the user is super active.\n\nStandard responses from the Twitter endpoints have also been saved under the folder responses - includes all tweet fields - so we can use whatever field is needed in the output file.\n\nThings to do to test the scripts:\n\n> Please remove <Auth_Token_Here> at the top of the script files and replace with the Auth Header Token provided by the twitter API\n\n> I have used a sample Tweet that was made at the time of testing the script - please pick a recent tweet and replace SAMPLE_TWEET with that tweet. \n\n> The function getTweets(SAMPLE_TWEET) is being called - and that is the starting point of execution\n\nI am not an expert in Javascript coding - so any issues including coding styles and standards can be raised on this PR. Thank @UnevenCoder for his inputs in writing this.\n\n\n\n## Attach Screenshot:\n![screenshot](https://user-images.githubusercontent.com/64691316/97374048-62d69080-18dd-11eb-9884-194bd9c02d9a.png)\n\n\n\n> Note 2 code reviewer approval needed. Approach in twitter group & discord channel.\n"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/package.json",
    "content": "{\n    \"name\": \"twitter-api\",\n    \"version\": \"1.0.0\",\n    \"description\": \"\",\n    \"main\": \"script.js\",\n    \"scripts\": {\n      \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n    },\n    \"keywords\": [],\n    \"author\": \"\",\n    \"license\": \"ISC\",\n    \"dependencies\": {\n      \"node-fetch\": \"^2.6.1\"\n    }\n  }\n  "
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/responses/response-version1-searchendpoint.json",
    "content": "{\n    \"statuses\":[\n       {\n          \"created_at\":\"Tue Oct 27 13:51:42 +0000 2020\",\n          \"id\":1321087179939057665,\n          \"id_str\":\"1321087179939057665\",\n          \"full_text\":\"I hope people understand how serious a threat to democracy this is: Kavanaugh is now \\u201cswing justice\\u201d on 6-3 court &amp; he\\u2019s spreading same disinformation about counting mail ballots as Trump\\n\\nWe need to vote in record numbers to overcome rigged Supreme Court https:\\/\\/t.co\\/Elnsnvqr4u\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             283\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                {\n                   \"url\":\"https:\\/\\/t.co\\/Elnsnvqr4u\",\n                   \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/2020-elections\\/2020\\/10\\/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election\\/\",\n                   \"display_url\":\"motherjones.com\\/2020-elections\\u2026\",\n                   \"indices\":[\n                      260,\n                      283\n                   ]\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320899108606017539,\n          \"in_reply_to_status_id_str\":\"1320899108606017539\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":126,\n          \"favorite_count\":313,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Tue Oct 27 12:19:01 +0000 2020\",\n          \"id\":1321063854504353794,\n          \"id_str\":\"1321063854504353794\",\n          \"full_text\":\"Given persistent USPS delays (it takes an average of 10 days for letter to be delivered in Wisconsin), I would urge voters to drop off their mail ballots or vote in person rather than risk mail ballot not being counted because USPS didn\\u2019t deliver by Election Day\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             262\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1321060511245606914,\n          \"in_reply_to_status_id_str\":\"1321060511245606914\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":752,\n          \"favorite_count\":1549,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Tue Oct 27 01:36:52 +0000 2020\",\n          \"id\":1320902249846083586,\n          \"id_str\":\"1320902249846083586\",\n          \"full_text\":\"If you\\u2019re feeling despair tonight (and I know I am), remember massive turnout can overcome massive suppression \\n\\nWe don\\u2019t have to accept an illegitimate system, we can vote to change it\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             185\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320783852219105287,\n          \"in_reply_to_status_id_str\":\"1320783852219105287\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":1267,\n          \"favorite_count\":5716,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Tue Oct 27 01:24:23 +0000 2020\",\n          \"id\":1320899108606017539,\n          \"id_str\":\"1320899108606017539\",\n          \"full_text\":\"Kagan dissent in Wisconsin case: \\\"the Court\\u2019s decision will disenfranchise large numbers of responsible voters in the midst of hazardous pandemic conditions\\\" https:\\/\\/t.co\\/Q4pEXmDN96\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             157\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1320898952183619584,\n                   \"id_str\":\"1320898952183619584\",\n                   \"indices\":[\n                      158,\n                      181\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Q4pEXmDN96\",\n                   \"display_url\":\"pic.twitter.com\\/Q4pEXmDN96\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320899108606017539\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":834,\n                         \"h\":476,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":834,\n                         \"h\":476,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":680,\n                         \"h\":388,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1320898952183619584,\n                   \"id_str\":\"1320898952183619584\",\n                   \"indices\":[\n                      158,\n                      181\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Q4pEXmDN96\",\n                   \"display_url\":\"pic.twitter.com\\/Q4pEXmDN96\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320899108606017539\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":834,\n                         \"h\":476,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":834,\n                         \"h\":476,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":680,\n                         \"h\":388,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320883524954660867,\n          \"in_reply_to_status_id_str\":\"1320883524954660867\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":539,\n          \"favorite_count\":2148,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Tue Oct 27 00:22:27 +0000 2020\",\n          \"id\":1320883524954660867,\n          \"id_str\":\"1320883524954660867\",\n          \"full_text\":\"Insane rhetoric from Kavanaugh decrying \\\"chaos &amp; suspicions of impropriety that can ensue if thousands of absentee ballots flow in after election day and potentially flip the results of an election\\\"\\n\\nThis comes on some night Trump tweets winner of election must be announced Nov 3 https:\\/\\/t.co\\/Y9iZcBGOGy\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             284\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1320882150552571905,\n                   \"id_str\":\"1320882150552571905\",\n                   \"indices\":[\n                      285,\n                      308\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Y9iZcBGOGy\",\n                   \"display_url\":\"pic.twitter.com\\/Y9iZcBGOGy\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320883524954660867\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1320882150552571905,\n                   \"id_str\":\"1320882150552571905\",\n                   \"indices\":[\n                      285,\n                      308\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Y9iZcBGOGy\",\n                   \"display_url\":\"pic.twitter.com\\/Y9iZcBGOGy\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320883524954660867\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":415,\n                         \"h\":410,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320877150770241541,\n          \"in_reply_to_status_id_str\":\"1320877150770241541\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":695,\n          \"favorite_count\":1855,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Mon Oct 26 23:57:08 +0000 2020\",\n          \"id\":1320877150770241541,\n          \"id_str\":\"1320877150770241541\",\n          \"full_text\":\"Kavanaugh cites Bush v. Gore to justify making it harder to vote in Wisconsin\\n\\nKavanaugh, Roberts &amp; Barrett all served on George W. Bush legal team in Florida 2000 recount &amp; worked to make sure only GOP votes counted https:\\/\\/t.co\\/NhNessbwoB\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             224\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1320876915889262592,\n                   \"id_str\":\"1320876915889262592\",\n                   \"indices\":[\n                      225,\n                      248\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                   \"url\":\"https:\\/\\/t.co\\/NhNessbwoB\",\n                   \"display_url\":\"pic.twitter.com\\/NhNessbwoB\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320877150770241541\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1320876915889262592,\n                   \"id_str\":\"1320876915889262592\",\n                   \"indices\":[\n                      225,\n                      248\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                   \"url\":\"https:\\/\\/t.co\\/NhNessbwoB\",\n                   \"display_url\":\"pic.twitter.com\\/NhNessbwoB\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320877150770241541\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"large\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":412,\n                         \"h\":448,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320872258085310466,\n          \"in_reply_to_status_id_str\":\"1320872258085310466\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":2198,\n          \"favorite_count\":3993,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Mon Oct 26 19:41:04 +0000 2020\",\n          \"id\":1320812709613588480,\n          \"id_str\":\"1320812709613588480\",\n          \"full_text\":\"\\\"Anger over perceived voter suppression tactics is fueling Black voters eagerness to cast early ballots in Georgia\\\"\\n\\n2.7 million Georgia voters have cast a ballot already \\u2014 a nearly 110 percent increase from 2016 https:\\/\\/t.co\\/rdjblfw3XV\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             236\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                {\n                   \"url\":\"https:\\/\\/t.co\\/rdjblfw3XV\",\n                   \"expanded_url\":\"https:\\/\\/www.politico.com\\/news\\/2020\\/10\\/26\\/georgia-voter-suppression-black-turnout-432405?nname=playbook&nid=0000014f-1646-d88f-a1cf-5f46b7bd0000&nrid=0000014e-f109-dd93-ad7f-f90d0def0000&nlid=63031\",\n                   \"display_url\":\"politico.com\\/news\\/2020\\/10\\/2\\u2026\",\n                   \"indices\":[\n                      213,\n                      236\n                   ]\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320058134753402880,\n          \"in_reply_to_status_id_str\":\"1320058134753402880\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":40,\n          \"favorite_count\":137,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Mon Oct 26 17:46:24 +0000 2020\",\n          \"id\":1320783852219105287,\n          \"id_str\":\"1320783852219105287\",\n          \"full_text\":\"While he steals another Supreme Court seat for Trump, Mitch McConnell has been blocking coronavirus relief legislation for 164 days, legislation to restore Voting Rights Act for 324 days &amp; legislation to prevent foreign election interference for 368 days\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             258\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320727906600030218,\n          \"in_reply_to_status_id_str\":\"1320727906600030218\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":1517,\n          \"favorite_count\":3949,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Mon Oct 26 00:41:42 +0000 2020\",\n          \"id\":1320525980411301889,\n          \"id_str\":\"1320525980411301889\",\n          \"full_text\":\"While he packs the courts for Trump, Mitch McConnell has been blocking coronavirus relief legislation for 163 days, legislation to restore Voting Rights Act for 323 days &amp; legislation to prevent foreign election interference for 367 days\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             241\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1320410270225829889,\n          \"in_reply_to_status_id_str\":\"1320410270225829889\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":202,\n          \"favorite_count\":486,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Sat Oct 24 17:42:39 +0000 2020\",\n          \"id\":1320058134753402880,\n          \"id_str\":\"1320058134753402880\",\n          \"full_text\":\".@MrPruneJuice waited 5 hours 7 min to vote on 1st day of early voting in GA but after \\\"horror stories\\\" of voter suppression in @staceyabrams race \\\"I wanted to get my early vote in\\\" he told me\\n\\n2.5 million early votes already cast in GA, exceeding total 2016 early voting turnout https:\\/\\/t.co\\/Q87PROZj0t\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             279\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                {\n                   \"screen_name\":\"MrPruneJuice\",\n                   \"name\":\"Matt\",\n                   \"id\":104184684,\n                   \"id_str\":\"104184684\",\n                   \"indices\":[\n                      1,\n                      14\n                   ]\n                },\n                {\n                   \"screen_name\":\"staceyabrams\",\n                   \"name\":\"Stacey Abrams\",\n                   \"id\":216065430,\n                   \"id_str\":\"216065430\",\n                   \"indices\":[\n                      128,\n                      141\n                   ]\n                }\n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1320057887746592775,\n                   \"id_str\":\"1320057887746592775\",\n                   \"indices\":[\n                      280,\n                      303\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElHJp6oWkAc9rMP.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElHJp6oWkAc9rMP.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Q87PROZj0t\",\n                   \"display_url\":\"pic.twitter.com\\/Q87PROZj0t\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320058134753402880\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"small\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1320057887746592775,\n                   \"id_str\":\"1320057887746592775\",\n                   \"indices\":[\n                      280,\n                      303\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElHJp6oWkAc9rMP.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElHJp6oWkAc9rMP.png\",\n                   \"url\":\"https:\\/\\/t.co\\/Q87PROZj0t\",\n                   \"display_url\":\"pic.twitter.com\\/Q87PROZj0t\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320058134753402880\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"small\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"medium\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":599,\n                         \"h\":439,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319804181457371136,\n          \"in_reply_to_status_id_str\":\"1319804181457371136\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":114,\n          \"favorite_count\":477,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Sat Oct 24 00:53:32 +0000 2020\",\n          \"id\":1319804181457371136,\n          \"id_str\":\"1319804181457371136\",\n          \"full_text\":\"Harris Co clerk @CGHollins deserves lot of credit for making voting easier:\\n\\n-3x early voting locations\\n\\n-drive-through voting\\n\\n-24\\/7 voting\\n\\n-arenas as polling places\\n\\n\\\"Voters are responding by saying, \\u2018I\\u2019ll show you,\\u2019 &amp; coming out in record numbers to have their voices heard.\\u201d https:\\/\\/t.co\\/KLXPb8wK1E\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             283\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                {\n                   \"screen_name\":\"CGHollins\",\n                   \"name\":\"Chris Hollins\",\n                   \"id\":30976233,\n                   \"id_str\":\"30976233\",\n                   \"indices\":[\n                      16,\n                      26\n                   ]\n                }\n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1319804109403361281,\n                   \"id_str\":\"1319804109403361281\",\n                   \"indices\":[\n                      284,\n                      307\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElDi2EoWkAE6I3w.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElDi2EoWkAE6I3w.png\",\n                   \"url\":\"https:\\/\\/t.co\\/KLXPb8wK1E\",\n                   \"display_url\":\"pic.twitter.com\\/KLXPb8wK1E\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1319804181457371136\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"medium\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1319804109403361281,\n                   \"id_str\":\"1319804109403361281\",\n                   \"indices\":[\n                      284,\n                      307\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElDi2EoWkAE6I3w.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElDi2EoWkAE6I3w.png\",\n                   \"url\":\"https:\\/\\/t.co\\/KLXPb8wK1E\",\n                   \"display_url\":\"pic.twitter.com\\/KLXPb8wK1E\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1319804181457371136\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"medium\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"small\":{\n                         \"w\":593,\n                         \"h\":224,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319777914947276800,\n          \"in_reply_to_status_id_str\":\"1319777914947276800\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":343,\n          \"favorite_count\":1491,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Fri Oct 23 23:09:09 +0000 2020\",\n          \"id\":1319777914947276800,\n          \"id_str\":\"1319777914947276800\",\n          \"full_text\":\"More than 1 million votes have already been cast in Harris County TX, exceeding 2016 early voting total with a week of early voting left\\n\\n@LinaHidalgoTX told me Greg Abbott's plan closing mail dropoff sites backfired on GOP\\n\\n\\\"It was so transparently about suppression,\\u201d she said.\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             279\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                {\n                   \"screen_name\":\"LinaHidalgoTX\",\n                   \"name\":\"Lina Hidalgo\",\n                   \"id\":247174365,\n                   \"id_str\":\"247174365\",\n                   \"indices\":[\n                      138,\n                      152\n                   ]\n                }\n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319763016452395009,\n          \"in_reply_to_status_id_str\":\"1319763016452395009\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":1155,\n          \"favorite_count\":4393,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Thu Oct 22 01:58:30 +0000 2020\",\n          \"id\":1319095755219607553,\n          \"id_str\":\"1319095755219607553\",\n          \"full_text\":\"Blocking legislation to prevent foreign election interference is one of 29 ways GOP making it harder to vote https:\\/\\/t.co\\/duQ3ZM8Ofb\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             132\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                {\n                   \"url\":\"https:\\/\\/t.co\\/duQ3ZM8Ofb\",\n                   \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/politics\\/2020\\/10\\/29-ways-trump-and-the-gop-are-making-it-harder-to-vote\\/\",\n                   \"display_url\":\"motherjones.com\\/politics\\/2020\\/\\u2026\",\n                   \"indices\":[\n                      109,\n                      132\n                   ]\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319063702361018376,\n          \"in_reply_to_status_id_str\":\"1319063702361018376\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":170,\n          \"favorite_count\":404,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Thu Oct 22 00:48:13 +0000 2020\",\n          \"id\":1319078067424399361,\n          \"id_str\":\"1319078067424399361\",\n          \"full_text\":\"Sotomayor dissent quotes Howard Porter Jr., a Black man in 70s with asthma &amp; Parkinson\\u2019s Disease: \\\"So many of my [ancestors] even died to vote. And while I don\\u2019t mind dying to vote, I think we\\u2019re past that time.\\\"\\n\\nRoberts Court forcing voters to choose between vote &amp; lives https:\\/\\/t.co\\/NT4lUDzWle\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             281\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ],\n             \"media\":[\n                {\n                   \"id\":1319077754390925318,\n                   \"id_str\":\"1319077754390925318\",\n                   \"indices\":[\n                      282,\n                      305\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/Ek5OOpWXUAYdtvv.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/Ek5OOpWXUAYdtvv.png\",\n                   \"url\":\"https:\\/\\/t.co\\/NT4lUDzWle\",\n                   \"display_url\":\"pic.twitter.com\\/NT4lUDzWle\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1319078067424399361\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"medium\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      },\n                      \"small\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"extended_entities\":{\n             \"media\":[\n                {\n                   \"id\":1319077754390925318,\n                   \"id_str\":\"1319077754390925318\",\n                   \"indices\":[\n                      282,\n                      305\n                   ],\n                   \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/Ek5OOpWXUAYdtvv.png\",\n                   \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/Ek5OOpWXUAYdtvv.png\",\n                   \"url\":\"https:\\/\\/t.co\\/NT4lUDzWle\",\n                   \"display_url\":\"pic.twitter.com\\/NT4lUDzWle\",\n                   \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1319078067424399361\\/photo\\/1\",\n                   \"type\":\"photo\",\n                   \"sizes\":{\n                      \"medium\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      },\n                      \"thumb\":{\n                         \"w\":150,\n                         \"h\":150,\n                         \"resize\":\"crop\"\n                      },\n                      \"small\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      },\n                      \"large\":{\n                         \"w\":414,\n                         \"h\":623,\n                         \"resize\":\"fit\"\n                      }\n                   }\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319075187581747200,\n          \"in_reply_to_status_id_str\":\"1319075187581747200\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":310,\n          \"favorite_count\":745,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Wed Oct 21 23:51:08 +0000 2020\",\n          \"id\":1319063702361018376,\n          \"id_str\":\"1319063702361018376\",\n          \"full_text\":\"SHIELD Act - requiring campaigns to notify FBI &amp; FEC if contacted by foreign actors - was passed by House October 23, 2019. McConnell has been blocking a vote on it for a year\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             179\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1319061747358142467,\n          \"in_reply_to_status_id_str\":\"1319061747358142467\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":525,\n          \"favorite_count\":1329,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Tue Oct 20 18:02:49 +0000 2020\",\n          \"id\":1318613659287982081,\n          \"id_str\":\"1318613659287982081\",\n          \"full_text\":\"Trump says he wants SCOTUS to \\\"look at the ballots\\\"\\n\\n4 conservative justices voted yesterday to reject mail ballots in PA\\n\\nGOP rushing to confirm Amy Coney Barrett so she's 5th vote for Trump in post-election dispute\\n\\nPay attention\\n\\nhttps:\\/\\/t.co\\/9EA67qvVrn\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             256\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                {\n                   \"url\":\"https:\\/\\/t.co\\/9EA67qvVrn\",\n                   \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/2020-elections\\/2020\\/10\\/supreme-court-hints-at-a-bush-v-gore-redux-with-barrett-breaking-the-tie\\/\",\n                   \"display_url\":\"motherjones.com\\/2020-elections\\u2026\",\n                   \"indices\":[\n                      233,\n                      256\n                   ]\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1318336526363856898,\n          \"in_reply_to_status_id_str\":\"1318336526363856898\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":193,\n          \"favorite_count\":301,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       },\n       {\n          \"created_at\":\"Mon Oct 19 23:41:36 +0000 2020\",\n          \"id\":1318336526363856898,\n          \"id_str\":\"1318336526363856898\",\n          \"full_text\":\"The fact that 4 conservative justices would've overruled PA Supreme Court is very bad sign for voting rights &amp; shows why GOP so desperate to confirm Amy Coney Barrett before election. They want to do Bush v Gore on steroids https:\\/\\/t.co\\/E4pKYWwz6z\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             251\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                {\n                   \"url\":\"https:\\/\\/t.co\\/E4pKYWwz6z\",\n                   \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/politics\\/2020\\/10\\/trump-wants-a-repeat-of-bush-v-gore-amy-coney-barrett-might-make-it-happen\\/\",\n                   \"display_url\":\"motherjones.com\\/politics\\/2020\\/\\u2026\",\n                   \"indices\":[\n                      228,\n                      251\n                   ]\n                }\n             ]\n          },\n          \"metadata\":{\n             \"iso_language_code\":\"en\",\n             \"result_type\":\"recent\"\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":1318330577871904769,\n          \"in_reply_to_status_id_str\":\"1318330577871904769\",\n          \"in_reply_to_user_id\":15952856,\n          \"in_reply_to_user_id_str\":\"15952856\",\n          \"in_reply_to_screen_name\":\"AriBerman\",\n          \"user\":{\n             \"id\":15952856,\n             \"id_str\":\"15952856\",\n             \"name\":\"Ari Berman\",\n             \"screen_name\":\"AriBerman\",\n             \"location\":\"New York\",\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                         \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                         \"display_url\":\"amzn.to\\/2asHld2\",\n                         \"indices\":[\n                            77,\n                            100\n                         ]\n                      }\n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":159657,\n             \"friends_count\":2782,\n             \"listed_count\":2703,\n             \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n             \"favourites_count\":17410,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":false,\n             \"verified\":true,\n             \"statuses_count\":26571,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0A4778\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n             \"profile_link_color\":\"C7124B\",\n             \"profile_sidebar_border_color\":\"FFFFFF\",\n             \"profile_sidebar_fill_color\":\"FF1D2E\",\n             \"profile_text_color\":\"7A3060\",\n             \"profile_use_background_image\":false,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":670,\n          \"favorite_count\":1556,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"possibly_sensitive\":false,\n          \"lang\":\"en\"\n       }\n    ],\n    \"search_metadata\":{\n       \"completed_in\":0.036,\n       \"max_id\":1321087179939057665,\n       \"max_id_str\":\"1321087179939057665\",\n       \"next_results\":\"?max_id=1318336526363856897&q=from%3AAriBerman%20to%3AAriBerman&count=100&include_entities=1\",\n       \"query\":\"from%3AAriBerman+to%3AAriBerman\",\n       \"refresh_url\":\"?since_id=1321087179939057665&q=from%3AAriBerman%20to%3AAriBerman&include_entities=1\",\n       \"count\":100,\n       \"since_id\":0,\n       \"since_id_str\":\"0\"\n    }\n }"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/responses/response-version1-user-timeline.json",
    "content": "[\n    {\n       \"created_at\":\"Tue Oct 27 15:03:12 +0000 2020\",\n       \"id\":1321105172400087040,\n       \"id_str\":\"1321105172400087040\",\n       \"full_text\":\"GOP: we shouldn't count votes that are mailed too close to Election Day, but we should absolutely confirm a Supreme Court justice 8 days before Nov 3 when 65 million already voted https:\\/\\/t.co\\/Elnsnvqr4u\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          203\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             {\n                \"url\":\"https:\\/\\/t.co\\/Elnsnvqr4u\",\n                \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/2020-elections\\/2020\\/10\\/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election\\/\",\n                \"display_url\":\"motherjones.com\\/2020-elections\\u2026\",\n                \"indices\":[\n                   180,\n                   203\n                ]\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":106,\n       \"favorite_count\":304,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 14:44:30 +0000 2020\",\n       \"id\":1321100463660507137,\n       \"id_str\":\"1321100463660507137\",\n       \"full_text\":\"Mitch McConnell rushed through Amy Coney Barrett\\u2019s nomination in 30 days but he\\u2019s been blocking coronavirus relief legislation for 165 days &amp; counting https:\\/\\/t.co\\/PYVrdpPaky\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          154\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             {\n                \"url\":\"https:\\/\\/t.co\\/PYVrdpPaky\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/jesselehrich\\/status\\/1320901441272373250\",\n                \"display_url\":\"twitter.com\\/jesselehrich\\/s\\u2026\",\n                \"indices\":[\n                   155,\n                   178\n                ]\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":true,\n       \"quoted_status_id\":1320901441272373250,\n       \"quoted_status_id_str\":\"1320901441272373250\",\n       \"quoted_status_permalink\":{\n          \"url\":\"https:\\/\\/t.co\\/PYVrdpPaky\",\n          \"expanded\":\"https:\\/\\/twitter.com\\/jesselehrich\\/status\\/1320901441272373250\",\n          \"display\":\"twitter.com\\/jesselehrich\\/s\\u2026\"\n       },\n       \"quoted_status\":{\n          \"created_at\":\"Tue Oct 27 01:33:39 +0000 2020\",\n          \"id\":1320901441272373250,\n          \"id_str\":\"1320901441272373250\",\n          \"full_text\":\"McConnell adjourned the Senate until 11\\/9 with no COVID relief.\",\n          \"truncated\":false,\n          \"display_text_range\":[\n             0,\n             63\n          ],\n          \"entities\":{\n             \"hashtags\":[\n                \n             ],\n             \"symbols\":[\n                \n             ],\n             \"user_mentions\":[\n                \n             ],\n             \"urls\":[\n                \n             ]\n          },\n          \"source\":\"\\u003ca href=\\\"https:\\/\\/about.twitter.com\\/products\\/tweetdeck\\\" rel=\\\"nofollow\\\"\\u003eTweetDeck\\u003c\\/a\\u003e\",\n          \"in_reply_to_status_id\":null,\n          \"in_reply_to_status_id_str\":null,\n          \"in_reply_to_user_id\":null,\n          \"in_reply_to_user_id_str\":null,\n          \"in_reply_to_screen_name\":null,\n          \"user\":{\n             \"id\":26186655,\n             \"id_str\":\"26186655\",\n             \"name\":\"Jesse Lehrich\",\n             \"screen_name\":\"JesseLehrich\",\n             \"location\":\"Chicago, by way of Boston\",\n             \"description\":\"co-founder of @accountabletech \\u2013 trying to make the internet less bad & more truthy. former foreign policy spokesman for @HillaryClinton.\",\n             \"url\":null,\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      \n                   ]\n                }\n             },\n             \"protected\":false,\n             \"followers_count\":56505,\n             \"friends_count\":913,\n             \"listed_count\":798,\n             \"created_at\":\"Tue Mar 24 05:45:39 +0000 2009\",\n             \"favourites_count\":14158,\n             \"utc_offset\":null,\n             \"time_zone\":null,\n             \"geo_enabled\":true,\n             \"verified\":true,\n             \"statuses_count\":32819,\n             \"lang\":null,\n             \"contributors_enabled\":false,\n             \"is_translator\":false,\n             \"is_translation_enabled\":false,\n             \"profile_background_color\":\"0099B9\",\n             \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme4\\/bg.gif\",\n             \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme4\\/bg.gif\",\n             \"profile_background_tile\":false,\n             \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1040437265237852160\\/h8PVbmgq_normal.jpg\",\n             \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1040437265237852160\\/h8PVbmgq_normal.jpg\",\n             \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/26186655\\/1536894883\",\n             \"profile_link_color\":\"0099B9\",\n             \"profile_sidebar_border_color\":\"5ED4DC\",\n             \"profile_sidebar_fill_color\":\"95E8EC\",\n             \"profile_text_color\":\"3C3940\",\n             \"profile_use_background_image\":true,\n             \"has_extended_profile\":true,\n             \"default_profile\":false,\n             \"default_profile_image\":false,\n             \"following\":null,\n             \"follow_request_sent\":null,\n             \"notifications\":null,\n             \"translator_type\":\"none\"\n          },\n          \"geo\":null,\n          \"coordinates\":null,\n          \"place\":null,\n          \"contributors\":null,\n          \"is_quote_status\":false,\n          \"retweet_count\":12053,\n          \"favorite_count\":25390,\n          \"favorited\":false,\n          \"retweeted\":false,\n          \"lang\":\"en\"\n       },\n       \"retweet_count\":133,\n       \"favorite_count\":300,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 13:51:42 +0000 2020\",\n       \"id\":1321087179939057665,\n       \"id_str\":\"1321087179939057665\",\n       \"full_text\":\"I hope people understand how serious a threat to democracy this is: Kavanaugh is now \\u201cswing justice\\u201d on 6-3 court &amp; he\\u2019s spreading same disinformation about counting mail ballots as Trump\\n\\nWe need to vote in record numbers to overcome rigged Supreme Court https:\\/\\/t.co\\/Elnsnvqr4u\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          283\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             {\n                \"url\":\"https:\\/\\/t.co\\/Elnsnvqr4u\",\n                \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/2020-elections\\/2020\\/10\\/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election\\/\",\n                \"display_url\":\"motherjones.com\\/2020-elections\\u2026\",\n                \"indices\":[\n                   260,\n                   283\n                ]\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1320899108606017539,\n       \"in_reply_to_status_id_str\":\"1320899108606017539\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":125,\n       \"favorite_count\":313,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 13:19:17 +0000 2020\",\n       \"id\":1321079019668967431,\n       \"id_str\":\"1321079019668967431\",\n       \"full_text\":\"In chilling opinion yesterday Brett Kavanaugh signaled he's willing to help Trump delegitimize election &amp; throw out mail ballots cast heavily by Dems\\n\\nKavanaugh &amp; Barrett worked on Florida 2000 recount for Bush &amp; could do Bush v Gore 2.0 for Trump https:\\/\\/t.co\\/Elnsnvqr4u\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          283\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             {\n                \"url\":\"https:\\/\\/t.co\\/Elnsnvqr4u\",\n                \"expanded_url\":\"https:\\/\\/www.motherjones.com\\/2020-elections\\/2020\\/10\\/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election\\/\",\n                \"display_url\":\"motherjones.com\\/2020-elections\\u2026\",\n                \"indices\":[\n                   260,\n                   283\n                ]\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":317,\n       \"favorite_count\":466,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 12:19:01 +0000 2020\",\n       \"id\":1321063854504353794,\n       \"id_str\":\"1321063854504353794\",\n       \"full_text\":\"Given persistent USPS delays (it takes an average of 10 days for letter to be delivered in Wisconsin), I would urge voters to drop off their mail ballots or vote in person rather than risk mail ballot not being counted because USPS didn\\u2019t deliver by Election Day\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          262\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1321060511245606914,\n       \"in_reply_to_status_id_str\":\"1321060511245606914\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":745,\n       \"favorite_count\":1531,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 12:05:44 +0000 2020\",\n       \"id\":1321060511245606914,\n       \"id_str\":\"1321060511245606914\",\n       \"full_text\":\"Very important: today is deadline USPS recommends for sending back your mail ballot to make sure it\\u2019s counted. Better yet, drop it off if you can. 30 states require ballots to be received by Election Day (as SCOTUS ruled in Wisconsin yesterday)\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          244\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":6300,\n       \"favorite_count\":8000,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 01:36:52 +0000 2020\",\n       \"id\":1320902249846083586,\n       \"id_str\":\"1320902249846083586\",\n       \"full_text\":\"If you\\u2019re feeling despair tonight (and I know I am), remember massive turnout can overcome massive suppression \\n\\nWe don\\u2019t have to accept an illegitimate system, we can vote to change it\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          185\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1320783852219105287,\n       \"in_reply_to_status_id_str\":\"1320783852219105287\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":1267,\n       \"favorite_count\":5713,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 01:24:23 +0000 2020\",\n       \"id\":1320899108606017539,\n       \"id_str\":\"1320899108606017539\",\n       \"full_text\":\"Kagan dissent in Wisconsin case: \\\"the Court\\u2019s decision will disenfranchise large numbers of responsible voters in the midst of hazardous pandemic conditions\\\" https:\\/\\/t.co\\/Q4pEXmDN96\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          157\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ],\n          \"media\":[\n             {\n                \"id\":1320898952183619584,\n                \"id_str\":\"1320898952183619584\",\n                \"indices\":[\n                   158,\n                   181\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                \"url\":\"https:\\/\\/t.co\\/Q4pEXmDN96\",\n                \"display_url\":\"pic.twitter.com\\/Q4pEXmDN96\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320899108606017539\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":834,\n                      \"h\":476,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":834,\n                      \"h\":476,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":680,\n                      \"h\":388,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"extended_entities\":{\n          \"media\":[\n             {\n                \"id\":1320898952183619584,\n                \"id_str\":\"1320898952183619584\",\n                \"indices\":[\n                   158,\n                   181\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElTGmT0XIAAAaiH.png\",\n                \"url\":\"https:\\/\\/t.co\\/Q4pEXmDN96\",\n                \"display_url\":\"pic.twitter.com\\/Q4pEXmDN96\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320899108606017539\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":834,\n                      \"h\":476,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":834,\n                      \"h\":476,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":680,\n                      \"h\":388,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1320883524954660867,\n       \"in_reply_to_status_id_str\":\"1320883524954660867\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":539,\n       \"favorite_count\":2148,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 01:20:01 +0000 2020\",\n       \"id\":1320898012391055360,\n       \"id_str\":\"1320898012391055360\",\n       \"full_text\":\"Trump campaign has filed lawsuits before Supreme Court to challenge ballots in states like PA &amp; NC that Barrett could rule on as soon as tomorrow. This White House event is total conflict of interest. She is even more illegitimate now than before\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          250\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"http:\\/\\/twitter.com\\/download\\/iphone\\\" rel=\\\"nofollow\\\"\\u003eTwitter for iPhone\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":1358,\n       \"favorite_count\":3607,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 01:13:26 +0000 2020\",\n       \"id\":1320896353904562176,\n       \"id_str\":\"1320896353904562176\",\n       \"full_text\":\"GOP traded 225,000 American lives for 220 Trump judges\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          54\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":3793,\n       \"favorite_count\":12430,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 00:34:54 +0000 2020\",\n       \"id\":1320886657156026369,\n       \"id_str\":\"1320886657156026369\",\n       \"full_text\":\"Crazy fact: 52 GOP senators who voted to confirm Amy Coney Barrett represent 17 million fewer people than 47 Dems &amp; Collins who voted no\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          140\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":5873,\n       \"favorite_count\":14768,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 00:22:27 +0000 2020\",\n       \"id\":1320883524954660867,\n       \"id_str\":\"1320883524954660867\",\n       \"full_text\":\"Insane rhetoric from Kavanaugh decrying \\\"chaos &amp; suspicions of impropriety that can ensue if thousands of absentee ballots flow in after election day and potentially flip the results of an election\\\"\\n\\nThis comes on some night Trump tweets winner of election must be announced Nov 3 https:\\/\\/t.co\\/Y9iZcBGOGy\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          284\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ],\n          \"media\":[\n             {\n                \"id\":1320882150552571905,\n                \"id_str\":\"1320882150552571905\",\n                \"indices\":[\n                   285,\n                   308\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                \"url\":\"https:\\/\\/t.co\\/Y9iZcBGOGy\",\n                \"display_url\":\"pic.twitter.com\\/Y9iZcBGOGy\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320883524954660867\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"extended_entities\":{\n          \"media\":[\n             {\n                \"id\":1320882150552571905,\n                \"id_str\":\"1320882150552571905\",\n                \"indices\":[\n                   285,\n                   308\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElS3UU3XEAEDo-9.png\",\n                \"url\":\"https:\\/\\/t.co\\/Y9iZcBGOGy\",\n                \"display_url\":\"pic.twitter.com\\/Y9iZcBGOGy\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320883524954660867\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":415,\n                      \"h\":410,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1320877150770241541,\n       \"in_reply_to_status_id_str\":\"1320877150770241541\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":695,\n       \"favorite_count\":1855,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Tue Oct 27 00:13:23 +0000 2020\",\n       \"id\":1320881243756597250,\n       \"id_str\":\"1320881243756597250\",\n       \"full_text\":\"Truly astonishing: with Amy Coney Barrett\\u2019s confirmation 5 of 6 conservative Supreme Court justices appointed by GOP presidents who initially lost popular vote &amp; confirmed by senators who do not represent a majority of Americans\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          232\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":null,\n       \"in_reply_to_status_id_str\":null,\n       \"in_reply_to_user_id\":null,\n       \"in_reply_to_user_id_str\":null,\n       \"in_reply_to_screen_name\":null,\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":2438,\n       \"favorite_count\":7334,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"lang\":\"en\"\n    },\n    {\n       \"created_at\":\"Mon Oct 26 23:57:08 +0000 2020\",\n       \"id\":1320877150770241541,\n       \"id_str\":\"1320877150770241541\",\n       \"full_text\":\"Kavanaugh cites Bush v. Gore to justify making it harder to vote in Wisconsin\\n\\nKavanaugh, Roberts &amp; Barrett all served on George W. Bush legal team in Florida 2000 recount &amp; worked to make sure only GOP votes counted https:\\/\\/t.co\\/NhNessbwoB\",\n       \"truncated\":false,\n       \"display_text_range\":[\n          0,\n          224\n       ],\n       \"entities\":{\n          \"hashtags\":[\n             \n          ],\n          \"symbols\":[\n             \n          ],\n          \"user_mentions\":[\n             \n          ],\n          \"urls\":[\n             \n          ],\n          \"media\":[\n             {\n                \"id\":1320876915889262592,\n                \"id_str\":\"1320876915889262592\",\n                \"indices\":[\n                   225,\n                   248\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                \"url\":\"https:\\/\\/t.co\\/NhNessbwoB\",\n                \"display_url\":\"pic.twitter.com\\/NhNessbwoB\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320877150770241541\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"extended_entities\":{\n          \"media\":[\n             {\n                \"id\":1320876915889262592,\n                \"id_str\":\"1320876915889262592\",\n                \"indices\":[\n                   225,\n                   248\n                ],\n                \"media_url\":\"http:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                \"media_url_https\":\"https:\\/\\/pbs.twimg.com\\/media\\/ElSyjoOX0AAr1jk.png\",\n                \"url\":\"https:\\/\\/t.co\\/NhNessbwoB\",\n                \"display_url\":\"pic.twitter.com\\/NhNessbwoB\",\n                \"expanded_url\":\"https:\\/\\/twitter.com\\/AriBerman\\/status\\/1320877150770241541\\/photo\\/1\",\n                \"type\":\"photo\",\n                \"sizes\":{\n                   \"large\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"medium\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"small\":{\n                      \"w\":412,\n                      \"h\":448,\n                      \"resize\":\"fit\"\n                   },\n                   \"thumb\":{\n                      \"w\":150,\n                      \"h\":150,\n                      \"resize\":\"crop\"\n                   }\n                }\n             }\n          ]\n       },\n       \"source\":\"\\u003ca href=\\\"https:\\/\\/mobile.twitter.com\\\" rel=\\\"nofollow\\\"\\u003eTwitter Web App\\u003c\\/a\\u003e\",\n       \"in_reply_to_status_id\":1320872258085310466,\n       \"in_reply_to_status_id_str\":\"1320872258085310466\",\n       \"in_reply_to_user_id\":15952856,\n       \"in_reply_to_user_id_str\":\"15952856\",\n       \"in_reply_to_screen_name\":\"AriBerman\",\n       \"user\":{\n          \"id\":15952856,\n          \"id_str\":\"15952856\",\n          \"name\":\"Ari Berman\",\n          \"screen_name\":\"AriBerman\",\n          \"location\":\"New York\",\n          \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https:\\/\\/t.co\\/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n          \"url\":null,\n          \"entities\":{\n             \"description\":{\n                \"urls\":[\n                   {\n                      \"url\":\"https:\\/\\/t.co\\/q5tNcCdQjy\",\n                      \"expanded_url\":\"http:\\/\\/amzn.to\\/2asHld2\",\n                      \"display_url\":\"amzn.to\\/2asHld2\",\n                      \"indices\":[\n                         77,\n                         100\n                      ]\n                   }\n                ]\n             }\n          },\n          \"protected\":false,\n          \"followers_count\":159648,\n          \"friends_count\":2782,\n          \"listed_count\":2703,\n          \"created_at\":\"Sat Aug 23 01:46:02 +0000 2008\",\n          \"favourites_count\":17410,\n          \"utc_offset\":null,\n          \"time_zone\":null,\n          \"geo_enabled\":false,\n          \"verified\":true,\n          \"statuses_count\":26571,\n          \"lang\":null,\n          \"contributors_enabled\":false,\n          \"is_translator\":false,\n          \"is_translation_enabled\":false,\n          \"profile_background_color\":\"0A4778\",\n          \"profile_background_image_url\":\"http:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_image_url_https\":\"https:\\/\\/abs.twimg.com\\/images\\/themes\\/theme1\\/bg.png\",\n          \"profile_background_tile\":false,\n          \"profile_image_url\":\"http:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_image_url_https\":\"https:\\/\\/pbs.twimg.com\\/profile_images\\/1286763565412753409\\/KkQAmz2N_normal.jpg\",\n          \"profile_banner_url\":\"https:\\/\\/pbs.twimg.com\\/profile_banners\\/15952856\\/1470151342\",\n          \"profile_link_color\":\"C7124B\",\n          \"profile_sidebar_border_color\":\"FFFFFF\",\n          \"profile_sidebar_fill_color\":\"FF1D2E\",\n          \"profile_text_color\":\"7A3060\",\n          \"profile_use_background_image\":false,\n          \"has_extended_profile\":true,\n          \"default_profile\":false,\n          \"default_profile_image\":false,\n          \"following\":null,\n          \"follow_request_sent\":null,\n          \"notifications\":null,\n          \"translator_type\":\"none\"\n       },\n       \"geo\":null,\n       \"coordinates\":null,\n       \"place\":null,\n       \"contributors\":null,\n       \"is_quote_status\":false,\n       \"retweet_count\":2197,\n       \"favorite_count\":3990,\n       \"favorited\":false,\n       \"retweeted\":false,\n       \"possibly_sensitive\":false,\n       \"lang\":\"en\"\n    }\n ]"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/responses/response-version2-conversation.json",
    "content": "{\n    \"data\":[\n       {\n          \"conversation_id\":\"1320872258085310466\",\n          \"lang\":\"en\",\n          \"id\":\"1320872258085310466\",\n          \"created_at\":\"2020-10-26T23:37:41.000Z\",\n          \"possibly_sensitive\":false,\n          \"text\":\"Minutes before Amy Coney Barrett is confirmed Supreme Court rules 5-3 to throw out ballots in Wisconsin that are postmarked by Election Day but arrive after. 80,000 ballots were counted in primary that arrived after Election Day but were postmarked by then https://t.co/qEZGQBzpOv\",\n          \"author_id\":\"15952856\",\n          \"entities\":{\n             \"annotations\":[\n                {\n                   \"start\":15,\n                   \"end\":31,\n                   \"probability\":0.9516,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Amy Coney Barrett\"\n                },\n                {\n                   \"start\":46,\n                   \"end\":58,\n                   \"probability\":0.9123,\n                   \"type\":\"Organization\",\n                   \"normalized_text\":\"Supreme Court\"\n                },\n                {\n                   \"start\":94,\n                   \"end\":102,\n                   \"probability\":0.9795,\n                   \"type\":\"Place\",\n                   \"normalized_text\":\"Wisconsin\"\n                },\n                {\n                   \"start\":127,\n                   \"end\":138,\n                   \"probability\":0.4046,\n                   \"type\":\"Other\",\n                   \"normalized_text\":\"Election Day\"\n                },\n                {\n                   \"start\":216,\n                   \"end\":227,\n                   \"probability\":0.5778,\n                   \"type\":\"Other\",\n                   \"normalized_text\":\"Election Day\"\n                }\n             ],\n             \"urls\":[\n                {\n                   \"start\":257,\n                   \"end\":280,\n                   \"url\":\"https://t.co/qEZGQBzpOv\",\n                   \"expanded_url\":\"https://twitter.com/stevenmazie/status/1320870358900158466\",\n                   \"display_url\":\"twitter.com/stevenmazie/st…\"\n                }\n             ]\n          },\n          \"public_metrics\":{\n             \"retweet_count\":10045,\n             \"reply_count\":937,\n             \"like_count\":15745,\n             \"quote_count\":2155\n          },\n          \"source\":\"Twitter Web App\",\n          \"context_annotations\":[\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"1314602198932750336\",\n                   \"name\":\"Amy Coney Barrett\"\n                }\n             }\n          ],\n          \"referenced_tweets\":[\n             {\n                \"type\":\"quoted\",\n                \"id\":\"1320870358900158466\"\n             }\n          ]\n       }\n    ]\n }"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/responses/response-version2-tweetthread.json",
    "content": "{\n    \"data\":[\n       {\n          \"public_metrics\":{\n             \"retweet_count\":137,\n             \"reply_count\":12,\n             \"like_count\":347,\n             \"quote_count\":15\n          },\n          \"text\":\"I hope people understand how serious a threat to democracy this is: Kavanaugh is now “swing justice” on 6-3 court &amp; he’s spreading same disinformation about counting mail ballots as Trump\\n\\nWe need to vote in record numbers to overcome rigged Supreme Court https://t.co/Elnsnvqr4u\",\n          \"lang\":\"en\",\n          \"in_reply_to_user_id\":\"15952856\",\n          \"source\":\"Twitter Web App\",\n          \"created_at\":\"2020-10-27T13:51:42.000Z\",\n          \"possibly_sensitive\":false,\n          \"author_id\":\"15952856\",\n          \"referenced_tweets\":[\n             {\n                \"type\":\"replied_to\",\n                \"id\":\"1320899108606017539\"\n             }\n          ],\n          \"entities\":{\n             \"annotations\":[\n                {\n                   \"start\":68,\n                   \"end\":76,\n                   \"probability\":0.9533,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Kavanaugh\"\n                },\n                {\n                   \"start\":182,\n                   \"end\":186,\n                   \"probability\":0.9978,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Trump\"\n                },\n                {\n                   \"start\":242,\n                   \"end\":254,\n                   \"probability\":0.5928,\n                   \"type\":\"Organization\",\n                   \"normalized_text\":\"Supreme Court\"\n                }\n             ],\n             \"urls\":[\n                {\n                   \"start\":260,\n                   \"end\":283,\n                   \"url\":\"https://t.co/Elnsnvqr4u\",\n                   \"expanded_url\":\"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\",\n                   \"display_url\":\"motherjones.com/2020-elections…\",\n                   \"images\":[\n                      {\n                         \"url\":\"https://pbs.twimg.com/news_img/1321079020184809474/XbL5QJjE?format=jpg&name=orig\",\n                         \"width\":1200,\n                         \"height\":630\n                      },\n                      {\n                         \"url\":\"https://pbs.twimg.com/news_img/1321079020184809474/XbL5QJjE?format=jpg&name=150x150\",\n                         \"width\":150,\n                         \"height\":150\n                      }\n                   ],\n                   \"status\":200,\n                   \"title\":\"Brett Kavanaugh just laid out his plan to help Trump steal the election\",\n                   \"description\":\"A Bush v. Gore 2.0 crisis just became much more likely.\",\n                   \"unwound_url\":\"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\"\n                }\n             ]\n          },\n          \"id\":\"1321087179939057665\",\n          \"context_annotations\":[\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"799022225751871488\",\n                   \"name\":\"Donald Trump\",\n                   \"description\":\"US President Donald Trump\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"1016678775298260992\",\n                   \"name\":\"Brett Kavanaugh\",\n                   \"description\":\"US Supreme Court nominee Brett Kavanaugh\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"799022225751871488\",\n                   \"name\":\"Donald Trump\",\n                   \"description\":\"US President Donald Trump\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"1016678775298260992\",\n                   \"name\":\"Brett Kavanaugh\",\n                   \"description\":\"US Supreme Court nominee Brett Kavanaugh\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"88\",\n                   \"name\":\"Political Body\",\n                   \"description\":\"A section of a government, like The Supreme Court\"\n                },\n                \"entity\":{\n                   \"id\":\"867872043672326144\",\n                   \"name\":\"Supreme Court of the United States\",\n                   \"description\":\"Conversation about the Supreme Court and justices\"\n                }\n             }\n          ],\n          \"conversation_id\":\"1320872258085310466\"\n       },\n       {\n          \"attachments\":{\n             \"media_keys\":[\n                \"3_1320898952183619584\"\n             ]\n          },\n          \"public_metrics\":{\n             \"retweet_count\":552,\n             \"reply_count\":60,\n             \"like_count\":2177,\n             \"quote_count\":41\n          },\n          \"text\":\"Kagan dissent in Wisconsin case: \\\"the Court’s decision will disenfranchise large numbers of responsible voters in the midst of hazardous pandemic conditions\\\" https://t.co/Q4pEXmDN96\",\n          \"lang\":\"en\",\n          \"in_reply_to_user_id\":\"15952856\",\n          \"source\":\"Twitter Web App\",\n          \"created_at\":\"2020-10-27T01:24:23.000Z\",\n          \"possibly_sensitive\":false,\n          \"author_id\":\"15952856\",\n          \"referenced_tweets\":[\n             {\n                \"type\":\"replied_to\",\n                \"id\":\"1320883524954660867\"\n             }\n          ],\n          \"entities\":{\n             \"annotations\":[\n                {\n                   \"start\":17,\n                   \"end\":25,\n                   \"probability\":0.9873,\n                   \"type\":\"Place\",\n                   \"normalized_text\":\"Wisconsin\"\n                }\n             ],\n             \"urls\":[\n                {\n                   \"start\":158,\n                   \"end\":181,\n                   \"url\":\"https://t.co/Q4pEXmDN96\",\n                   \"expanded_url\":\"https://twitter.com/AriBerman/status/1320899108606017539/photo/1\",\n                   \"display_url\":\"pic.twitter.com/Q4pEXmDN96\"\n                }\n             ]\n          },\n          \"id\":\"1320899108606017539\",\n          \"context_annotations\":[\n             {\n                \"domain\":{\n                   \"id\":\"123\",\n                   \"name\":\"Ongoing News Story\",\n                   \"description\":\"Ongoing News Stories like 'Brexit'\"\n                },\n                \"entity\":{\n                   \"id\":\"1220701888179359745\",\n                   \"name\":\"COVID-19\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"867887467902283776\",\n                   \"name\":\"Elena Kagan\",\n                   \"description\":\"US Supreme Court Justice Elena Kagan\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"867887467902283776\",\n                   \"name\":\"Elena Kagan\",\n                   \"description\":\"US Supreme Court Justice Elena Kagan\"\n                }\n             }\n          ],\n          \"conversation_id\":\"1320872258085310466\"\n       },\n       {\n          \"attachments\":{\n             \"media_keys\":[\n                \"3_1320882150552571905\"\n             ]\n          },\n          \"public_metrics\":{\n             \"retweet_count\":696,\n             \"reply_count\":108,\n             \"like_count\":1875,\n             \"quote_count\":152\n          },\n          \"text\":\"Insane rhetoric from Kavanaugh decrying \\\"chaos &amp; suspicions of impropriety that can ensue if thousands of absentee ballots flow in after election day and potentially flip the results of an election\\\"\\n\\nThis comes on some night Trump tweets winner of election must be announced Nov 3 https://t.co/Y9iZcBGOGy\",\n          \"lang\":\"en\",\n          \"in_reply_to_user_id\":\"15952856\",\n          \"source\":\"Twitter Web App\",\n          \"created_at\":\"2020-10-27T00:22:27.000Z\",\n          \"possibly_sensitive\":false,\n          \"author_id\":\"15952856\",\n          \"referenced_tweets\":[\n             {\n                \"type\":\"replied_to\",\n                \"id\":\"1320877150770241541\"\n             }\n          ],\n          \"entities\":{\n             \"annotations\":[\n                {\n                   \"start\":21,\n                   \"end\":29,\n                   \"probability\":0.6753,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Kavanaugh\"\n                },\n                {\n                   \"start\":225,\n                   \"end\":229,\n                   \"probability\":0.9984,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Trump\"\n                }\n             ],\n             \"urls\":[\n                {\n                   \"start\":285,\n                   \"end\":308,\n                   \"url\":\"https://t.co/Y9iZcBGOGy\",\n                   \"expanded_url\":\"https://twitter.com/AriBerman/status/1320883524954660867/photo/1\",\n                   \"display_url\":\"pic.twitter.com/Y9iZcBGOGy\"\n                }\n             ]\n          },\n          \"id\":\"1320883524954660867\",\n          \"context_annotations\":[\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"799022225751871488\",\n                   \"name\":\"Donald Trump\",\n                   \"description\":\"US President Donald Trump\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"1016678775298260992\",\n                   \"name\":\"Brett Kavanaugh\",\n                   \"description\":\"US Supreme Court nominee Brett Kavanaugh\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"799022225751871488\",\n                   \"name\":\"Donald Trump\",\n                   \"description\":\"US President Donald Trump\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"1016678775298260992\",\n                   \"name\":\"Brett Kavanaugh\",\n                   \"description\":\"US Supreme Court nominee Brett Kavanaugh\"\n                }\n             }\n          ],\n          \"conversation_id\":\"1320872258085310466\"\n       },\n       {\n          \"attachments\":{\n             \"media_keys\":[\n                \"3_1320876915889262592\"\n             ]\n          },\n          \"public_metrics\":{\n             \"retweet_count\":2255,\n             \"reply_count\":226,\n             \"like_count\":4100,\n             \"quote_count\":414\n          },\n          \"text\":\"Kavanaugh cites Bush v. Gore to justify making it harder to vote in Wisconsin\\n\\nKavanaugh, Roberts &amp; Barrett all served on George W. Bush legal team in Florida 2000 recount &amp; worked to make sure only GOP votes counted https://t.co/NhNessbwoB\",\n          \"lang\":\"en\",\n          \"in_reply_to_user_id\":\"15952856\",\n          \"source\":\"Twitter Web App\",\n          \"created_at\":\"2020-10-26T23:57:08.000Z\",\n          \"possibly_sensitive\":false,\n          \"author_id\":\"15952856\",\n          \"referenced_tweets\":[\n             {\n                \"type\":\"replied_to\",\n                \"id\":\"1320872258085310466\"\n             }\n          ],\n          \"entities\":{\n             \"annotations\":[\n                {\n                   \"start\":0,\n                   \"end\":8,\n                   \"probability\":0.9211,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Kavanaugh\"\n                },\n                {\n                   \"start\":16,\n                   \"end\":19,\n                   \"probability\":0.9427,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Bush\"\n                },\n                {\n                   \"start\":24,\n                   \"end\":27,\n                   \"probability\":0.7073,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Gore\"\n                },\n                {\n                   \"start\":68,\n                   \"end\":76,\n                   \"probability\":0.6797,\n                   \"type\":\"Organization\",\n                   \"normalized_text\":\"Wisconsin\"\n                },\n                {\n                   \"start\":79,\n                   \"end\":87,\n                   \"probability\":0.7773,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Kavanaugh\"\n                },\n                {\n                   \"start\":90,\n                   \"end\":96,\n                   \"probability\":0.9675,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Roberts\"\n                },\n                {\n                   \"start\":100,\n                   \"end\":106,\n                   \"probability\":0.9509,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"Barrett\"\n                },\n                {\n                   \"start\":122,\n                   \"end\":135,\n                   \"probability\":0.9268,\n                   \"type\":\"Person\",\n                   \"normalized_text\":\"George W. Bush\"\n                },\n                {\n                   \"start\":151,\n                   \"end\":157,\n                   \"probability\":0.9004,\n                   \"type\":\"Place\",\n                   \"normalized_text\":\"Florida\"\n                },\n                {\n                   \"start\":199,\n                   \"end\":201,\n                   \"probability\":0.8738,\n                   \"type\":\"Organization\",\n                   \"normalized_text\":\"GOP\"\n                }\n             ],\n             \"urls\":[\n                {\n                   \"start\":225,\n                   \"end\":248,\n                   \"url\":\"https://t.co/NhNessbwoB\",\n                   \"expanded_url\":\"https://twitter.com/AriBerman/status/1320877150770241541/photo/1\",\n                   \"display_url\":\"pic.twitter.com/NhNessbwoB\"\n                }\n             ]\n          },\n          \"id\":\"1320877150770241541\",\n          \"context_annotations\":[\n             {\n                \"domain\":{\n                   \"id\":\"10\",\n                   \"name\":\"Person\",\n                   \"description\":\"Named people in the world like Nelson Mandela\"\n                },\n                \"entity\":{\n                   \"id\":\"921071164469911552\",\n                   \"name\":\"George W. Bush\",\n                   \"description\":\"Former US President George W. Bush\"\n                }\n             },\n             {\n                \"domain\":{\n                   \"id\":\"35\",\n                   \"name\":\"Politician\",\n                   \"description\":\"Politicians in the world, like Joe Biden\"\n                },\n                \"entity\":{\n                   \"id\":\"921071164469911552\",\n                   \"name\":\"George W. Bush\",\n                   \"description\":\"Former US President George W. Bush\"\n                }\n             }\n          ],\n          \"conversation_id\":\"1320872258085310466\"\n       }\n    ],\n    \"includes\":{\n       \"users\":[\n          {\n             \"url\":\"\",\n             \"id\":\"15952856\",\n             \"username\":\"AriBerman\",\n             \"name\":\"Ari Berman\",\n             \"protected\":false,\n             \"public_metrics\":{\n                \"followers_count\":159733,\n                \"following_count\":2782,\n                \"tweet_count\":26572,\n                \"listed_count\":2705\n             },\n             \"entities\":{\n                \"description\":{\n                   \"urls\":[\n                      {\n                         \"start\":77,\n                         \"end\":100,\n                         \"url\":\"https://t.co/q5tNcCdQjy\",\n                         \"expanded_url\":\"http://amzn.to/2asHld2\",\n                         \"display_url\":\"amzn.to/2asHld2\"\n                      }\n                   ],\n                   \"mentions\":[\n                      {\n                         \"start\":109,\n                         \"end\":121,\n                         \"username\":\"motherjones\"\n                      }\n                   ]\n                }\n             },\n             \"description\":\"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https://t.co/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n             \"profile_image_url\":\"https://pbs.twimg.com/profile_images/1286763565412753409/KkQAmz2N_normal.jpg\",\n             \"verified\":true,\n             \"pinned_tweet_id\":\"1159848529038364672\",\n             \"location\":\"New York\",\n             \"created_at\":\"2008-08-23T01:46:02.000Z\"\n          }\n       ]\n    },\n    \"meta\":{\n       \"newest_id\":\"1321087179939057665\",\n       \"oldest_id\":\"1320877150770241541\",\n       \"result_count\":4\n    }\n }"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/script-version1-searchendpoint.js",
    "content": "const { url } = require(\"inspector\");\nconst fetch = require(\"node-fetch\");\n\nconst BEARER_TOKEN = 'Bearer <Auth_Token_Here>';\nconst TWEET_LOOKUP_ENDPOINT = 'https://api.twitter.com/1.1/statuses/show.json?id=<tweet_id>&tweet_mode=extended';\nconst SEARCH_ENDPOINT = 'https://api.twitter.com/1.1/search/tweets.json?q=from:<screen_name>+to:<screen_name>&count=200&tweet_mode=extended';\nconst SAMPLE_TWEET = 'https://twitter.com/AriBerman/status/1320872258085310466';\nvar tweets = [];\nvar tweet_id = \"\";\nvar screen_name = \"\";\nvar conversation_id = \"\";\n\ngetTweets(SAMPLE_TWEET);\n\nfunction getTweetId(tweet_url) {\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\") + 1);\n}\n\nfunction getScreenName(tweet_url) {\n    tweet_url = tweet_url.substring(0, tweet_url.lastIndexOf(\"/status\"));\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\")+1);\n}\n\nfunction getURL(url_type) {\n    var url = \"\";\n    if(url_type == 'tweet-lookup')\n        url = TWEET_LOOKUP_ENDPOINT.replace(\"<tweet_id>\", tweet_id);\n    else if(url_type == 'search-tweets') {\n        url = SEARCH_ENDPOINT.replace(\"<screen_name>\", screen_name);\n        url = url.replace(\"<screen_name>\", screen_name);\n    }\n    return url; \n}\n\nfunction getTweets(tweet_url) {\n    tweet_id = getTweetId(tweet_url);\n    screen_name = getScreenName(tweet_url);\n    var url_type = 'tweet-lookup';\n    var url = getURL(url_type);\n    fetchURL(url, url_type);\n}\n\nfunction processResponse(url_type, responseJSON) {\n    if(url_type == 'tweet-lookup')\n        processTweetLookup(responseJSON);\n    else if(url_type == 'search-tweets')\n        processSearchResponse(responseJSON);\n}\n\nfunction processTweetLookup(responseJSON) {\n    var tweet = getTweetObject(responseJSON);\n    if(isTweetNotOlderThanSevenDays(tweet)) {\n        createCustomTweet(getTweetObject(responseJSON), getUserObject(responseJSON));\n        var url_type = 'search-tweets';\n        var url = getURL(url_type);\n        fetchURL(url, url_type);\n    }\n}\n\nfunction processSearchResponse(responseJSON) {\n    var directReply = getTweetArray(responseJSON).filter((tweet)=>\n        tweet.in_reply_to_screen_name == screen_name && tweet.in_reply_to_status_id_str == tweet_id);\n    while(directReply.length > 0) {\n        var tweet = directReply[0];\n        var replyId = tweet.id_str;\n        createCustomTweet(tweet, tweet.user);\n        directReply = getTweetArray(responseJSON).filter((tweet)=>\n            tweet.in_reply_to_screen_name == screen_name && tweet.in_reply_to_status_id_str == replyId);\n    }\n    console.log(tweets);\n    console.log(tweets.length);\n}\n\nfunction getTweetObject(responseJSON) {\n    return responseJSON;\n}\n\nfunction getTweetArray(responseJSON) {\n    return responseJSON.statuses;\n}\n\nfunction getUserObject(responseJSON) {\n    return responseJSON.user;\n}\n\nfunction createCustomTweet(tweet_object, user_object) {\n    var customTweet = {};\n    customTweet.name = user_object.name;\n    customTweet.twitterHandle = user_object.screen_name;\n    customTweet.image = user_object.profile_image_url_https;\n    customTweet.createdAt = tweet_object.created_at;\n    customTweet.tweet = tweet_object.full_text;\n    tweets.push(customTweet);\n}\n\nfunction isTweetNotOlderThanSevenDays(tweet) {\n    var dateOfTweet = new Date(tweet.created_at);\n    var currentDate = new Date();\n    var differenceInDays = (currentDate.getTime() - dateOfTweet.getTime())/(1000*3600*24);\n    if(differenceInDays >= 7) {\n        console.log(\"cannot retrieve tweet thread\");\n        return false;\n    }\n    return true;\n}\n\n\nasync function fetchURL(url, url_type) {\n    var myHeaders = {};\n    myHeaders.Authorization = BEARER_TOKEN;\n    var requestOptions = {\n        method : 'GET',\n        headers: myHeaders,\n        redirect: 'follow'\n    };\n    console.log(url);\n    await fetch(url, requestOptions)\n            .then((response)=>response.json())\n            .then((result)=>processResponse(url_type, result))\n            .catch((error)=>console.log(\"error\", error));\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/script-version1-usertimeline.js",
    "content": "const { url } = require(\"inspector\");\nconst fetch = require(\"node-fetch\");\n\nconst BEARER_TOKEN = 'Bearer <Auth_Token_Here>';\nconst TWEET_LOOKUP_ENDPOINT = 'https://api.twitter.com/1.1/statuses/show.json?id=<tweet_id>&tweet_mode=extended';\nconst USER_TIMELINE_ENDPOINT = 'https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=<screen_name>&since_id=<tweet_id>&tweet_mode=extended&count=200';\nconst SAMPLE_TWEET = 'https://twitter.com/AriBerman/status/1320872258085310466';\nvar tweets = [];\nvar tweet_id = \"\";\nvar screen_name = \"\";\nvar conversation_id = \"\";\n\ngetTweets(SAMPLE_TWEET);\n\nfunction getTweetId(tweet_url) {\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\") + 1);\n}\n\nfunction getScreenName(tweet_url) {\n    tweet_url = tweet_url.substring(0, tweet_url.lastIndexOf(\"/status\"));\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\")+1);\n}\n\nfunction getURL(url_type) {\n    var url = \"\";\n    if(url_type == 'tweet-lookup')\n        url = TWEET_LOOKUP_ENDPOINT.replace(\"<tweet_id>\", tweet_id);\n    else if(url_type == 'search-tweets') {\n        url = USER_TIMELINE_ENDPOINT.replace(\"<tweet_id>\", tweet_id);\n        url = url.replace(\"<screen_name>\", screen_name);\n    }\n    return url; \n}\n\nfunction getTweets(tweet_url) {\n    tweet_id = getTweetId(tweet_url);\n    screen_name = getScreenName(tweet_url);\n    var url_type = 'tweet-lookup';\n    var url = getURL(url_type);\n    fetchURL(url, url_type);\n}\n\nfunction processResponse(url_type, responseJSON) {\n    if(url_type == 'tweet-lookup')\n        processTweetLookup(responseJSON);\n    else if(url_type == 'search-tweets')\n        processSearchResponse(responseJSON);\n}\n\nfunction processTweetLookup(responseJSON) {\n    var tweet = getTweetObject(responseJSON);\n    if(isTweetNotOlderThanSevenDays(tweet)) {\n        createCustomTweet(getTweetObject(responseJSON), getUserObject(responseJSON));\n        var url_type = 'search-tweets';\n        var url = getURL(url_type);\n        fetchURL(url, url_type);\n    }\n}\n\nfunction processSearchResponse(responseJSON) {\n    var directReply = getTweetArray(responseJSON).filter((tweet)=>\n        tweet.in_reply_to_screen_name == screen_name && tweet.in_reply_to_status_id_str == tweet_id);\n    while(directReply.length > 0) {\n        var tweet = directReply[0];\n        var replyId = tweet.id_str;\n        createCustomTweet(tweet, tweet.user);\n        directReply = getTweetArray(responseJSON).filter((tweet)=>\n            tweet.in_reply_to_screen_name == screen_name && tweet.in_reply_to_status_id_str == replyId);\n    }\n    console.log(tweets);\n    console.log(tweets.length);\n}\n\nfunction getTweetObject(responseJSON) {\n    return responseJSON;\n}\n\nfunction getTweetArray(responseJSON) {\n    return responseJSON;\n}\n\nfunction getUserObject(responseJSON) {\n    return responseJSON.user;\n}\n\nfunction createCustomTweet(tweet_object, user_object) {\n    var customTweet = {};\n    customTweet.name = user_object.name;\n    customTweet.twitterHandle = user_object.screen_name;\n    customTweet.image = user_object.profile_image_url_https;\n    customTweet.createdAt = tweet_object.created_at;\n    customTweet.tweet = tweet_object.full_text;\n    tweets.push(customTweet);\n}\n\nfunction isTweetNotOlderThanSevenDays(tweet) {\n    var dateOfTweet = new Date(tweet.created_at);\n    var currentDate = new Date();\n    var differenceInDays = (currentDate.getTime() - dateOfTweet.getTime())/(1000*3600*24);\n    if(differenceInDays >= 7) {\n        console.log(\"cannot retrieve tweet thread\");\n        return false;\n    }\n    return true;\n}\n\n\nasync function fetchURL(url, url_type) {\n    var myHeaders = {};\n    myHeaders.Authorization = BEARER_TOKEN;\n    var requestOptions = {\n        method : 'GET',\n        headers: myHeaders,\n        redirect: 'follow'\n    };\n    console.log(url);\n    await fetch(url, requestOptions)\n            .then((response)=>response.json())\n            .then((result)=>processResponse(url_type, result))\n            .catch((error)=>console.log(\"error\", error));\n}\n"
  },
  {
    "path": "misc/playground/cli/spike/twitter-api/script-version2-conversation.js",
    "content": "const { url } = require(\"inspector\");\nconst fetch = require(\"node-fetch\");\nconst BEARER_TOKEN = 'Bearer <Auth_Token_Here>';\nconst ENDPOINT_TO_FETCH_CONVERSATION_ID = 'https://api.twitter.com/2/tweets?ids='\nconst TWEET_FIELDS = '&tweet.fields=attachments,author_id,context_annotations,conversation_id,created_at,entities,geo,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,source,withheld';\nconst USER_FIELDS = '&user.fields=created_at,description,entities,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified,withheld';\nconst MEDIA_FIELDS = '&media.fields=duration_ms,height,preview_image_url,public_metrics,width';\nconst POLL_FIELDS = '&poll.fields=duration_minutes,end_datetime,voting_status';\nconst PLACE_FIELDS = '&place.fields=contained_within,country,country_code,geo,name,place_type';\nconst EXPANSIONS = '&expansions=author_id';\nconst ENDPOINT_TO_FETCH_CONVERSATION_TWEETS = 'https://api.twitter.com/2/tweets/search/recent?query=conversation_id:<conversation_id>+from:<screen_name>';\nconst SAMPLE_TWEET = 'https://twitter.com/AriBerman/status/1320872258085310466';\nvar tweets = [];\nvar tweet_id = \"\";\nvar screen_name = \"\";\nvar conversation_id = \"\";\n\ngetTweets(SAMPLE_TWEET);\n\nfunction getTweetId(tweet_url) {\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\") + 1);\n}\n\nfunction getScreenName(tweet_url) {\n    tweet_url = tweet_url.substring(0, tweet_url.lastIndexOf(\"/status\"));\n    return tweet_url.substring(tweet_url.lastIndexOf(\"/\")+1);\n}\n\nfunction getURL(url_type) {\n    var url = \"\";\n    if(url_type == 'tweet-lookup')\n        url += ENDPOINT_TO_FETCH_CONVERSATION_ID + tweet_id;\n    else if(url_type == 'search-tweets') {\n        url = ENDPOINT_TO_FETCH_CONVERSATION_TWEETS.replace(\"<conversation_id>\", conversation_id);\n        url = url.replace(\"<screen_name>\", screen_name);\n    }\n    url += TWEET_FIELDS + EXPANSIONS + USER_FIELDS + MEDIA_FIELDS + PLACE_FIELDS + POLL_FIELDS;\n    return url; \n}\n\nfunction getTweets(tweet_url) {\n    tweet_id = getTweetId(tweet_url);\n    screen_name = getScreenName(tweet_url);\n    var url_type = 'tweet-lookup';\n    var url = getURL(url_type);\n    fetchURL(url, url_type);\n}\n\nfunction processResponse(url_type, responseJSON) {\n    if(url_type == 'tweet-lookup')\n        processTweetLookup(responseJSON);\n    else if(url_type == 'search-tweets')\n        processSearchResponse(responseJSON);\n}\n\nfunction processTweetLookup(responseJSON) {\n    var tweetArray = responseJSON.data;\n    var tweet = tweetArray[0];\n    if(isTweetNotOlderThanSevenDays(tweet)) {\n        createCustomTweet(getTweetObject(responseJSON), getUserObject(responseJSON));\n        conversation_id = getTweetObject(responseJSON).conversation_id;\n        var url_type = 'search-tweets';\n        var url = getURL(url_type);\n        fetchURL(url, url_type);\n    }    \n}\n\nfunction processSearchResponse(responseJSON) {\n    var directReplies = getTweetArray(responseJSON).filter((tweet)=>\n        tweet.referenced_tweets.filter((ref)=>ref.type == 'replied_to' && ref.id == tweet_id).length > 0);\n    while(directReplies.length > 0) {\n        var reply_id = directReplies[0].id;\n        createCustomTweet(directReplies[0], getUserObject(responseJSON));\n        directReplies = getTweetArray(responseJSON).filter((tweet)=>\n                            tweet.referenced_tweets.filter((ref)=>\n                                ref.type == 'replied_to' && ref.id == reply_id).length > 0);\n    }\n    console.log(tweets);\n    console.log(tweets.length);\n}\n\nfunction getTweetObject(responseJSON) {\n    return responseJSON.data[0];\n}\n\nfunction getTweetArray(responseJSON) {\n    return responseJSON.data;\n}\n\nfunction getUserObject(responseJSON) {\n    return responseJSON.includes.users[0];\n}\n\nfunction createCustomTweet(tweet_object, user_object) {\n    var customTweet = {};\n    customTweet.name = user_object.name;\n    customTweet.twitterHandle = user_object.username;\n    customTweet.image = user_object.profile_image_url;\n    customTweet.createdAt = tweet_object.created_at;\n    customTweet.tweet = tweet_object.text;\n    tweets.push(customTweet);\n}\n\n\n\nfunction isTweetNotOlderThanSevenDays(tweet) {\n    var dateOfTweet = new Date(tweet.created_at);\n    var currentDate = new Date();\n    var differenceInDays = (currentDate.getTime() - dateOfTweet.getTime())/(1000*3600*24);\n    if(differenceInDays >= 7) {\n        console.log(\"cannot retrieve tweet thread\");\n        return false;\n    }\n    return true;\n}\n\nasync function fetchURL(url, url_type) {\n    var myHeaders = {};\n    myHeaders.Authorization = BEARER_TOKEN;\n    var requestOptions = {\n        method : 'GET',\n        headers: myHeaders,\n        redirect: 'follow'\n    };\n    console.log(url);\n    await fetch(url, requestOptions)\n            .then((response)=>response.json())\n            .then((result)=>processResponse(url_type, result))\n            .catch((error)=>console.log(\"error\", error));\n}\n"
  },
  {
    "path": "misc/playground/mock/README.md",
    "content": "# mock\n\nHere is the test thread link https://twitter.com/johnjacobkenny/status/1320972620817260544\n\nHere is the code for the endpoint which fetches the first tweet data. Response is in `./twitter-tweet-api-response.json`\n\n```javascript\nvar myHeaders = new Headers();\nmyHeaders.append(\"Authorization\", \"Bearer <enter key here>\");\n\nvar requestOptions = {\n  method: \"GET\",\n  headers: myHeaders,\n  redirect: \"follow\",\n};\n\nfetch(\n  \"https://api.twitter.com/2/tweets/1320972620817260544?tweet.fields=author_id,created_at,entities,geo,id,in_reply_to_user_id,text,conversation_id,referenced_tweets&expansions=author_id,in_reply_to_user_id,referenced_tweets.id&user.fields=name,username\",\n  requestOptions\n)\n  .then((response) => response.text())\n  .then((result) => console.log(result))\n  .catch((error) => console.log(\"error\", error));\n```\n\nHere is the code for the endpoint which fetches the conversation tweet data. Response is in `twitter-recent-search-api-response.json`\n\n```javascript\nvar myHeaders = new Headers();\nmyHeaders.append(\"Authorization\", \"Bearer <enter key here>\");\n\nvar requestOptions = {\n  method: \"GET\",\n  headers: myHeaders,\n  redirect: \"follow\",\n};\n\nfetch(\n  \"https://api.twitter.com/2/tweets/search/recent?tweet.fields=author_id,created_at,entities,geo,id,in_reply_to_user_id,text,conversation_id,referenced_tweets&expansions=author_id,in_reply_to_user_id,referenced_tweets.id&query=conversation_id:1320972620817260544\",\n  requestOptions\n)\n  .then((response) => response.text())\n  .then((result) => console.log(result))\n  .catch((error) => console.log(\"error\", error));\n```\n"
  },
  {
    "path": "misc/playground/mock/twit-thread.json",
    "content": "[\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"How to Get Rich (without getting lucky):\"\n\t},\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"Seek wealth, not money or status. Wealth is having assets that earn while you sleep. Money is how we transfer time and wealth. Status is your place in the social hierarchy.\"\n\t},\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"Understand that ethical wealth creation is possible. If you secretly despise wealth, it will elude you.\"\n\t},\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"Ignore people playing status games. They gain status by attacking people playing wealth creation games.:\"\n\t},\n\t{\n\t\t\"name\": \"Naval\",\n\t\t\"twitterHandle\": \"naval\",\n\t\t\"image\": \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n\t\t\"createdAt\": \"2018-05-31\",\n\t\t\"tweet\": \"Ignore people playing status games. They gain status by attacking people playing wealth creation games.:\"\n\t}\n]\n"
  },
  {
    "path": "misc/playground/mock/twitter-recent-search-api-response.json",
    "content": "{\n  \"data\": [\n    {\n      \"conversation_id\": \"1320972620817260544\",\n      \"id\": \"1320974447952547840\",\n      \"text\": \"@lucifierpraveen Hehe 😜 Yeah it was kinda boring, but in the end we all learnt something valuable!\",\n      \"author_id\": \"391224534\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320974110826979328\"\n        }\n      ],\n      \"created_at\": \"2020-10-27T06:23:45.000Z\",\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"start\": 0,\n            \"end\": 16,\n            \"username\": \"lucifierpraveen\"\n          }\n        ]\n      },\n      \"in_reply_to_user_id\": \"2933420017\"\n    },\n    {\n      \"conversation_id\": \"1320972620817260544\",\n      \"entities\": {\n        \"hashtags\": [\n          {\n            \"start\": 76,\n            \"end\": 84,\n            \"tag\": \"twindle\"\n          }\n        ],\n        \"mentions\": [\n          {\n            \"start\": 0,\n            \"end\": 15,\n            \"username\": \"johnjacobkenny\"\n          }\n        ]\n      },\n      \"id\": \"1320974110826979328\",\n      \"text\": \"@johnjacobkenny I am also one of the guy who taste the merge conflicts 😅 in #twindle lol\",\n      \"author_id\": \"2933420017\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320972623031906306\"\n        }\n      ],\n      \"created_at\": \"2020-10-27T06:22:25.000Z\",\n      \"in_reply_to_user_id\": \"391224534\"\n    },\n    {\n      \"conversation_id\": \"1320972620817260544\",\n      \"id\": \"1320973015539093505\",\n      \"text\": \"Anyone can join at anytime, we have a lot of people joining daily, though we started a couple of weeks ago.\",\n      \"author_id\": \"391224534\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320972624965427200\"\n        }\n      ],\n      \"created_at\": \"2020-10-27T06:18:04.000Z\",\n      \"in_reply_to_user_id\": \"391224534\"\n    },\n    {\n      \"conversation_id\": \"1320972620817260544\",\n      \"id\": \"1320972624965427200\",\n      \"text\": \"3\\n\\nEveryone is encouraged to ask questions and contribute to create documentation and to help others out wherever they can!\\n\\nCheck it out @twindleco\",\n      \"author_id\": \"391224534\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320972623031906306\"\n        }\n      ],\n      \"created_at\": \"2020-10-27T06:16:30.000Z\",\n      \"entities\": {\n        \"mentions\": [\n          {\n            \"start\": 138,\n            \"end\": 148,\n            \"username\": \"twindleco\"\n          }\n        ]\n      },\n      \"in_reply_to_user_id\": \"391224534\"\n    },\n    {\n      \"conversation_id\": \"1320972620817260544\",\n      \"id\": \"1320972623031906306\",\n      \"text\": \"2\\n\\nWe are mostly a community of beginners with a few mentors guiding the product development. Everyone created their first PR and experienced the horrors of having a merge conflict, which was super fun tbh 🥳\\n\\nContd ...\",\n      \"author_id\": \"391224534\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320972620817260544\"\n        }\n      ],\n      \"created_at\": \"2020-10-27T06:16:30.000Z\",\n      \"in_reply_to_user_id\": \"391224534\"\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"id\": \"391224534\",\n        \"name\": \"Kenny Jacob ⚡\",\n        \"username\": \"johnjacobkenny\"\n      },\n      {\n        \"id\": \"2933420017\",\n        \"name\": \"PraveenKumar\",\n        \"username\": \"lucifierpraveen\"\n      }\n    ],\n    \"tweets\": [\n      {\n        \"conversation_id\": \"1320972620817260544\",\n        \"entities\": {\n          \"hashtags\": [\n            {\n              \"start\": 76,\n              \"end\": 84,\n              \"tag\": \"twindle\"\n            }\n          ],\n          \"mentions\": [\n            {\n              \"start\": 0,\n              \"end\": 15,\n              \"username\": \"johnjacobkenny\"\n            }\n          ]\n        },\n        \"id\": \"1320974110826979328\",\n        \"text\": \"@johnjacobkenny I am also one of the guy who taste the merge conflicts 😅 in #twindle lol\",\n        \"author_id\": \"2933420017\",\n        \"referenced_tweets\": [\n          {\n            \"type\": \"replied_to\",\n            \"id\": \"1320972623031906306\"\n          }\n        ],\n        \"created_at\": \"2020-10-27T06:22:25.000Z\",\n        \"in_reply_to_user_id\": \"391224534\"\n      },\n      {\n        \"conversation_id\": \"1320972620817260544\",\n        \"id\": \"1320972623031906306\",\n        \"text\": \"2\\n\\nWe are mostly a community of beginners with a few mentors guiding the product development. Everyone created their first PR and experienced the horrors of having a merge conflict, which was super fun tbh 🥳\\n\\nContd ...\",\n        \"author_id\": \"391224534\",\n        \"referenced_tweets\": [\n          {\n            \"type\": \"replied_to\",\n            \"id\": \"1320972620817260544\"\n          }\n        ],\n        \"created_at\": \"2020-10-27T06:16:30.000Z\",\n        \"in_reply_to_user_id\": \"391224534\"\n      },\n      {\n        \"conversation_id\": \"1320972620817260544\",\n        \"id\": \"1320972624965427200\",\n        \"text\": \"3\\n\\nEveryone is encouraged to ask questions and contribute to create documentation and to help others out wherever they can!\\n\\nCheck it out @twindleco\",\n        \"author_id\": \"391224534\",\n        \"referenced_tweets\": [\n          {\n            \"type\": \"replied_to\",\n            \"id\": \"1320972623031906306\"\n          }\n        ],\n        \"created_at\": \"2020-10-27T06:16:30.000Z\",\n        \"entities\": {\n          \"mentions\": [\n            {\n              \"start\": 138,\n              \"end\": 148,\n              \"username\": \"twindleco\"\n            }\n          ]\n        },\n        \"in_reply_to_user_id\": \"391224534\"\n      },\n      {\n        \"conversation_id\": \"1320972620817260544\",\n        \"entities\": {\n          \"hashtags\": [\n            {\n              \"start\": 145,\n              \"end\": 153,\n              \"tag\": \"twindle\"\n            }\n          ],\n          \"annotations\": [\n            {\n              \"start\": 81,\n              \"end\": 87,\n              \"probability\": 0.8492,\n              \"type\": \"Product\",\n              \"normalized_text\": \"Twitter\"\n            },\n            {\n              \"start\": 133,\n              \"end\": 139,\n              \"probability\": 0.8567,\n              \"type\": \"Product\",\n              \"normalized_text\": \"Twitter\"\n            }\n          ]\n        },\n        \"id\": \"1320972620817260544\",\n        \"text\": \"Please ignore this tweet thread. It is for testing purposes only. Trying out the Twitter v2 beta API for fetching tweet threads from Twitter for #twindle project.\\n\\nTwindle is the place where beginners contribute to open source by building a product in the open. Contd ...\",\n        \"author_id\": \"391224534\",\n        \"created_at\": \"2020-10-27T06:16:29.000Z\"\n      }\n    ]\n  },\n  \"meta\": {\n    \"newest_id\": \"1320974447952547840\",\n    \"oldest_id\": \"1320972623031906306\",\n    \"result_count\": 5\n  }\n}\n"
  },
  {
    "path": "misc/playground/mock/twitter-tweet-api-response.json",
    "content": "{\n  \"data\": {\n    \"text\": \"Please ignore this tweet thread. It is for testing purposes only. Trying out the Twitter v2 beta API for fetching tweet threads from Twitter for #twindle project.\\n\\nTwindle is the place where beginners contribute to open source by building a product in the open. Contd ...\",\n    \"author_id\": \"391224534\",\n    \"id\": \"1320972620817260544\",\n    \"conversation_id\": \"1320972620817260544\",\n    \"entities\": {\n      \"annotations\": [\n        {\n          \"start\": 81,\n          \"end\": 87,\n          \"probability\": 0.8492,\n          \"type\": \"Product\",\n          \"normalized_text\": \"Twitter\"\n        },\n        {\n          \"start\": 133,\n          \"end\": 139,\n          \"probability\": 0.8567,\n          \"type\": \"Product\",\n          \"normalized_text\": \"Twitter\"\n        }\n      ],\n      \"hashtags\": [\n        {\n          \"start\": 145,\n          \"end\": 153,\n          \"tag\": \"twindle\"\n        }\n      ]\n    },\n    \"created_at\": \"2020-10-27T06:16:29.000Z\"\n  },\n  \"includes\": {\n    \"users\": [\n      {\n        \"id\": \"391224534\",\n        \"name\": \"Kenny Jacob ⚡\",\n        \"username\": \"johnjacobkenny\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "misc/playground/tests/github/giturl.unit.test.js",
    "content": "const { validateGithubURL } = require(\"../../../../twindle-cli/src/cli\");\n\nconst mockURL =\n  \"https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/README.html\";\n\ntest(\"verify github url extension\", async () => {\n  await expect(() => validateGithubURL(mockURL)).rejects.toThrow(Error);\n});\n"
  },
  {
    "path": "misc/playground/tests/github/repo.unit.test.js",
    "content": "const { getHtml } = require(\"../../src/github/githubparse/app\");\n\nconst mockURL = \"https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch1.md\";\n\ntest(\"fetch function return repo details in json\", async () => {\n  const data = await getHtml(mockURL);\n  expect(data).toMatchObject(/{}/);\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/api.test.js",
    "content": "const { getTweetById, getConversationById } = require(\"../../src/twitter/api\");\nconst { ApiErrors } = require(\"../../src/twitter/error\");\n\ndescribe(\"api module\", () => {\n  it(\"should throw an error if called without tweet id\", () => {\n    expect(() => getTweetById()).toThrow(ApiErrors.TweetIDNotProvidedError);\n    expect(() => getConversationById()).toThrow(ApiErrors.TweetIDNotProvidedError);\n  });\n\n  // it(\"#getConversationByID() should throw error if screen name not provided\", () => {\n  //   expect(() => getConversationById(\"1324263512621883393\", undefined, \"API Key\")).toThrow(\n  //     ApiErrors.UserScreenNameInvalid\n  //   );\n  // });\n\n  // see https://github.com/facebook/jest/issues/5538#issuecomment-461013424\n  // it(\"should throw an error if called without token\", async () => {\n  //   await expect(() => getTweetById(\"1324263512621883393\")).rejects.toThrow(\n  //     ApiErrors.TokenNotProvidedError\n  //   );\n  //   await expect(() => getConversationById(\"1324263512621883393\")).rejects.toThrow(\n  //     ApiErrors.TokenNotProvidedError\n  //   );\n  // });\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/unit/api/helpers/fetch.unit.test.js",
    "content": "const { ApiErrors } = require(\"../../../../../src/twitter/error\");\nconst { fetch } = require(\"../../../../../src/twitter/api/helpers/fetch\");\n\nconst mockURL =\n  \"https://api.twitter.com/2/tweets?ids=1326680823559380992,1325817877585387521&tweet.fields=attachments,author_id,context_annotations,conversation_id,created_at,entities,geo,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,source,withheld&expansions=author_id,attachments.media_keys&user.fields=created_at,description,entities,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified,withheld&media.fields=duration_ms,height,preview_image_url,url,media_key,public_metrics,width&place.fields=contained_within,country,country_code,geo,name,place_type&poll.fields=duration_minutes,end_datetime,voting_status\";\n\ndescribe(\"twitter |> api |> helpers |> fetch module\", () => {\n  it(\"should throw error if called without token\", async () => {\n    await expect(() => fetch(mockURL)).rejects.toThrow(ApiErrors.TokenNotProvidedError);\n  });\n\n  it(\"should throw error if token is invalid\", async () => {\n    await expect(() => fetch(mockURL, \"blah blah blah\")).rejects.toThrow(\n      ApiErrors.InvalidTokenError\n    );\n  });\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/unit/api/twitter-endpoints/search.unit.test.js",
    "content": "const { ApiErrors } = require(\"../../../../../src/twitter/error\");\nconst { getConversationById } = require(\"../../../../../src/twitter/api/twitter-endpoints/search\");\n\ndescribe(\"twitter |> api |> twitter-endpoints |> search\", () => {\n  it(\"should throw error if ID is falsey(undefined, null, false, etc)\", () => {\n    expect(() => getConversationById()).toThrow(ApiErrors.TweetIDNotProvidedError);\n  });\n\n  it(\"should throw error if screenName is falsey\", () => {\n    expect(() => getConversationById(\"Tweet ID\")).toThrow(ApiErrors.UserScreenNameInvalid);\n  });\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/unit/api/twitter-endpoints/tweets.unit.test.js",
    "content": "const { ApiErrors } = require(\"../../../../../src/twitter/error\");\nconst { getTweetById } = require(\"../../../../../src/twitter/api/twitter-endpoints/tweets\");\n\ndescribe(\"twitter |> api |> twitter-endpoints |> tweets module\", () => {\n  it(\"should throw error if tweet id is falsey\", () => {\n    expect(() => getTweetById()).toThrow(ApiErrors.TweetIDNotProvidedError);\n  });\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/unit/transformations/helpers.unit.test.js",
    "content": "describe(\"helpers module\", () => {\n  it(\"2 + 2 == 4\", () => {\n    expect(2 + 2).toBe(4);\n  });\n});\n"
  },
  {
    "path": "misc/playground/tests/twitter/utils.test.js",
    "content": "const { formatTimestamp } = require(\"../../src/twitter/utils/date\");\n\ndescribe(\"utils module\", () => {\n  describe(\"date module\", () => {\n    it(\"#format() should correctly format the date\", () => {\n      const aDate = new Date(\"2020-01-01T00:00:00\");\n      const transformedDate = formatTimestamp(aDate);\n      const expectedOutput = \"Jan 1, 2020\";\n      expect(transformedDate).toEqual(expectedOutput);\n    });\n  });\n});\n"
  },
  {
    "path": "misc/playground/twindleco/header/header1/header1.css",
    "content": "*{\n    box-sizing: border-box;\n    margin: o;\n    padding: 0;\n    background-color: #212121;\n}\nli,a,button{\n    font-family: Arial, Helvetica, sans-serif;\n    font-weight: 500;\n    font-size: 16px;\n    color: #edf0f1;\n    text-decoration: none;\n\n}\nheader{\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 30px 10%;\n}\n.logo{\n    cursor: pointer;\n    color: #32e0c4;\n\n}\n.nav-links{\n    list-style:none ;\n}\n.nav-links li{\n    display: inline-block;\n    padding: 0px 20px;\n}\n.nav-links li a{\n    transition: all 0.3s ease 0s;\n}\n.nav-links li a:hover{\n    color: #0088a9;\n}\nbutton{\n    padding: 9px 25px;\n    background-color: rgba(0,136,169,1);\n    border: none;\n    border-radius: 50px;\n    cursor: pointer;\n    transition: all 0.3s ease 0s;\n}\nbutton:hover{\n    background-color: rgba(0,136,169,0.8);\n}"
  },
  {
    "path": "misc/playground/twindleco/header/header1/header1.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>HEADER</title>\n    <link rel=\"stylesheet\" href=\"header1.css\">\n</head>\n<body>\n    <header>\n        <h1 class=\"logo\">TWINDLE</h1>\n        <nav>\n            <ul class=\"nav-links\">\n                <li><a href=\"#\">Features</a></li>\n                <li><a href=\"#\">Team</a></li>\n                <li><a href=\"#\">About</a></li>\n            </ul>\n        </nav>\n        <a class=\"cta\" href=\"#\"><button>Contact</button></a>\n        </a>\n    </header>\n</body>\n</html>"
  },
  {
    "path": "misc/playground/twindleco/header/header2/header2.css",
    "content": "* {\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n}\n\nhtml, body {\n    background-color: #fff;\n    color: #000;\n    font-family: 'Lato', 'Arial', sans-serif;\n    font-size: 20px;\n    font-weight: 300;\n}\n\nheader {\n    background-color: #E0F6F6;\n}\n\nnav {\n    height: 20vh;\n    padding: 20px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center\n}\n\n.main-nav {\n    list-style: none;\n    padding: 10px;\n    display: flex;\n    align-items: flex-end;\n}\n\n.main-nav li {\n    display: inline-block;\n    margin-left: 40px;\n}\n\n.main-nav li a:link, .main-nav li a:visited {\n    padding: 8px 0;\n    color: #095a5a;\n    text-decoration: none;\n    text-transform: uppercase;\n    border-bottom: 2px solid transparent;\n    transition: border-bottom 0.2s;\n}\n\n.main-nav li a:hover, .main-nav li a:active {\n    border-bottom: 2px solid #095a5a;\n}"
  },
  {
    "path": "misc/playground/twindleco/header/header2/header2.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link\n      href=\"https://fonts.googleapis.com/css?family=Lato:300,300i,400,400i,700\"\n      rel=\"stylesheet\"\n    />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./header2.css\" />\n    <title>Twindle</title>\n  </head>\n  <body>\n    <header>\n      <nav>\n        <img src=\"https://img.icons8.com/doodle/48/000000/twitter--v1.png\"/>\n        <ul class=\"main-nav\">\n          <li><a href=\"#features\">Feature</a></li>\n          <li><a href=\"#works\">Team</a></li>\n          <li><a href=\"#cities\">About Us</a></li>\n        </ul>\n      </nav>\n    </header>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/header/header3/header3.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;700&display=swap');\n\n*{\n  margin: 0;\n  padding: 0;\n  font-family: 'Montserrat', sans-serif;\n}\n\n/* Header */\nheader{\n  display: flex;\n}\n\n    /* Logo */\n    #logo{\n      background-color: rgb(168, 28, 28);\n      font-weight: bolder;\n      font-size: 2.5rem;\n      padding: 10px;\n    }\n\n    #logo a{\n      text-decoration: none;\n      position: relative;\n      left: 102px;\n      color: white;\n    }\n\n    /* Menu List */\n    header ul{\n      list-style: none;\n      display: flex;\n      position: relative;\n      left: 100px;\n    }\n\n    header ul li{\n      text-align: center;\n      font-size: 1.2rem;\n      width: 150px;\n      padding: 23px 0px;\n    }\n\n    li a{\n      text-decoration: none;\n      color: black;\n    }\n    \n    li a:hover{\n      color: rgb(168, 28, 28);\n    }\n"
  },
  {
    "path": "misc/playground/twindleco/header/header3/header3.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Header</title>\n    <!-- CSS Links -->\n    <link rel=\"stylesheet\" href=\"header3.css\">\n\n</head>\n\n<body>\n    <!-- Header -->\n    <header>\n            <span id=\"logo\"><a href=\"#twindle\">Twi<span style=\"color: #a81c1c\">ndle</span></a></span>\n            <ul>\n                <li><a href=\"#blog\">Blog</a></li>\n                <li><a href=\"#videos\">Videos</a></li>\n                <li><a href=\"#faq\">FAQs</a></li>\n            </ul>\n    </header>\n\n</body>\n\n</html>"
  },
  {
    "path": "misc/playground/twindleco/header/header4/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" >\n<head>\n  <meta charset=\"UTF-8\">\n  <title>CodePen - A Pen by Marcus</title>\n  <link href=\"https://fonts.googleapis.com/css2?family=Permanent+Marker&display=swap\" rel=\"stylesheet\">\n\n<link href=\"https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Sansita+Swashed:wght@500&display=swap\" rel=\"stylesheet\">\n<link href=\"https://fonts.googleapis.com/css2?family=Sansita+Swashed:wght@700&display=swap\" rel=\"stylesheet\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"./style.css\">\n\n</head>\n<body>\n<!-- partial:index.partial.html -->\n<DOCTYPE! html>\n  \n  <div class='container'>\n    <div class='header'>\n      <h1><a href='#homepage' class='logo'>Twindle</a></h1>\n      <div class='links-container'>\n        <ul class='links'>\n          <li><a href='#home'>Home</a></li>\n          <li><a href='#teams'>Teams</a></li>\n          <li><a href='#FAQs'>FAQs</a></li>\n          <li><a href='chrome'><button>Add to Chrome</button></a></li>\n        </ul>\n      </div>\n    </div>\n    \n    <div class='body'>\n      <img src='https://img.freepik.com/free-vector/cute-bot-say-users-hello-chatbot-greets-online-consultation_80328-195.jpg?size=338&ext=jpg&ga=GA1.2.1454976031.1600875484' alt='bot greeting'>\n    </div>\n  </div>\n<!-- partial -->\n  \n</body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/header/header4/public/style.css",
    "content": "@charset \"UTF-8\";\n.container {\n  height: 100vh;\n  width: 100vw;\n  padding: 10px;\n  padding-top: 0px;\n  background-color: white;\n  margin-left: -10px;\n}\n\n.header {\n  position: fixed;\n  top: 0px;\n  left: 0px;\n  right: 0px;\n  width: 100vw;\n  height: 50px;\n  background-color: white;\n  padding-left: 30px;\n  padding-right: 30px;\n  padding: 10px;\n  display: flex;\n  flex-direction: row;\n}\n.header h1 {\n  display: block;\n  border: 2px solid transparent;\n  width: 70px;\n  height: 66px;\n  margin-left: -10px;\n  padding-left: 30px;\n  padding-right: 20px;\n  margin-top: -10px;\n  background-color: #E01C15;\n  display: flex;\n  align-items: center;\n}\n\n.logo {\n  color: white;\n  text-decoration: none;\n  font-size: 20px;\n  font-family: 'Sansita Swashed', cursive;\n}\n\n.links-container {\n  position: absolute;\n  right: 5%;\n}\n.links-container .links {\n  display: flex;\n  flex-direction: row;\n}\n.links-container .links a {\n  color: rgba(0, 0, 0, 0.9);\n  text-decoration: none;\n}\n.links-container .links li {\n  font-size: 15px;\n  margin-right: 3vw;\n  list-style: none;\n  font-family: 'Sansita Swashed', cursive;\n}\n.links-container .links a:hover {\n  border-bottom: 2px solid rgba(0, 0, 0, 0.6);\n}\n.links-container .links button {\n  padding: 5px;\n  color: white;\n  background-color: cyan;\n  border-radius: 5px;\n  border-color: transparent;\n  font-size: 15px;\n  font-weight: bold;\n  position: relative;\n  cursor: pointer;\n}\n.links-container .links button::before {\n  content: \"↖ IT'S FREE!\";\n  position: absolute;\n  color: rgba(0, 0, 0, 0.4);\n  top: 39px;\n  right: 2px;\n  font-size: 10px;\n}\n.links-container .links button:hover {\n  padding: 6.6px;\n}\n\n.body {\n  height: 100vh;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.body img {\n  margin-top: 100px;\n  width: 40%;\n  height: 100%;\n}\n\n@media screen and (max-width: 1000px) {\n  li {\n    font-size: 5px;\n    margin-right: 1vw;\n  }\n}"
  },
  {
    "path": "misc/playground/twindleco/header/header5/header_twindle.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@700&display=swap');\n#header {\n    position: relative;\n    height: 10vh;\n    width: 100vw;\n    background-color: rgb(116, 22, 139);\n}\n\n#logo {\n    width: 100px;\n    height: 80%;\n    margin: 5px 21px;\n    background-color: white;\n    position: absolute;\n}\n\n#logo img {\n    height: 52px;\n    width: 100%;;\n\n}\n\nnav {\n    position: absolute;\n    display: flex;\n    align-items: center;\n    justify-content: flex-end;\n    top: 0;\n    height: 10vh;\n    margin-left: 6vw;\n    width: 94vw;\n}\n\nul {\n    display: flex;\n    justify-content: flex-end;\n    align-items: center;\n    height: 100%;\n}\n\nbody, header, ul{\n    margin: 0;\n}\n\nli {\n    list-style-type: none;\n    margin: 0 2vw;\n    font-size: 2.5vh;\n}\n\na{\n    text-decoration: none;\n    color: white;\n    padding: 1.2vw;\n    font-family: 'Noto Sans JP', sans-serif;\n}\n\na:hover {\n    background-color: rgb(135, 75, 150);\n    border-radius: 5px;\n}\n\n\n\n"
  },
  {
    "path": "misc/playground/twindleco/header/header5/header_twindle.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Header</title>\n    <link rel=\"stylesheet\" href=\"header_twindle.css\">\n</head>\n<body>\n    <header id=\"header\">\n        <div id=\"logo\"><img src=\"logo.JPG\" alt=\"logo\"></div>\n        <nav>\n            <ul>\n                <li><a href=\"#Home\">Home</a></li>\n                <li><a href=\"#OurTeam\">Our Team</a></li>\n                <li><a href=\"#Features\">Features</a></li>\n                <li><a href=\"#About\">About</a></li>\n            </ul>\n        </nav>\n    </header>\n</body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/header/header6/header6.css",
    "content": "body { \n    margin: 0;\n    font-family: Arial, Helvetica, sans-serif;\n  }\n  .box {\n    display: flex;\n    flex-wrap: nowrap;\n    background-color: #f1f1f1;\n  }\n  \n  .box > div {\n    text-align: center;\n    font-size: 30px;\n    padding: 20px 10px;\n  }\n  .box a {\n    color: black;\n    text-align: center;\n    padding: 12px;\n    text-decoration: none;\n    font-size: 18px; \n    line-height: 25px;\n    border-radius: 4px;\n  \n  }\n  .box a:hover {\n    background-color: #ddd;\n    color: black;\n    }\n  .logo\n  {\n  font-size: 30px;\n  font-weight: bold;\n  }\n  .push{\n  margin-left :auto;\n  }"
  },
  {
    "path": "misc/playground/twindleco/header/header6/header6.html",
    "content": "<!-- Header file for Twindle Home page created by Swati Arnd Rao-->\n\n<!DOCTYPE html>\n<html>\n<title>Welcome to Twindle</title>\n<head>\n<link rel=\"stylesheet\" href=\"header6.css\">\n</head>\n<body>\n\n<div class=\"box\">\n  <div class = \"logo\"><a href=\"#default\">Twindle📖</a></div>\n  <div class=\"push\"><a href=\"#home\">Home </a></div>\n  <div><a href=\"#about\">About Us  </a></div>\n  <div><a href=\"#email\">Email Us  </a></div>\n  </div>\n \n</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/header/header7/header7.css",
    "content": "*{\n    padding: 0;\n    margin: 0;\n    \n}\n\n\nli, a{\n    font-size: 18px;\n    color: #3b98d5;\n\n}\n\nheader{\n    display: flex;\n    justify-content: flex-end;\n    align-items: center;\n    background-color:  #eef9ff;\n    height: 50px;\n    padding: 25px;\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    box-shadow: 10px 15px 15px #D3D3D3;\n    \n}\n\n.link{\n    list-style: none;\n}\n\n.link,li{\n    display: inline-block;\n    padding: 0px 20px;\n}\n\n#logo{\n    margin-right: auto;\n    color: #3b98d5;\n}\n\nli,a:hover{\n    color:  #586066;\n}\n\n\n"
  },
  {
    "path": "misc/playground/twindleco/header/header7/header7.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Header</title>\n    <link rel=\"stylesheet\" href=\"header7.css\">\n</head>\n<body>\n  <header>\n    <h3 id='logo'><img src =\"C:\\Users\\Eudy\\Documents\\GitHub\\twindle\\playground\\twindleco\\Twindle-web-Teampage\\images\\twindle.png\" alt=\"Twindle-logo\"width=\"100px\"height=\"100px\"></h3>\n    <nav>  \n        <ul class='link'>\n            <li><a href='#ABOUT'>ABOUT</a></li>\n            <li><a href='#TEAM'>TEAM</a></li>\n            <li><a href='#CONTACT'>CONTACT</a></li>\n        </ul>  \n    </nav>\n</header>\n        \n\n</body>\n</html>"
  },
  {
    "path": "misc/playground/twindleco/header8/header8.css",
    "content": "* {\r\n    margin: 0;\r\n    padding: 0;\r\n  }\r\n  \r\n  \r\n  .navbar {\r\n    width: 100%;\r\n    overflow: hidden;\r\n    height: 70px;\r\n    line-height: 70px;\r\n    background: rgb(62, 121, 155);\r\n    border-bottom: 2px solid blue;\r\n    position: fixed;\r\n    z-index: 9999;\r\n  }\r\n  \r\n  .logo{ \r\n  width: 100px;\r\n  height: 70px;\r\n  float: left;\r\n  }\r\n  \r\n  .menu {\r\n    float: right; \r\n  }\r\n  \r\n  .menu li { \r\n  float: left;\r\n  width: 120px;\r\n  height: 70px;\r\n  line-height: 70px;\r\n  text-align: center;\r\n  list-style-type: none;\r\n  }\r\n\r\n  .menu li a { \r\n  color: whitesmoke;\r\n  text-decoration: none;\r\n  list-style: none;\r\n  font-family: Calibri;\r\n  text-transform: capitalize;\r\n  display: block;\r\n  }\r\n  \r\n\r\n  .menu li a:hover {\r\n    background: rgb(40, 54, 179);\r\n  }"
  },
  {
    "path": "misc/playground/twindleco/header8/header8.html",
    "content": "<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <link rel=\"stylesheet\" href=\"header8.css\">\r\n    <title>Twindle</title>\r\n</head>\r\n<body>\r\n   \r\n             <div class=\"navbar\">                   \r\n                  <img id=\"header-img\" src=\"twindleImg.jpg\" alt=\"company_logo\" class=\"logo\">         \r\n                <ul class=\"menu\">  \r\n                    <li><a class=\"nav-link\" href=\"\">About Us</a></li>\r\n                    <li><a class=\"nav-link\" href=\"\">Our Team</a></li>\r\n                    <li><a class=\"nav-link\" href=\"\">Features</a></li>\r\n                </ul>\r\n              </div>\r\n       </body>\r\n</html>\r\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage1/Twindle Home Page.html",
    "content": "<!DOCTYPE html>\n\n<html>\n\n    <head>\n    \n    <meta charset=\"utf-8\">\n    \n    <meta name= \"description\" content= \"Description\">\n    <meta name= \"description\" content= \"Description\">  \n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <base target=\"_blank\">    \n    <title>Twindle - Home</title> \n     <link href=\"style.css\" type =\"text/css\" rel=\"stylesheet\" media=\"screen\">     \n     <link href=\"https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap\" rel=\"stylesheet\">\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\">\t\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css\"> \n    <link rel=\"stylesheet\" type=\"text/css\" href=\"font-awesome.min.css\">   \n                  \n    <title>Twindle - Home</title>    \n                \n    </head>\n    <style>a {text-decoration: none;} </style>\n    <body>\n        <!------- HEADER AND NAVIGATION BAR----->\n        <header>\n            <div>\n              <img src=\"twindle.png\" alt= \"twindle logo\" style=\" height :120px ; width: 30%; float: left;\">\n            </div>\n              \n              <div class = \"nav\">\n                  <a href=\"#\">Our Team</a>\n                  <a href=\"#\">About</a>\n                  <a href=\"#\">Features</a>\n              \n              </div>\n            \n          </header> \n         <!----------BANNER---------->\n          <div class= \"banner\" >\n            <img src=\"img.jpg\">\n          </div>\n          <br>\n            <h2 class=\"heading\" >Export your tweets to pdf or epub with the click of a button</h2>\n            <br>\n          <div class=\"details\">\n            <input \n              type=\"text\"\n              class=\"input\"\n              placeholder = \"Enter twitter thread link here\"\n            />\n            <br>\n            <button type=\"submit\" class=\"cnvt-btn\" style=\"color: black;\">Convert</button>\n          </div>\n          <br><br>\n          <hr /> \n         \n        <br>\n        <div class= \"how\">\n          <h2 class=\"heading\">How is a Twitter thread converted to PDF or EPUB?</h2>\n          <br>\n          Lorem ipsum dolor sit amet, consectetur adipiscing elit. \n          Cras vitae urna porttitor, dignissim magna et, lobortis dui. \n          Sed ullamcorper quam eget ultrices efficitur. \n          Aliquam vulputate a urna vel bibendum. \n          Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n          Suspendisse varius, ex a vehicula fringilla, libero purus blandit ex, eget bibendum risus mi sit amet nisl. \n          Nam viverra scelerisque arcu, nec ultrices eros molestie quis. \n          Pellentesque vestibulum pharetra turpis, et sodales justo tempus sit amet.\n\n          Integer semper lectus sit amet eros volutpat faucibus. \n          Proin sed eros nec est mollis efficitur at quis arcu. \n          Vivamus iaculis lobortis molestie. \n          Etiam ut dignissim sem, quis aliquam ligula. \n          Aenean vehicula dapibus eros vitae egestas. \n          Donec sed lectus id libero gravida sodales. \n          Donec eu finibus erat, sed lacinia arcu. \n          Vestibulum consequat augue at mattis sagittis. \n          Nullam sit amet quam aliquet, rutrum erat a, ornare lacus. \n          Duis in elit in ex semper vestibulum sed in velit. \n          Vivamus lobortis arcu non libero ultrices, id sollicitudin velit eleifend. \n          Aenean efficitur, odio quis facilisis posuere, lacus lorem volutpat risus, a malesuada augue elit vitae erat.\n        </div>  \n        <br>\n      </div>\n\n    <!---------FOOTER------->   \n    \n    <div class= \"social-container\"> \n        <div class=\"social-buttons\">\n          <a href=\"#\"><i class=\"fab fa-github\"></i></a>\n          <a href=\"#\"><i class=\"fab fa-youtube\"></i></a> \n          <a href=\"#\"><i class=\"fab fa-twitter\"></i></a>\n          <a href=\"#\"><i class=\"fab fa-dribbble\"></i></a>  \n        </div> \n    \n    </body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage1/style.css",
    "content": "\nbody\n{\n  background: linear-gradient(90deg, #4975ee 0%,#3e40b3 25% ,#1b1da8 50%, #030f7e 100%);\n}\n\n\n*{\n    font-family: 'Noto Sans JP', sans-serif;\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n }\n\n\nheader\n{\n  \n    display: flex;\n    justify-content: space-between;\n    width : 100% ;\n    align-items: center;\n    background: linear-gradient(90deg, #020250 0%,#01033a 25% ,#01021b 50%, #000011 100%);\n\theight: 65px;\n   \n}\n\nheader a\n{\n   \n    justify-content: space-between;\n    \n    padding: 9px 25px;\n    border-radius: 5px;\n\t  border: 0px;\n    color: #2577d4;\n\t  height: 45px;\n}\n\n.nav{\n\tdisplay: flex;\n\tjustify-content: space-between;\n\talign-items: center;\n\twidth: 430px;\n\theight: 100%;\n\tpadding-right: 7px\n}\n.heading\n{\n  color: #fff;\n  text-align: center;\n}\n\na:hover\n{\n  font-size: 150%;\n  color: white;\n}\n\n.input \n{\n  display: -webkit-box;\n  display: -webkit-flex;\n  display: -moz-box;\n  display: -ms-flexbox;\n  display: flex;\n  justify-content: center;\n  align-items: center; \n  padding :20px;\n  border-radius: 10px;\n  margin: auto;\n  \n}\n.cnvt-btn\n{\n  display: -webkit-box;\n  display: -webkit-flex;\n  display: -moz-box;\n  display: -ms-flexbox;\n  margin: auto;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  padding: 0 20px;\n  min-width: 160px;\n  height: 62px;\n  border-radius: 20px;\n\n  font-family: Arial, Helvetica, sans-serif;\n  font-size: 16px;\n  color: rgb(255, 255, 255);\n  line-height: 1.2;\n}\n\n.banner\n{\n    top: 0;\n    left: 0;\n    margin-left: auto;\n    margin-right: auto;\n    height: 100%;\n    width: 100%;\n \n}\n.details\n{\n    top: 0;\n    left: 0;\n\n    margin : 0 auto;\n}\n .how\n {\n   text-align: left;\n   color: #fff;\n   display: block;\n   padding-left: 300px;\n   padding-right: 300px;\n }\n\n.banner img\n{\n  width: 100px;\n  height: 100px;\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n  margin-top: 30px;\n  border-radius: 10px;\n}\n\n.details\n{\n  display: block;\n  margin-left: auto;\n  margin-right: auto;\n}\n\n/*Social Buttons*/\n\n.social-container{\n    background: #0d0d0e;\n    height: 20vh;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-top: 0;\n  }\n  \n  .social-buttons a{\n    display: inline-flex;\n    text-decoration: none;\n    font-size: 18px;\n    width: 60px;\n    height: 60px;\n    color: #fff;\n    justify-content: center;\n    align-items: center;\n    position: relative;\n    margin: 0 8px;\n  }\n  \n  .social-buttons a::before{\n    content: \"\";\n    position: absolute;\n    width: 60px;\n    height: 60px;\n    background: linear-gradient(45deg,#10a2db,#070aa0);\n    border-radius: 50%;\n    z-index: -1;\n    transition: 0.3s ease-in;\n  }\n  \n  .social-buttons a:hover::before{\n    transform: scale(0);\n  }\n  \n  .social-buttons a i{\n    transition: 0.3s ease-in;\n  }\n  \n  .social-buttons a:hover i{\n    background: linear-gradient(45deg,#2577d4,#1b09c2);\n    background-clip: text;\n    -webkit-text-fill-color: transparent;\n    transform: scale(2.2);\n  }"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage2/README.md",
    "content": "# twindle-web\n\nhere's the preview link: https://madhusudanbabar.github.io/twindle-web"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage2/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link rel=\"stylesheet\" href=\"style.css\" />\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Sacramento&display=swap\"\n      rel=\"stylesheet\"\n    />\n    <script src=\"https://code.jquery.com/jquery-3.5.1.min.js\" integrity=\"sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=\" crossorigin=\"anonymous\"></script>\n    <title>Twindle - the tweeter reader</title>\n  </head>\n  <body>\n    <!-- HEADER START -->\n    <header class=\"header\">\n      <div class=\"header__logo-box\">\n        <img src=\"./assets/twindle.png\" alt=\"Twindle\" class=\"header__logo\" />\n      </div>\n      <ul class=\"header__menu\">\n        <li class=\"header__item\">\n          <a href=\"#\" class=\"header__link\">Our Team</a>\n        </li>\n        <li class=\"header__item\"><a href=\"#\" class=\"header__link\">About</a></li>\n        <li class=\"header__item\">\n          <a href=\"#\" class=\"header__link\">Features</a>\n        </li>\n      </ul>\n    </header>\n    <!-- HEADER END -->\n\n    <!-- HOME SECTION -->\n    <div class=\"home\">\n      <h1 class=\"home__title\">Twindle</h1>\n      <h2 class=\"home__title--sub\">Export your tweets to pdf easily</h2>\n\n      <input\n        type=\"text\"\n        class=\"home__input\"\n        placeholder=\"Enter account id here\"\n        value=\"madhusudanbabar\"\n      />\n      <button type=\"submit\" class=\"home__cta\">Click to convert</button>\n    </div>\n    <hr />\n    <!-- HOME SECTION -->\n\n    <!-- FAQ -->\n    <ul class=\"faq\">\n      <li class=\"faq__item\">\n        <h1 class=\"faq__title\">\n          How twitter thread is converted to pdf or epub\n        </h1>\n        <p class=\"faq__info\">\n          Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam\n          voluptas explicabo alias aliquam enim harum facere non et repellat,\n          officia dignissimos, numquam, repudiandae incidunt quidem ex vel\n          accusamus fugiat corrupti? Lorem ipsum dolor sit amet consectetur\n          adipisicing elit. Rem atque maxime repellendus laudantium. Eius\n          mollitia dolor amet? Repellendus, quos amet corporis eum dolorem ab\n          officiis soluta, porro odio aliquid quae. Lorem ipsum dolor sit, amet\n          consectetur adipisicing elit. Voluptatum ut iusto aliquid dolores\n          error omnis incidunt repellat at? Sit distinctio sequi, iure animi\n          quisquam recusandae. Maxime consectetur nostrum numquam odit. Lorem,\n          ipsum dolor sit amet consectetur adipisicing elit. Porro perferendis\n          ex maxime neque alias, sunt tenetur veniam officia rem facilis\n          voluptas earum quia et id error consectetur sit ea magnam. Lorem ipsum\n          dolor sit amet consectetur adipisicing elit. Error praesentium et\n          tempora harum. Dicta cupiditate est reiciendis et perferendis dolorem\n          aperiam cum iste necessitatibus nihil numquam tempore, quam nam ea!\n        </p>\n      </li>\n      <li class=\"faq__item\">\n        <h1 class=\"faq__title\">\n          How twitter thread is converted to pdf or epub\n        </h1>\n        <p class=\"faq__info\">\n          Rem atque maxime repellendus laudantium. Eius mollitia dolor amet?\n          Repellendus, quos amet corporis eum dolorem ab officiis soluta, porro\n          odio aliquid quae. Lorem ipsum dolor sit, amet consectetur adipisicing\n          elit. Voluptatum ut iusto aliquid dolores error omnis incidunt\n          repellat at? Sit distinctio sequi, iure animi quisquam recusandae.\n          Maxime consectetur nostrum numquam odit. Lorem, ipsum dolor sit amet\n          consectetur adipisicing elit. Porro perferendis ex maxime neque alias,\n          sunt tenetur veniam officia rem facilis voluptas earum quia et id\n          error consectetur sit ea magnam. Lorem ipsum dolor sit amet\n          consectetur adipisicing elit. Error praesentium et tempora harum.\n          Dicta cupiditate est reiciendis et perferendis dolorem aperiam cum\n          iste necessitatibus nihil numquam tempore, quam nam ea!\n        </p>\n      </li>\n      <li class=\"faq__item\">\n        <h1 class=\"faq__title\">\n          How twitter thread is converted to pdf or epub\n        </h1>\n        <p class=\"faq__info\">\n          Lorem ipsum dolor sit amet consectetur adipisicing elit. Aperiam\n          voluptas explicabo alias aliquam enim harum facere non et repellat,\n          officia dignissimos, numquam, repudiandae incidunt quidem ex vel\n          accusamus fugiat corrupti? Lorem ipsum dolor sit amet consectetur\n          adipisicing elit. Rem atque maxime repellendus laudantium. Eius\n          mollitia dolor amet? Repellendus, quos amet corporis eum dolorem ab\n          officiis soluta, porro odio aliquid quae. Lorem ipsum dolor sit, amet\n          consectetur adipisicing elit. Voluptatum ut iusto aliquid dolores\n          error omnis incidunt repellat at? Sit distinctio sequi, iure animi\n          quisquam recusandae. Maxime consectetur nostrum numquam odit. Lorem,\n          ipsum dolor sit amet consectetur adipisicing elit.\n        </p>\n      </li>\n    </ul>\n    <hr />\n    <!-- FAQ -->\n\n    <!-- CONTACT -->\n    <div class=\"contact\">\n      <h1 class=\"contact__title\">Drop your emails for latest updates.</h1>\n      <form name=\"form\"\n        action=\"https://docs.google.com/forms/u/0/d/e/1FAIpQLSeKaWedF1F53Uql2AEXEGSiZFG_vy5x4hUDxjSoS9kBuPee-Q/formResponse\"\n        class=\"contact__form\" id=\"form\"\n      >\n        <input type=\"email\" class=\"contact__input\" id=\"email\" name=\"entry.1537290942\" placeholder=\"you@example.com\" required />\n        <button type=\"submit\" class=\"contact__cta\">subscribe</button>\n        <p class=\"contact__status\"></p>\n      </form>\n    </div>\n    <!-- CONTACT -->\n\n    <!-- FOOTER -->\n    <footer class=\"footer\">\n      <ul class=\"footer__menu\">\n        <li class=\"footer__item\">\n          <a href=\"#\" class=\"footer__link\"\n            ><img src=\"./assets/twitter.png\" alt=\"twitter\" class=\"footer__logo\"\n          /></a>\n        </li>\n        <li class=\"footer__item\">\n          <a href=\"youtube\" class=\"footer__link\"\n            ><img src=\"./assets/youtube.png\" alt=\"youtube\" class=\"footer__logo\"\n          /></a>\n        </li>\n        <li class=\"footer__item\">\n          <a href=\"github\" class=\"footer__link\"\n            ><img src=\"./assets/github.png\" alt=\"github\" class=\"footer__logo\"\n          /></a>\n        </li>\n      </ul>\n      <h1 class=\"footer__copyright\">\n        &copy; Twindle 2020 - All rights reserved\n      </h1>\n    </footer>\n    <!-- FOOTER -->\n    <script src=\"./index.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage2/index.js",
    "content": "$(\"form\").on(\"submit\", function (event) {\n  event.preventDefault();\n  var form = $(this);\n  const action = form.attr(\"action\");\n  var email = $(\"email\").val();\n  console.log(form.serialize());\n\n  $.ajax({\n    type: \"POST\",\n    url: action,\n    data: form.serialize(),\n    dataType: \"json\",\n    statusCode: {\n      0: function () {\n        $(\"form\")[0].reset();\n        $(\".contact__status\").html(\"Thank you for being part of <strong>Twindle</strong> family ! <br>You wil be the first one to know about new releases!!!\");\n      },\n      200: function () {\n        $(\"form\")[0].reset();\n        $(\".contact__status\").html(\"Email Subscribe Successfully !!!\");\n      },\n    },\n    // contentType: \"application/json; charset=utf-8\",\n    success: function (data) {\n      //var ms = data['msg'];\n      if (data[\"result\"] != \"success\") {\n        $(\"#FailureMsg\").html(\"Mail not sent, Something went Wrong!\");\n\n      } else {\n        $(\"form\")[0].reset();\n        $(\".contact__status\").html(\"Email Subscribe Successfully !!!\");\n      }\n    },\n    error: function (err) {\n      $(\".contact__status\").html(\n        \"Could not connect to the registration server. Please try again later!\" +\n          JSON.stringify(err)\n      );\n    },\n  });\n});\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage2/style.css",
    "content": "*,\n*::after,\n*::before {\n  margin: 0;\n  padding: 0;\n  -webkit-box-sizing: inherit;\n          box-sizing: inherit;\n}\n\nhtml {\n  font-size: 62.5%;\n  -webkit-box-sizing: border-box;\n          box-sizing: border-box;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n}\n\nbody {\n  font-size: 1.6rem;\n}\n\n:root {\n  --logo-size: 7.5rem;\n  --color-primary: #345391;\n  --color-secondary: #d43b76;\n  --separator: 5px dashed var(--color-primary);\n}\n\n*[class$=\"title\"] {\n  font-size: 2.5rem;\n  color: var(--color-primary);\n  text-align: center;\n  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n  margin: 5rem auto 2.5rem auto;\n}\n\n*[class$=\"input\"] {\n  display: inline-block;\n  margin-top: 5rem;\n  font-size: 1.6rem;\n  border: 1px solid gray;\n  border-radius: 7.5rem;\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  text-align: center;\n  padding: .75em 2rem;\n  color: var(--color-primary);\n}\n\n*[class$=\"input\"]:focus {\n  outline: none;\n  border-style: dashed;\n}\n\n*[class$=\"input\"]::-webkit-input-placeholder {\n  color: var(--color-primary) !important;\n}\n\n*[class$=\"cta\"] {\n  display: block;\n  padding: .75rem 2rem;\n  color: white;\n  background-color: var(--color-secondary);\n  border: none;\n  border-radius: 7.5rem;\n  font-size: 2rem;\n  margin-top: 1rem;\n  -webkit-box-shadow: 0px 1px 2px var(--color-secondary);\n          box-shadow: 0px 1px 2px var(--color-secondary);\n  -webkit-transition: all .3s;\n  transition: all .3s;\n}\n\n*[class$=\"cta\"]:hover {\n  -webkit-box-shadow: 0px 2px 2px var(--color-secondary);\n          box-shadow: 0px 2px 2px var(--color-secondary);\n  -webkit-transform: translateY(-1px);\n          transform: translateY(-1px);\n  background: white;\n  color: var(--color-secondary);\n}\n\n*[class$=\"cta\"]:active {\n  -webkit-transform: translateY(0);\n          transform: translateY(0);\n}\n\n*[class$=\"cta\"]:focus {\n  outline: none;\n  -webkit-box-shadow: 0px 1px 2px var(--color-secondary);\n          box-shadow: 0px 1px 2px var(--color-secondary);\n}\n\n.header {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: horizontal;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: row nowrap;\n          flex-flow: row nowrap;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n          justify-content: space-between;\n  padding: 0 2rem;\n  font-size: 1.6rem;\n  height: var(--logo-size);\n  border-bottom: var(--separator);\n  border-width: 2.5px;\n  position: fixed;\n  top: 0;\n  left: 0;\n  background-color: white;\n  width: 100%;\n}\n\n.header__logo-box {\n  height: var(--logo-size);\n  width: var(--logo-size);\n}\n\n.header__logo {\n  height: 100%;\n  width: 100%;\n  -o-object-fit: cover;\n     object-fit: cover;\n}\n\n.header__menu {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: horizontal;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: row nowrap;\n          flex-flow: row nowrap;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  list-style: none;\n}\n\n.header__link {\n  display: block;\n  padding: 1rem;\n  -webkit-transition: all .3s ease;\n  transition: all .3s ease;\n  text-decoration: none;\n  border-radius: .5rem;\n  color: black;\n}\n\n.header__link:hover {\n  color: var(--color-primary);\n  background-color: rgba(76, 43, 243, 0.1);\n}\n\n.home {\n  margin-top: var(--logo-size);\n  background: -webkit-gradient(linear, left top, right bottom, from(rgba(194, 97, 233, 0.2)), to(rgba(76, 43, 243, 0.1))), url(./assets/cover.jpg);\n  background: linear-gradient(to bottom right, rgba(194, 97, 233, 0.2), rgba(76, 43, 243, 0.1)), url(./assets/cover.jpg);\n  min-height: calc(100vh - var(--logo-size));\n  background-repeat: no-repeat;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: column wrap;\n          flex-flow: column wrap;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  border-bottom: var(--separator);\n}\n\n.home__input {\n  background-color: rgba(255, 255, 255, 0.75);\n}\n\n.home__input::-webkit-input-placeholder {\n  color: white;\n}\n\n.home__title {\n  font-size: 7.5rem;\n  margin-top: 10rem;\n  margin-bottom: 0;\n  font-family: 'Sacramento', cursive;\n}\n\n.faq {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: column nowrap;\n          flex-flow: column nowrap;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  list-style: none;\n  padding: 2rem 0 5rem;\n  border-bottom: var(--separator);\n}\n\n.faq__item {\n  max-width: 75%;\n}\n\n.contact {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: column nowrap;\n          flex-flow: column nowrap;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding: 2rem 0 5rem;\n  border-bottom: var(--separator);\n}\n\n.contact__form {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: column;\n          flex-flow: column;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n}\n\n.contact__input {\n  margin-top: 0rem;\n  border-radius: 7.5rem;\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none;\n  background-color: #618adb3b;\n}\n\n.contact__input::-webkit-input-placeholder {\n  color: var(--color-primary);\n}\n\n.contact__input:focus {\n  outline: none;\n  border-style: dashed;\n}\n\n.contact__status {\n  text-align: center;\n  color: var(--color-primary);\n}\n\n.contact__cta {\n  border-radius: 7.5rem;\n}\n\n.footer {\n  padding: 2rem 0 5rem;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-flow: column;\n          flex-flow: column;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n}\n\n.footer__menu {\n  list-style: none;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n}\n\n.footer__link {\n  padding: 1rem 2rem;\n}\n\n.footer__logo {\n  height: 4rem;\n  width: 4rem;\n  -o-object-fit: cover;\n     object-fit: cover;\n}\n\n.footer__copyright {\n  font-size: 1.6rem;\n  font-weight: 500;\n  margin-top: 1rem;\n}\n/*# sourceMappingURL=style.css.map */"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage2/style.scss",
    "content": "//  RESET\n*,\n*::after,\n*::before{\n    margin: 0;\n    padding: 0;\n    box-sizing: inherit;\n}\n\nhtml{\n    font-size: 62.5%;\n    box-sizing: border-box;\n    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n}\n\nbody{\n    font-size: 1.6rem;\n}\n\n// VARIABLES\n:root{\n    --logo-size: 7.5rem;\n    --color-primary: #345391;\n    --color-secondary: #d43b76;\n    --separator: 5px dashed var(--color-primary);\n}\n\n// TYPOGRAPHY\n\n*[class$=\"title\"]{\n    font-size: 2.5rem;\n    color: var(--color-primary);\n    text-align: center;\n    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n    margin: 5rem auto 2.5rem auto;\n}\n\n*[class$=\"input\"]{\n    display: inline-block;\n    margin-top: 5rem;\n    font-size: 1.6rem;\n    border: 1px solid gray;\n    border-radius: 7.5rem;\n    // border-radius: .5rem;\n    appearance: none;\n    text-align: center;\n    padding: .75em 2rem;\n    color: var(--color-primary);\n\n    &:focus{\n        outline: none;\n        border-style: dashed;;\n    }\n\n    &::-webkit-input-placeholder{\n        color: var(--color-primary) !important;\n    }\n}\n\n*[class$=\"cta\"]{\n    display: block;\n    padding: .75rem 2rem;\n    color: white;\n    background-color: var(--color-secondary);\n    border: none;\n    border-radius: 7.5rem;\n    font-size: 2rem;\n    margin-top: 1rem;\n    box-shadow: 0px 1px 2px var(--color-secondary);\n\n    transition: all .3s;\n\n    &:hover{\n        box-shadow: 0px 2px 2px var(--color-secondary);\n        transform: translateY(-1px);\n        background: white;\n        color: var(--color-secondary);\n    }\n    \n    &:active{\n        transform: translateY(0);\n    }\n\n    &:focus{\n        outline: none;\n        box-shadow: 0px 1px 2px var(--color-secondary);\n}\n}\n\n\n// COMPONENTS\n// HEADER\n.header{\n    display: flex;\n    flex-flow: row nowrap;\n    align-items: center;\n    justify-content: space-between;\n    padding: 0 2rem;\n    font-size: 1.6rem;\n    height: var(--logo-size);\n    border-bottom: var(--separator);\n    border-width: 2.5px;\n\n    position: fixed;\n    top: 0;\n    left: 0;\n    background-color: white;\n    width: 100%;\n\n    &__logo-box{\n        height: var(--logo-size);\n        width: var(--logo-size);\n    }\n\n    &__logo{\n        height: 100%;\n        width: 100%;\n        object-fit: cover;\n    }\n    &__menu{\n        display: flex;\n        flex-flow: row nowrap;\n        align-items: center;\n\n        list-style: none;\n\n    }\n\n    &__link{\n        display: block;\n        padding: 1rem;\n        transition: all .3s ease;\n        text-decoration: none;\n        border-radius: .5rem;\n\n        color: black;\n\n\n        &:hover{\n           color: var(--color-primary);\n           background-color:  rgba(76, 43, 243, 0.1); \n        }\n    }\n}\n\n// HOME\n.home{\n    margin-top: var(--logo-size);\n    background: linear-gradient(to bottom right,rgba(194, 97, 233, 0.2), rgba(76, 43, 243, 0.1)) , url(./assets/cover.jpg);\n    min-height: calc(100vh - var(--logo-size));\n    background-repeat: no-repeat;\n    display: flex;\n    flex-flow: column wrap;\n    align-items: center;\n    \n\n    border-bottom: var(--separator);\n\n    &__input{\n        background-color: rgba(255, 255, 255, 0.75);\n\n        &::-webkit-input-placeholder{\n            color: white;\n        }\n\n    }\n\n    &__title{\n        font-size: 7.5rem;\n        margin-top: 10rem;\n        margin-bottom: 0;\n        font-family: 'Sacramento', cursive;\n    }\n}\n\n//  FAQ\n.faq{\n    display: flex;\n    flex-flow: column nowrap;\n    align-items: center;\n    list-style: none;\n    padding: 2rem 0 5rem;\n    border-bottom: var(--separator);\n\n    &__item{\n        max-width: 75%;\n    }\n}\n\n//  CONTACT\n.contact{\n    display: flex;\n    flex-flow: column nowrap;\n    align-items: center;\n    justify-content: center;\n    padding: 2rem 0 5rem;\n\n    border-bottom: var(--separator);\n\n    &__form{\n        display: flex;\n        flex-flow: column;\n        align-items: center;\n    }\n\n    &__input{\n        margin-top: 0rem;\n        border-radius: 7.5rem;\n        appearance: none;\n        background-color: #618adb3b;\n\n        &::-webkit-input-placeholder{\n            color: var(--color-primary);\n        }\n        &:focus{\n            outline: none;\n            border-style: dashed;;\n        }\n    }\n\n    &__status{\n        text-align: center;\n        color: var(--color-primary);\n    }\n\n    &__cta{\n        border-radius: 7.5rem;\n    }\n}\n\n//  FOOTER\n.footer{\n    padding: 2rem 0 5rem;\n    display: flex;\n    flex-flow: column;\n    justify-content: center;\n    align-items: center;\n\n    &__menu{\n        list-style: none;\n        display: flex;\n    }\n\n    &__link{\n        padding: 1rem 2rem;\n    }\n\n    &__logo{\n        height: 4rem;\n        width: 4rem;\n        object-fit: cover;\n    }\n\n    &__copyright{\n        font-size: 1.6rem;\n        font-weight: 500;\n        margin-top: 1rem;\n    }\n}\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage3/index.css",
    "content": "body {\r\n  background:white;\r\n  padding: 0px;\r\n  font-family: Helvetica;\r\n  margin:0;\r\n}\r\n\r\nhr{\r\n  height: 4px;\r\n  margin-left: 2px;\r\n  width: 200px;\r\n  background-color:#0000ff;\r\n  text-align: left;\r\n}\r\n\r\nfooter{\r\n  text-align: center;\r\n}\r\n#secondhr\r\n{\r\n  width:200px;\r\n  height: 4px;\r\n}\r\n#foothr\r\n{\r\n  height: 2px;\r\n  width:100%;\r\n}\r\n\r\n.index_second_div\r\n{\r\n  padding:440px 350px;\r\n  \r\n}\r\n\r\n.index_first_div{\r\n  padding:20px 40px;\r\n}\r\n.index_sub_first\r\n{\r\n  padding:10px 40px;\r\n  font-size: 20px;\r\n}\r\n.index_sub_third\r\n{\r\n  padding:4px 40px;\r\n  font-size: 20px;\r\n}\r\nbutton\r\n{\r\n\tborder-radius: 4px;\r\n\tbackground-color:hsl(240,100%,50%);\r\n\tborder:white;\r\n\theight:50px;\r\n\twidth:150px;\r\n\tcolor:white;\r\n  text-align:center;\r\n  font-size: 21px;\r\n\tpadding:10px;\r\n\tmargin:5px;\r\n  box-shadow:0px 5px #999;\r\n  \r\n\t\r\n}\r\nbutton:hover\r\n{\r\n\tbackground-color:hsl(240,100%,45%);\r\n\tcursor:pointer;\r\n\tborder:white;\r\n\tbox-shadow:0px 10px #999;\r\n\ttransform:translateY(-4px);\r\n\t\r\n}\r\np {  \r\n    padding: 0 20px 20px 0;\r\n    font-size: 24px;\r\n    \r\n  }\r\n.logo img\r\n{\r\n  position: absolute;\r\n  margin-top: 0px;\r\n  margin-left: 0px;\r\n  \r\n}\r\n\r\n#img {\r\n    float: right;\r\n    padding: 0px;\r\n  }\r\n\r\n  \r\nnav {\r\n    background-color:blue;\r\n    width: 100%;\r\n    overflow: auto;\r\n    \r\n  }\r\n\r\n  ul{\r\n    padding: 0;\r\n    margin:0 0 0 150px;\r\n    list-style: none;\r\n  }\r\n \r\n  li\r\n  {\r\n    float: left;\r\n  }\r\n  nav a {\r\n    float: left;\r\n    display:block;\r\n    color: #f2f2f2;\r\n    text-align: center;\r\n    padding: 16px 20px;\r\n    margin-left: 4px;\r\n    text-decoration: none;\r\n    font-size: 24px;\r\n    width:100px;\r\n    \r\n  }\r\n  \r\n \r\n  nav a:hover {\r\n    background-color: #ddd;\r\n    color: black;\r\n    transition: 0.5s;\r\n    \r\n  }\r\n   \r\n\r\n  /*.nav a.active {\r\n    background-color: 1d72d8;\r\n    color: white;\r\n  }*/"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage3/index.html",
    "content": "<!DOCTYPE html>\r\n<html>\r\n    <head>\r\n        <title>Twindle</title>\r\n        <link rel = \"icon\" href =  \r\n        \"assets/titleLogo.png\" \r\n                type = \"image/x-icon\"> \r\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n        <link rel=\"stylesheet\" href=\"index.css\">\r\n    </head>\r\n    <body>\r\n        <div class=\"logo\"><a href=\"./index.html\"><img src=\"assets/Logo_Topnav.png\" alt=\"logo\" /></a></div>\r\n        <nav> \r\n          <ul>        \r\n            <li><a class=\"active\" href=\"./index.html\">Home</a></li>\r\n            <li><a href=\"./blog.html\">Blog</a></li>\r\n            <li><a href=\"./team.html\">Team</a></li>\r\n            </li><a href=\"./faq.html\">FAQs</a></li>\r\n          </ul>  \r\n          </nav>\r\n        \r\n        <section id=\"#home\">\r\n          <div class=\"img_text\">            \r\n            <p>\r\n              <div class=\"index_first_div\">\r\n              <h2>What is Twindle?</h2><hr></hr>\r\n          </div>\r\n          <div class=\"check\"><img src=\"assets/index_assets/Girl_with_dog.svg\"alt=\"image\" id=\"img\"  width=\"700\" height=\"700\" />\r\n          </div>\r\n              <div class=\"index_sub_first\">\r\n              Twindle is an open source project made for the purpose of converting long \r\n              Twitter threads into ePub's or PDF documents to allow users to read longscroll \r\n            threads on a kindle or as a PDF on their device of choice.\r\n            <br></br>\r\n            <a href=\"./conversion.html\"><button>Try for Free</button></a>\r\n          </div>\r\n        </p>\r\n\r\n          </div>\r\n          <div class=\"index_second_div\">\r\n            <br><br><br><br>\r\n            <p>\r\n              <div class=\"index_sub_second\">\r\n              <h2>\r\n              Why should twitter users consider Twindle to read through Twitter thread?</h2>\r\n              <hr id=\"secondhr\"></hr>\r\n            </div>\r\n              <img src=\"assets/index_assets/boy_describing.svg\" width=\"400\" height=\"400\"><br>\r\n              <div class=\"index_sub_third\">\r\n              In walks Twindle, a web application allows users to convert threads on Twitter to pdf or ePub format. -\r\n               The users can store the pdf/ePub on a digital device - The users can store the pdf/ePub on\r\n                cloud services i.e, Dropbox, Google Drive and access offline, - Otherwise, print a hard copy and \r\n                read like a good-old-traditional-reader.\r\n              </div>\r\n            </p>\r\n\r\n          </div>\r\n        </section>\r\n        <footer>\r\n          <hr id=\"foothr\">\r\n          <h2>Want to Contribute?</h2>\r\n          <img src=\"assets/index_assets/handshake-colour.svg\" height=\"300\" width=\"300\">\r\n          <p>Connect with us</p>\r\n          <div id=\"footimg\">\r\n          <a href=\"https://twitter.com/twindleco\"><img src=\"assets/twitter_logo.png\" width=\"45\" height=\"45\" ></a>\r\n          <a href=\"https://github.com/twindle-co/twindle\"><img src=\"assets/github_icon.png\" width=\"50\" height=\"50\"></a>\r\n        </div>\r\n        </footer>\r\n\r\n    </body>\r\n</html>"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage4/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link rel=\"stylesheet\" href=\"styles.css\">\n    <title>Twindle Landing Page - Style 2</title>\n    <script src=\"https://unpkg.com/ionicons@5.2.3/dist/ionicons.js\"></script>\n</head>\n<body id=\"home\">\n    <nav class=\"navbar\">\n        <div class=\"container\">\n            <img src=\"img/twindle.png\" width=\"60px\"/>\n            <ul class=\"nav\">\n                <li><a href=\"#\">Features</a></li>\n                <li><a href=\"#\">Team</a></li>\n                <li><a href=\"#\">About Us</a></li>\n            </ul>\n        </div>\n    </nav>\n\n    <section class=\"section-convert\">\n        <div class=\"container\">\n            <h2 class=\"section-title\">Twindle</h2>\n            <input type=\"text\" placeholder=\"Insert your link here\">\n            <button class=\"button\">Click to Convert</button>\n        </div>\n        \n    </section>\n    <hr>\n    <section class=\"section-description\">\n        <div class=\"container\">\n            <div>\n                <h2 class=\"section-title\">How Twitter thread is converted to pdf or ePub?</h2>\n                <p>\n                    Lorem ipsum dolor sit amet consectetur, adipisicing elit. Porro perferendis dolorem eaque totam expedita dolorum similique, laborum repellat quia facere voluptates magnam? Aliquid, iure a. Adipisci error ratione exercitationem neque!\n                </p>\n            </div>\n            <img src=\"img/bird.png\" width=\"300px\" class=\"img-bird\">\n        </div>\n    </section>\n\n    <footer class=\"section-footer\">\n        <div class=\"container\">\n            <div class=\"contact\">\n              <a href=\"https://twitter.com/twindleco\">\n                <ion-icon name=\"logo-twitter\"></ion-icon>\n              </a>\n              <a href=\"https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA\">\n                <ion-icon name=\"logo-youtube\"></ion-icon>\n              </a>\n              <a href=\"https://github.com/twindle-co\">\n                <ion-icon name=\"logo-github\"></ion-icon>\n              </a>\n            </div>\n            <div>© Twindle 2020</div>\n    </footer>\n</body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage4/styles.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Indie+Flower&family=Poppins:wght@300&display=swap');\n\n:root {\n    --primary-color: rgb(86, 115, 184);\n    --secondary-color: rgb(100, 156, 201);\n    --twindle-yellow: rgb(237, 175, 21);\n    --twindle-pink: rgb(246, 210, 200);\n    --twindle-red: rgb(231, 36, 68);\n    --overlay-color: rgba(0, 0, 0, 0.7);\n}\n\n* {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n}\n\nbody {\n    font-family: 'Poppins';\n    line-height: 1.6;\n    color: #333;\n    font-size: 1.1rem;\n}\n\nh1 {\n    line-height: 1.3;\n}\n\na {\n    color: white;\n    text-decoration: none;\n}\n\nul {\n    list-style: none;\n  }\n\n.container {\n    max-width: 1100px;\n    margin: auto;\n    overflow: hidden;\n    padding: 0 2rem;\n}\n\n.navbar {\n    font-size: 0.9rem;\n    padding-top: 0.3rem;\n    padding-bottom: 0.3rem;\n    background-color: var(--secondary-color);\n}\n\n.navbar .container {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n}\n\n.navbar .nav{\n    justify-self: flex-end;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n}\n\n.navbar .nav a {\n    padding: 0 1rem;\n}\n\n.navbar .nav a:hover {\n    color: var(--twindle-yellow);\n    background-color: white;\n    border-radius: 1rem;\n    font-size: 1rem;\n}\n\nhr {\n    border-top: 0px dashed var(--twindle-pink);\n}\n\n.section-convert {\n    margin: 3rem 0;\n}\n\n.section-convert .container {\n    display: grid;\n    grid-gap: 3rem;\n    align-items: center;\n    justify-items: center;\n}\n\n.section-title{\n    font-family: 'Indie Flower', cursive;;\n    font-size: 2rem;\n    color: var(--primary-color);\n}\n\n.section-description {\n    margin: 3rem 0;\n}\n\n.section-description .container {\n    display: grid;\n    grid-gap: 3rem;\n    align-items: center;\n    justify-items: center; \n}\n\n.section-description .container div{\n    display: contents;\n}\n\n.section-description .container p{\n    width: 70%;\n}\n\n.img-bird {\n    margin-top: -150px;\n    margin-left: 800px;\n}\n\ninput {\n    height: 30px;\n    width: 40%;\n    border: none;\n    border-bottom: 1.8px solid silver;\n    font-family: 'Poppins';\n    \n}\n\n.button{\n    display: inline-block;\n    background: var(--twindle-red);\n    color: white;\n    padding: 0.8rem 1.5rem;\n    border: none;\n    cursor: pointer;\n    border-radius: 30px;\n}\n\n.button:hover{\n    background: var(--twindle-yellow);\n}\n\n.section-footer {\n    background-color: var(--primary-color);\n    padding: 3rem 0;\n}\n\n.section-footer .container{\n    grid-template-columns: 1fr;\n    text-align: center;\n    width: 23%;\n    color: white;\n    font-size: small;\n}\n\n.section-footer .container .contact{\n    display: flex;\n    align-content: center;\n    justify-content: space-between;\n}\n\nion-icon {\n    color: var(--twindle-yellow);\n    font-size: 2rem;\n}"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage6/home.css",
    "content": ":root{\n    background-color: \t#fef6e4;\n    text-align: center;\n    line-height: 1.6;\n    font-style: italic;\n    font-size: 18px;\n    font-family: Arial, Helvetica, sans-serif;\n}\n\n#logo{\n    margin-right: auto;\n    padding-right: auto;\n    color: \t#001858;\n\n}\n\n.link{\n    height: 70px;  \n    display: flex;\n    justify-content: flex-end;\n    align-items: center;   \n    margin-left: 50px; \n    color: #001858;\n\n}\n\n.link a{\n    \ndisplay: block;\npadding: 15px 10px;\nmargin-right: 40px;\ncolor: #001858;\ntext-decoration: none;\n\n}\n\nh2{\n    font-size: 50px;\n    color: #001858;\n    font-family: 'PT Serif', serif;\n}\n\n\nh2 ,p{\n text-align: center;\n font-weight: bold;\n padding-top: 50px;\n color:#172c66;\n \n}\n\n.btn{\n    color:   #001858;\n    padding: 14px 40px;\n    border-radius: 10px; \n    border: 3px solid  \t#f582ae;\n    background-color: #fef6e4;\n}\n\n\n.link a:hover{\n    background-color: #f582ae;\n}\n\n@media screen and (max-width: 600px){\n    .link a {\n        flex: 50%;\n        \n    }\n    #pan{\n        flex: none;\n        height: 100px;\n    }\n}\n\n.one{\n    margin-top: 20%;\n    font-size: 30px;\n    height: 60px;\n    padding: 20px;\n}\n\ninput[type='text'] {\n    color:  #001858;\n    padding: 14px 40px;\n    border-radius: 10px;\n    border: 3px solid #f582ae;\n    background-color: #fef6e4;\n   \n}\n\n.footer{\n    display: inline-flex;\n}\n\n .footer .icon{\n    margin-right: 100px;\n    padding: 20px 15px 10px 20px;\n    color: \t#f582ae;\n }\n\n h3{\n    color:#001858;\n }\n"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage6/home.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>HomePage</title>\n    <link rel=\"stylesheet\" href=\"home.css\">\n    <script src=\"https://kit.fontawesome.com/72427a3a1f.js\"></script>\n    <style>\n      @import url('https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,700;1,700&display=swap');\n      </style>\n</head>\n<body>\n   <div class='link'>\n       <h3 id='logo'>logo</h3>\n       <a href=\"www.blog.com\" target=\"_blank\">BLOG</a>\n       <a href=\"#about\">ABOUT</a>\n       <a href=\"#contact\">CONTACT</a>\n       <a href=\"#faq\">FAQ</a>\n   </div>\n     <h2>TWINDLE</h2>\n        <p id='pan'>where your twitter thread gets\n          converted to pdf or epub.\n         you are a click away</p> \n           <input type='text' placeholder=\"convert thread...\">           \n          <button  type='button' class='btn'>Convert</button>\n                \n        <div class='one'>\n          <h3>JOIN US</h3> \n          <footer class='footer'>\n            <div class='icon'><i class=\"fab fa-github\"></i></div>\n            <div class='icon'><i class=\"fab fa-twitter\"></i></div>\n            <div class='icon'><i class=\"fab fa-youtube\"></i></div> \n            <div class='icon'><i class=\"fab fa-discord\"></i></div>  \n          </footer>    \n    </div>    \n</body>\n</html>"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage7/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Twindle | Welcome</title>\n    <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>\n\n    <!-- Header & Navigation Bar -->\n    <header>\n        <h2><a href=\"#\">Twindle</a></h2>\n        <nav>\n            <li><a href=\"#home\">Home</a></li>\n            <li><a href=\"#ourteam\">Our Team</a></li>\n            <li><a href=\"#features\">Features</a></li>\n            <li><a href=\"#about\">About</a></li>\n        </nav>\n    </header>\n    \n    <!-- Home Section -->\n    <section class=\"home-area\" id=\"home\">\n        <div class=\"home-img\"></div>\n        <h1>Twindle</h1>\n        <h3>Convert your tweets into ePub or pdf</h3>\n        <form action=\"\">\n            <input type=\"url\" id=\"fname\" name=\"fname\" placeholder=\"Enter twitter your link here...\">\n            <select id=\"format\" name=\"format\">\n              <option value=\"ePub\">ePub</option>\n              <option value=\"pdf\">pdf</option>\n              <option value=\"mobi\">mobi</option>\n              <option value=\"md\">doc</option>\n            </select>\n            <input type=\"submit\" value=\"Click To Convert\" id=\"submit\">\n        </form>\n    </section>\n\n    <!-- Our Team Section -->\n    <section class=\"ourteam\" id=\"ourteam\">\n        <h3 class=\"section-title\">Our Team</h3>\n        <ul class=\"team-members\">\n            <li>\n                <img src=\"../images/pic1.jpg\" alt=\"\" class=\"photo1\">\n                <h4>Name: Shikha</h4>\n                <p>Lorem ipsum dolor sit amet.</p>\n            </li>\n            <li>\n                <img src=\"../images/pic2.jpg\" alt=\"\" class=\"photo2\">\n                <h4>Name: Bruce Banner</h4>\n                <p>Lorem ipsum dolor sit amet.\n                </p>\n            </li>\n            <li>\n                <img src=\"../images/pic3.jpg\" alt=\"\" class=\"photo3\">\n                <h4>Name: Jofra</h4>\n                <p>Lorem ipsum dolor sit amet.\n                </p>\n            </li>\n            <li>\n                <img src=\"../images/pic4.jpg\" alt=\"\" class=\"photo4\">\n                <h4>Name: Peggy</h4>\n                <p>Lorem ipsum dolor sit amet.\n                </p>\n            </li>\n            <li>\n                <img src=\"../images/pic5.jpg\" alt=\"\" class=\"photo5\">\n                <h4>Name: Jenny</h4>\n                <p>Lorem ipsum dolor sit amet.\n                </p>\n            </li>\n            <li>\n                <img src=\"../images/pic6.jpg\" alt=\"\" class=\"photo6\">\n                <h4>Name: Raghav</h4>\n                <p>Lorem ipsum dolor sit amet.\n                </p>\n            </li>\n        </ul>\n    </section>\n    \n    <!-- Features Section -->\n    <section class=\"features\" id=\"features\">\n        <h3 class=\"section-title\">Features</h3>\n        <h3>How tweet is converted to ePub or pdf?</h3>\n        <p>Lorem ipsum dolor sit amet consectetur \n            adipisicing elit. Totam vitae a deleniti \n            eaque ex adipisci ab laborum optio \n            veniam sapiente. Amet accusamus illum \n            ex quidem iusto. Eos eligendi aliquid, \n            nemo ipsum illum dolor accusamus nobis \n            officia laudantium possimus nisi animi \n            sapiente esse consequatur delectus libero \n            iusto quo similique nam dicta temporibus? \n            Recusandae dolorum laborum laboriosam \n            quisquam, labore ea omnis nemo.\n        </p>\n    </section>\n    \n    <!-- About Section -->\n    <section class=\"about-area\" id=\"about\">\n        <h3 class=\"section-title\">About</h3>\n                <p>Lorem ipsum dolor, sit amet consectetur \n                    adipisicing elit. Eius molestiae \n                    temporibus distinctio ullam ratione, \n                    quis labore iure odit debitis, quo \n                    quidem qui. Nostrum autem, numquam \n                    vel neque, quisquam excepturi omnis \n                    voluptatum quaerat magnam culpa sunt \n                    fugit alias accusamus aperiam odio \n                    adipisci maiores, dolor labore nam \n                    eligendi unde cupiditate. Aut commodi \n                    nulla odit quis! Voluptatum, temporibus! \n                    Fugiat officia consequatur itaque odit.\n                </p>\n    </section>\n    \n    <!-- Footer -->\n    <footer>\n        <div id=\"footerPhoto\">\n            <a href=\"https://twitter.com/twindleco\" style=\"margin: 0px 27px -19px 8px;\">\n                <img alt=\"twitter-logo\" src=\"http://pngimg.com/uploads/twitter/twitter_PNG9.png\"\n                width=20\" height=\"20\">\n             </a>\n             <a href=\"https://github.com/twindle-co/twindle\" style=\"margin: 8px 27px -8px 8px;\">\n                <img alt=\"Github\" src=\"http://pngimg.com/uploads/github/github_PNG85.png\"\n                width=35\" height=\"35\">\n             </a>\n             <a href=\"https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA\" style=\"margin: 8px 8px -3px 8px;\">\n                <img alt=\"Qries\" src=\"http://pngimg.com/uploads/youtube/youtube_PNG2.png\"\n                width=20\" height=\"20\">\n             </a>\n        </div>\n        <p style=\"background-color: black;\">&#169 Twindle 2020 - All Rights Reserved.</p>\n    </footer>\n</body>\n</html>"
  },
  {
    "path": "misc/playground/twindleco/homepage/homepage7/style.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Baloo+Chettan+2:wght@500&display=swap');\n\n/* Global  */\n*{\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n    color: white;\n}\n\nhtml{\n    scroll-behavior: smooth;\n}\n\nbody {\n    background: rgba(2, 110, 199, 0.945);\n    font-family: 'Baloo Chettan 2', cursive;\n    font-size: 120%;\n}\n\nul, nav {\n    list-style: none;\n}\n\na{\n    text-decoration: none;\n    cursor: pointer;\n}\n\n\n/* Header & Navigation Bar*/\nheader{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n    position: sticky;\n    top: 0;\n    left: 0;\n    z-index: 5;\n    width: 100%;\n    justify-content: space-between;\n    align-items: center;\n    background: rgba(24, 119, 197, 0.945);\n    padding: 35px 100px 0;\n}\n\nheader h2{\n    text-transform: uppercase;\n}\n\nheader nav{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n}\n\nheader nav li{\n    margin: 0 20px;\n}\n\nheader nav li:first-child{\n    margin-left: 0;\n}\n\nheader nav li:last-child{\n    margin-right: 0;\n}\n\n@media(max-width: 1000px){\n    header{\n        padding: 20px 50px;\n    }\n}\n\n@media(max-width: 700px){\n    header{\n        flex-direction: column;\n    }\n    header h2{\n        margin-bottom: 15px;\n    }\n    header nav li{\n        margin: 0 7px;\n    }\n}\n\n/* Section  */\nsection{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    padding: 110px 100px;\n}\n\n.section-title{\n    text-transform: uppercase;\n    font-size: 50px;\n    margin-bottom: 5%;\n}\n\nsection p{\n    max-width: 800px;\n    text-align: center;\n    margin-bottom: 15px;\n    padding: 0 10px;\n    line-height: 1.3;\n}\n\n@media(max-width: 1000px){\n    section{\n        padding: 100px 50px;\n    }\n}\n\n@media(max-width: 600px){\n    section{\n        padding: 125px 30px;\n    }\n}\n\n\n/* Home Section */\n.home-area{\n    position: relative;\n    margin-top: -6%;\n    justify-content: center;\n    min-height: 100vh;\n    color: white;\n    text-align: center;\n}\n\n.home-area h1{\n    margin-bottom: 10px;\n    font-size: 65px;\n    color: white;\n}\n\n.home-area h3{\n    margin-bottom: 40px;\n    font-size: 25px;\n}\n\n.home-area form #fname{\n    padding: 3% 24% 3% 1%;\n    margin-left: -2%;\n    border: none;\n    border-top-left-radius: 7px;\n    border-bottom-left-radius: 7px;\n    background: white;\n    color: gray;\n    font-size: 79%;\n    font-family: 'Baloo Chettan 2', cursive;\n    font-weight: bolder;\n    \n}\n\n.home-area form #format{\n    padding: 3.2% 1%;\n    margin-left: -1%;\n    border: none;\n    border-top-right-radius: 7px;\n    border-bottom-right-radius: 7px;\n    background: gray;\n    color: white;\n    cursor: pointer;\n    font-family: 'Baloo Chettan 2', cursive;\n}\n\n.home-area form #submit{\n    border: 1px solid white;\n    border-radius: 7px;\n    margin-top: 4%;\n    background: white;\n    padding: 3% 6%;\n    color:  rgba(2, 110, 199, 0.945);\n    font-size: 80%;\n    cursor: pointer;\n    font-family: 'Baloo Chettan 2', cursive;\n    font-weight: bolder;\n    border: none;\n}\n\n@media(max-width:800px){\n    .home-area{\n        min-height: 600px\n    }\n    .home-area h1{\n        font-size: 32px;\n    }\n    .home-area h3{\n        padding: 15px 40px;\n    }\n}\n\n\n\n/* Our Team Section  */\n.team-members{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n    flex-wrap: wrap;\n    justify-content: center;\n    width: 100%;\n}\n\n.team-members li{\n    padding: 1%;\n    flex-basis: 33%;\n    text-align: center;\n    margin: 1px;\n    border-radius: 10px;\n}\n\n.team-members li:hover{\n    background: rgba(4, 83, 149, 0.95);\n}\n\n.team-members li h4{\n    font-size: 20px;\n    margin-bottom: 0px;\n}\n\n.team-members li p{\n    margin: 0;\n}\n\n.team-members li img{\n    width: 20%;\n    height: 35%;\n    border-radius: 5px;\n}\n\n@media (max-width: 1000px){\n    .team-members li{\n       flex-basis: 100%;\n       margin-bottom: 65px; \n    }\n    .team-members li:last-child{\n        margin-bottom: 0;\n    }\n    .team-members li p{\n        padding: 0;\n    }\n}\n\n\n\n/* Footer  */\nfooter{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    text-align: center;\n    color: white;\n    background: #000;\n}\n\n#footerPhoto{\n    display: -webkit-box;\n    display: -webkit-flex;\n    display: -moz-box;\n    display: -ms-flexbox;\n    display: flex;\n    align-items: center;  \n}"
  },
  {
    "path": "misc/playground/twindleco/readme.md",
    "content": "# File Naming Convention\nheader.html\nheader.css\n\nhome_banner.html\nhome_banner.css\n\nfooter.html\nfooter.css\n\nteam_details.html\nteam_details.css\n\n\n# HomePage\n\n1. Header\n1. Banner\n1. Footer\n\n# Team Page\n\n# About us\n\n\n\n\n"
  },
  {
    "path": "misc/playground/twindleco/team/team1/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Sacramento&amp;display=swap\"\n      rel=\"stylesheet\"\n    />\n    <script src=\"https://kit.fontawesome.com/d27dca2043.js\" crossorigin=\"anonymous\"></script>\n    <title>Meet Our Team</title>\n  </head>\n  <body>\n\n    <header>\n      <nav class=\"nav-wrapper\">\n        <div class=\"logo\">\n          <img src=\"./images/twindleco.png\" alt=\"Twindle Logo\">\n        </div>\n        <ul class=\"nav-bar\">\n          <li class=\"nav-link\">\n            <a href=\"#about\">About Us</a>\n          </li>\n          <li class=\"nav-link\">\n            <a href=\"#features\">Features</a>\n          </li>\n          <li class=\"nav-link\">\n            <a href=\"#team\">Team</a>\n          </li>  \n        </ul>\n      </nav>\n    </header> \n    <div class=\"main-wrapper\">\n      <div class=\"heading\">\n        <h1>Meet Our Team</h1>\n      </div>\n      <div class=\"team-wrapper\">\n        <div class=\"team\">\n          <img src=\"images/photo1.jpg\">\n          <div class=\"name\">Robert Lee</div>\n          <div class=\"role\">Systems Engineer</div>\n          <div class=\"details\">\n            <p>\n               Lorem ipsum dolor sit amet, consectetur adipisci elit, sed\n              eiusmod tempor incidunt ut labore et dolore magna aliqua.\n            </p>\n          </div>\n        </div>\n        <div class=\"team\">\n          <img src=\"images/photo2.jpg\">\n          <div class=\"name\">Mary Butler</div>\n          <div class=\"role\">Developer</div>\n          <div class=\"details\">\n            <p>\n               Lorem ipsum dolor sit amet, consectetur adipisci elit, sed\n              eiusmod tempor incidunt ut labore et dolore magna aliqua.\n            </p>\n          </div>\n        </div>\n        <div class=\"team\">\n            <img src=\"images/photo3.jpg\">\n            <div class=\"name\">Krisha Gresh</div>\n            <div class=\"role\">UX Designer</div>\n            <div class=\"details\">\n            <p>\n                 Lorem ipsum dolor sit amet, consectetur adipisci elit, sed\n                eiusmod tempor incidunt ut labore et dolore magna aliqua.\n            </p>\n          </div>\n        </div>\n        <div class=\"team\">\n          <img src=\"images/photo4.png\">\n          <div class=\"name\">Abdul Raja</div>\n          <div class=\"role\">Content Creator</div>\n          <div class=\"details\">\n            <p>\n               Lorem ipsum dolor sit amet, consectetur adipisci elit, sed\n              eiusmod tempor incidunt ut labore et dolore magna aliqua.\n            </p>\n          </div>\n        </div>\n      </div>\n    </div>\n    <footer class=\"footer-wrapper\">\n      <ul class=\"footer\">\n        <li class=\"social-icon1\">\n          <a target=\"_blank\" href=\"https://twitter.com/twindleco\">\n            <i class=\"fab fa-twitter fa-2x\" aria-hidden=\"true\"></i></a>\n        </li>\n        <li class=\"social-icon2\">\n          <a href=\"https://www.youtube.com/\">\n          <i class=\"fab fa-youtube fa-2x\" aria-hidden=\"true\"></i></a>\n        </li>\n        <li class=\"social-icon3\">\n          <a target=\"_blank\" href=\"https://github.com/twindle-co/twindle\">\n            <i class=\"fab fa-github fa-2x\" aria-hidden=\"true\"></i></a>\n        </i>\n      </ul>\n      <h1 class=\"copy-right\"> © Twindle 2020</h1>\n    </footer>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/playground/twindleco/team/team1/style.css",
    "content": "*,\n*::after,\n*::before {\n    margin: 0;\n    padding: 0;\n    box-sizing: inherit;\n}\nhtml {\nfont-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n  box-sizing: border-box;\n  scroll-behavior: smooth;\n}\n.nav-wrapper {\n  background: #085394;\n  width: 100%;\n  padding: 20px 100px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  font-size: 24px;\n  text-align: right;\n  height: 80px;\n  position: fixed;\n}\n.nav-wrapper a {\n  text-decoration: none;\n  color: #fff;\n  font-family: sans-serif;\n  font-size: 16px;\n  display: inline-block;\n  margin-right: 20px;\n}\n.logo img {\n  width: 80px;\n  height: 80px;\n} \n.nav-link a {\n  display: block;\n  padding: 1em;\n  transition: color 0.5s;\n}\n.nav-bar {\n  display: flex;\n  list-style: none;\n  align-items: center;\n}\n.nav-bar a:hover{\n  background: white;\n  color: black;\n}\n.main-wrapper {\n  text-align: center;\n  background: #eee;\n}\n.heading {\n  padding-top: 50px;\n  color: #444;\n  font-size: 15px;\n  width: 300px;\n  margin: auto;\n  line-height: 80px;\n}\n.team-wrapper {\n  max-width: 100%;\n  margin: auto;\n  padding: 50px 0;\n  display: flex;\n  flex-wrap: wrap;\n}\n.team {\n  margin: 20px;\n  padding: 25px;\n  max-width: 22%;\n  box-sizing: border-box;\n  cursor: pointer;\n  transition: 0.3s;\n}\n.team:hover {\n  background: #ddd;\n}\n.team img {\n  width: 130px;\n  height: 140px;\n  border: 1px solid #E1E0DE;\n  border-radius: 50%;\n}\n.name {\n  padding: 20px;\n  color: red;\n  font-size: 25px;\n  text-transform: uppercase;\n}\n.role {\n  font-style: italic;\n  font-size: 18px;\n  color: #726b6b;\n}\n.details {\n  margin: 20px 0;\n  text-align: justify;\n  font-weight: lighter;\n  line-height: 25px;\n  color: #4E4E4E;\n}\n.footer-wrapper {\n  padding: 30px 0 78px;\n  background-color:#fff;\n  display: flex;\n  justify-content: center;\n  flex-direction: column;\n  align-items: center;\n}\n.footer-wrapper h1 {\n  font-size: 15px;\n}\n.footer {\n  list-style: none;\n  display: flex;\n}\n.footer i {\n  padding: 12px 28px;\n}\n.social-icon1 i {\n  color: #1DA1F2;\n}\n.social-icon2 i {\n  color: #FF0000;\n}\n.social-icon3 i {\n  color: #000000;\n}\n.copy-right {\n  margin-top: 16px;\n  font-size: 25px;\n  font-weight: 500;\n}\n@media screen and (max-width: 980px) {\n  .team {\n    max-width: 45%;\n  }\n}\n@media screen and (max-width: 600px)  {\n  .team   {\n    max-width: 100%;\n  }\n}"
  },
  {
    "path": "misc/twindle-thread/.gitignore",
    "content": ".build\nbuild\nweb_modules\nnode_modules"
  },
  {
    "path": "misc/twindle-thread/.prettierrc",
    "content": "{\n  \"printWidth\": 100,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": "misc/twindle-thread/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Fred K. Schott\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "misc/twindle-thread/README.md",
    "content": "# Twindle Threads project\n\nThis project is basically a big curated list of threads best usable for Twindle for PDF conversion.\n\nThis folder contains the **frontend** part of Twindle Threads. See the `README.md` in `backend` folder for documentation about the server-side code.\n\n# Stack\n\n- ⚡ Svelte as the frontend framework. Reasons - Beginner-friendly, encapsulation, Simple Code, Reactivity. Working knowledge required for contribution (See Svelte Tutorial).\n- ❄ Snowpack for serving and building the app. Kind of like Webpack or Rollup, but with next to no configuration. Reasons - No configs, highly organized, very customizable, 🔥 Hot Reloading. Working knowledge not needed 😀\n\n# How to run\n\n> Prerequisite: This app is tightly integrated with the backend code and will break if backend server is not started. See the `README.md` file in `backend` folder for instructions\n\n## Development\n\nFor development stage(i.e. when you're contributing), Open up your terminal/Command Prompt and enter the following command:\n\n```bash\nnpm start\n```\n\nThis will start a local server and open up the page in your default browser automatically 🎇.\n\nSnowpack supports hot reloading, so any changes you do in your codebase will be reflected instantaneously in the open tab.\n\n> Note: Hot reloading support for Svelte is a bit flaky. Reload your page if you don't see instant hot reload.\n\n## Production build\n\nTo generate a production build, just run the `npm run build` command, and the production build will be generated, and a service worker precaching all the JS files will be generated too. [What are service workers?](https://medium.com/commencis/what-is-service-worker-4f8dc478f0b9)\n\nThe build generated is optimized, but not bundled. That is, there are multiple JS files rather than a single huge file, and they all work through ES imports.\n\nAdvantages: \\\n⚡ These bundles can be loaded parallelly on HTTP/2 connections, and a lot of browsers support HTTP/2, increasing performance. \\\n📦 Caching: When any changes are deployed to our live site, user's device will download only files that have changed, rather than a huge singleton bundle. \\\n🤏 These bundles downloaded separately are much smaller than a huge singleton.\n\nHowever, the support for these go back only as back as mid-2017, when Chrome first implemented ES6 Modules, and up to Firefox, which did that in early 2018.\n\n![ES6 Modules support](https://res.cloudinary.com/ireaderinokun/image/upload/v1606567471573/caniuse-embed/all/es6-module.png)\n\nFor now, we're sticking only to ES Modules. However, we may introduce Differential loading in future, where newer browsers get the separate ES Modules, and old ones get the single bundled file.\n\n### Destination\n\nThese bundles are generated in the `build` folder (not in the repo).\n\n## Documentation\n\n### Media Queries\n\nWe'll follow the breakpoint:\n\n- `960px` for tablets\n- `600px` for mobile screens\n\nThis value will need to be hard coded into our code, so it's better to keep as low of these breakpoints as possible for better maintainability.\n"
  },
  {
    "path": "misc/twindle-thread/backend/README.md",
    "content": "This is the server-side code of Twindle-threads project.\n\n# Stack\n\n- 🐕‍🦺 Express Server\n- 🗳 MySQL for storing the data\n- 🐱‍👤 [mysql2](https://www.npmjs.com/package/mysql2) for interfacing with the MySQL Database\n\n# Installing dependencies\n\nOpen up your terminal/Command Prompt, `cd` into this directory, and run the following command:\n\n```bash\nnpm install\n```\n\n# Installing MySQL\n\nYou need to have MySQL Database and Server installed on your device. The guides to download these are below:\n\nWindows: https://www.liquidweb.com/kb/install-mysql-windows/ \\\nMacOS: https://flaviocopes.com/mysql-how-to-install/ \\\nUbuntu/Debian: https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04 \\\nFedora Linux: https://computingforgeeks.com/how-to-install-mysql-8-on-fedora/ \\\n\nAfter completing the above steps, run the below command\n\n```bash\nnpm start\n```\n\nThis will start up the express server. It uses [nodemon](https://www.npmjs.com/package/nodemon), so the server automatically refreshes on any code changes.\n\n## Troubleshooting\n\nYou might get this error when testing out the code: `Client does not support authentication protocol requested by server; consider upgrading MySQL client`\n\nIn that case, the solution is documented here: https://stackoverflow.com/questions/50093144/mysql-8-0-client-does-not-support-authentication-protocol-requested-by-server\n"
  },
  {
    "path": "misc/twindle-thread/backend/common.d.ts",
    "content": "declare namespace NodeJS {\n  export interface ProcessEnv {\n    MYSQL_HOST: string;\n    MYSQL_USER: string;\n    MYSQL_PASSWORD: string;\n\n    TWITTER_AUTH_TOKEN: string;\n  }\n}\n"
  },
  {
    "path": "misc/twindle-thread/backend/index.js",
    "content": "// @ts-check\n\nrequire('dotenv').config();\nconst express = require('express');\nconst bodyParser = require('body-parser');\nconst cors = require('cors');\n\nconst { addThread } = require('./src/add-thread');\nconst { getThreadData } = require('./src/get-thread-data');\nconst { getThreadsLists } = require('./src/get-threads-list');\n\nconst port = 3800;\n\nconst app = express();\n\napp.use(bodyParser.urlencoded({ extended: true }));\napp.use(bodyParser.json());\n\napp.use(\n  cors({\n    origin: true,\n  })\n);\n\napp.listen(port, () => {\n  console.log(`API listening at port ${port}`);\n});\n\napp.get('/threads/:id', getThreadData);\napp.get('/threads/', getThreadsLists);\n\napp.post('/threads/', addThread);\n"
  },
  {
    "path": "misc/twindle-thread/backend/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"resolveJsonModule\": true,\n  }\n}"
  },
  {
    "path": "misc/twindle-thread/backend/package.json",
    "content": "{\n  \"name\": \"twindle-thread\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"start\": \"nodemon index.js\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"body-parser\": \"^1.19.0\",\n    \"cors\": \"^2.8.5\",\n    \"dotenv\": \"^8.2.0\",\n    \"express\": \"^4.17.1\",\n    \"mysql2\": \"^2.2.5\",\n    \"node-fetch\": \"^2.6.1\"\n  },\n  \"devDependencies\": {\n    \"@types/express\": \"^4.17.9\",\n    \"@types/mysql2\": \"github:types/mysql2\",\n    \"nodemon\": \"2.0.6\"\n  }\n}\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/add-thread.js",
    "content": "// @ts-check\n\nconst { getTweetById } = require('./twitter');\nconst { dbInstance } = require('./helpers/connection');\nconst { calculateTwitterScore } = require('./helpers/score');\n\n/**\n *\n * @param {import('express').Request} req\n * @param {import('express').Response} res\n * @returns {Promise<void>}\n */\nasync function addThread(req, res) {\n  const { threadID } = req.body;\n\n  // FUTURE: Do some checking here\n  // Get basic info about the tweet\n  const { data: twitterResponseJSON } = await getTweetById(\n    threadID,\n    process.env.TWITTER_AUTH_TOKEN\n  );\n\n  if (twitterResponseJSON.errors) {\n    return void Response('tweet-does-not-exists', '', res);\n  }\n\n  const { text, public_metrics } = twitterResponseJSON.data[0];\n  const twitterUser = twitterResponseJSON.includes.users[0];\n\n  // Get the basic data\n  const basicData = {\n    text,\n    conversation_id: threadID,\n    likes: +public_metrics.like_count,\n    retweets: +public_metrics.retweet_count,\n    repliesCount: +public_metrics.reply_count,\n  };\n\n  const userData = {\n    user_id: twitterUser.id,\n    handle: twitterUser.username,\n    name: twitterUser.name,\n    profile_photo: twitterUser.profile_image_url.replace('_normal', '_reasonably_small'),\n    verified: twitterUser.verified + '',\n  };\n\n  const { pool } = await dbInstance();\n\n  try {\n    // First check if this ID already in DB\n\n    /** @type {[rows: import('mysql2').RowDataPacket[]]} */\n    // @ts-ignore\n    const [rows] = await pool.execute('SELECT * FROM twitter_threads WHERE conversation_id=?', [\n      threadID,\n    ]);\n\n    if (rows.length) {\n      return void Response('thread-id-already-in-database', '', res);\n    }\n\n    // Calculate the score\n    const score = calculateTwitterScore(\n      basicData.likes,\n      basicData.retweets,\n      basicData.repliesCount,\n      new Date() + '',\n      new Date() + ''\n    );\n\n    // Do the checks\n    /** @type {[rows: import('mysql2').RowDataPacket[]]} */\n    // @ts-ignore\n    const [checkUser__Rows] = await pool.execute('SELECT * FROM twitter_users WHERE user_id=?', [\n      userData.user_id,\n    ]);\n\n    /**\n     * NOTE: This part below looks super verbose and seems like can be deduplicated. That is the plan.\n     * But not now. When Twindle Threads becomes stable, we'll be coming for this code.\n     * @todo Deduplicate below part\n     */\n    if (checkUser__Rows.length) {\n      await pool.execute(\n        'UPDATE twitter_users SET handle=?, name=?, profile_photo=?, verified=? WHERE user_id=?',\n        [\n          userData.handle,\n          userData.name,\n          userData.profile_photo,\n          userData.verified,\n          userData.user_id,\n        ]\n      );\n    } else {\n      await pool.execute(\n        'INSERT INTO twitter_users (user_id, handle, name, profile_photo, verified) VALUES (?, ?, ?, ?, ?)',\n        [\n          userData.user_id,\n          userData.handle,\n          userData.name,\n          userData.profile_photo,\n          userData.verified,\n        ]\n      );\n    }\n\n    // Do the thing\n    await pool.execute(\n      'INSERT INTO twitter_threads (conversation_id, user_id, text, likes, retweets, replies_count, score) VALUES (?, ?, ?, ?, ?, ?, ?)',\n      [\n        basicData.conversation_id + '',\n        userData.user_id + '',\n        basicData.text,\n        basicData.likes,\n        basicData.retweets,\n        basicData.repliesCount,\n        score,\n      ]\n    );\n\n    return void Response('', 'successful', res);\n  } catch (e) {\n    console.error(e);\n  } finally {\n    await pool.end();\n  }\n\n  return void Response('unable-to-add-thread', '', res);\n}\n\n/**\n * @param {string} error\n * @param {string} message\n * @param {import('express').Response} res\n */\nfunction Response(error, message, res) {\n  return res.json({ error, message });\n}\n\nmodule.exports = { addThread };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/error/api.js",
    "content": "const { TwitterError } = require(\"./base\");\n\nclass NetworkRequestError extends TwitterError {\n  constructor() {\n    super(\"request-failed\", \"Request failed. Check your network and try again\");\n  }\n}\n\nclass TokenNotProvidedError extends TwitterError {\n  constructor() {\n    super(\"token-not-passed\", \"Please provide the token\");\n  }\n}\n\nclass InvalidTokenError extends TwitterError {\n  constructor() {\n    super(\n      \"invalid-token\",\n      \"Inavalid Bearer token. Please see if your bearer token in the .env file is valid\"\n    );\n  }\n}\n\nclass BadTwitterRequestError extends TwitterError {\n  constructor() {\n    super(\n      \"bad-request\",\n      \"Invalid request parameters. This error happens most likely because of invalid tweet id\"\n    );\n  }\n}\n\nclass TwitterServiceError extends TwitterError {\n  constructor() {\n    super(\n      \"twitter-service-error\",\n      \"Try later. Twitter is facing issues with too many requests or the access token limits were reached.\"\n    );\n  }\n}\n\nclass TweetIDNotProvidedError extends TwitterError {\n  constructor() {\n    super(\"tweet-id-not-passed\", \"Please provide the tweet id\");\n  }\n}\n\nclass TweetDoesNotExist extends TwitterError {\n  constructor() {\n    super(\"tweet-does-not-exists\", \"Tweet does not exists. PLease check the tweet ID you entered\");\n  }\n}\n\nclass UserScreenNameInvalid extends TwitterError {\n  constructor() {\n    super(\"user-screen-name-invalid\", \"Invalid user screen user name\");\n  }\n}\n\nmodule.exports = {\n  NetworkRequestError,\n  TokenNotProvidedError,\n  TweetIDNotProvidedError,\n  InvalidTokenError,\n  BadTwitterRequestError,\n  TwitterServiceError,\n  TweetDoesNotExist,\n  UserScreenNameInvalid,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/error/base.js",
    "content": "const { UserError } = require(\"../helpers/error\");\n\nclass TwitterError extends UserError {\n  constructor(name, message) {\n    super(\"twitter-api:\" + name, message);\n  }\n}\n\nmodule.exports = {\n  TwitterError,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/error/index.js",
    "content": "const ApiErrors = require(\"./api\");\nconst ValidationErrors = require(\"./validation\");\n\nmodule.exports = {\n  ApiErrors,\n  ValidationErrors,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/error/validation.js",
    "content": "const { TwitterError } = require(\"./base\");\n\nclass TweetDeletedError extends TwitterError {\n  constructor() {\n    super(\"tweet-deleted\", \"Cannot fetch details of this tweet.\");\n  }\n}\n\nclass TweetOlderThan7DaysError extends TwitterError {\n  constructor() {\n    super(\"tweet-older-than-7-days\", \"The tweet must not be older than 7 days.\");\n  }\n}\n\nclass TweetNotFirstOfThreadError extends TwitterError {\n  constructor() {\n    super(\"tweet-not-first-of-thread\", \"The provided tweet is not the first of the thread.\");\n  }\n}\n\nmodule.exports = {\n  TweetDeletedError,\n  TweetOlderThan7DaysError,\n  TweetNotFirstOfThreadError,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/get-thread-data.js",
    "content": "const { dbInstance } = require('./helpers/connection');\n\n/**\n * Get the data related to the specific thread ID passed\n * @param {import('express').Request} req\n * @param {import('express').Response} res\n * @returns {Promise<void>}\n */\nasync function getThreadData(req, res) {\n  const { id } = req.params;\n\n  const { pool } = await dbInstance();\n  try {\n    /** @type {[rows: import('mysql2').RowDataPacket[]]} */\n    // @ts-ignore\n    const [rows] = await pool.execute(\n      `SELECT tt.*, tu.* FROM twitter_threads tt \n        INNER JOIN twitter_users tu \n        ON tt.user_id = tu.user_id \n        WHERE id=?\n        GROUP BY tt.user_id\n        `,\n      [id]\n    );\n\n    if (!rows.length) {\n      // Empty result\n      return void Response('thread-not-in-database', '', {}, res);\n    }\n\n    const finalData = rows.map(\n      ({\n        conversation_id,\n        date_created,\n        last_updated,\n        id,\n        likes,\n        replies_count,\n        retweets,\n        score,\n        text,\n        user_id,\n        verified,\n        handle,\n        name,\n        profile_photo,\n      }) => ({\n        conversation_id,\n        date_created,\n        last_updated,\n        id,\n        likes,\n        replies_count,\n        retweets,\n        score,\n        text,\n        user: {\n          user_id,\n          verified,\n          handle,\n          name,\n          profile_photo,\n        },\n      })\n    );\n\n    return void Response('', 'successful', finalData[0], res);\n  } catch (e) {\n    console.log(e);\n  } finally {\n    await pool.end();\n  }\n\n  return void Response('unable-to-get-thread-data', '', {}, res);\n}\n\n/**\n * @param {string} error\n * @param {string} message\n * @param {Object} data\n * @param {import('express').Response} res\n */\nasync function Response(error, message, data, res) {\n  return res.json({ error, message, data });\n}\n\nmodule.exports = { getThreadData };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/get-threads-list.js",
    "content": "// @ts-check\n\nconst { dbInstance } = require('./helpers/connection');\n\n/**\n * @typedef {{page: number | string; limit: number | string; by: 'popular:desc' | 'date:desc' | 'date:asc'}} ParamData\n */\n\n/**\n * Get the data related to the specific thread ID passed\n * @param {import('express').Request} req\n * @param {import('express').Response} res\n * @returns {Promise<void>}\n */\nasync function getThreadsLists(req, res) {\n  /** @type {ParamData} */\n  // @ts-ignore\n  const { page = 1, limit = 20, by = 'popular:desc' } = req.query;\n\n  /**\n   * --------------- READ 👇 ----------------\n   * Algorithm: `page` and `limit` are correlated. \\\n   * i.e, if page = 2, and limit=20, it's gonna go over and get the items from number 21-40\n   */\n\n  // First verify if `by` field has valid values\n  const [prefix, suffix] = by\n    .trim()\n    .split(':')\n    .map((item) => item.trim().toLowerCase());\n\n  if (!['date', 'popular'].includes(prefix) || !['asc', 'desc'].includes(suffix)) {\n    // Invalid format\n    return void Response(\n      'invalid-value-of-by',\n      'Value of `by` parameter can only be popular:desc, popular.asc, date:desc, date:asc',\n      [],\n      res\n    );\n  }\n\n  // Check page\n  if (isNaN(+page) || !Number.isInteger(+page) || +page <= 0) {\n    return void Response(\n      'invalid-value-of-page',\n      'page should be a integer greater than 0',\n      [],\n      res\n    );\n  }\n\n  // Check `limit`\n  if (isNaN(+limit) || !Number.isInteger(+limit) || +limit <= 0) {\n    return void Response('invalid-value-of-limit', 'limit should be a number', [], res);\n  }\n\n  /** Columns and order, based on the query */\n  const tokens = {\n    column: prefix === 'popular' ? 'score' : 'last_updated',\n    order: suffix.toUpperCase(),\n  };\n\n  const { pool } = await dbInstance();\n\n  try {\n    // Get items\n    /** @type {[rows: import('mysql2').RowDataPacket[]]} */\n    // @ts-ignore\n    const [rows] = await pool.execute(\n      `SELECT tt.*, tu.* FROM twitter_threads tt \n        INNER JOIN twitter_users tu \n        ON tt.user_id = tu.user_id \n        GROUP BY tt.user_id\n        ORDER BY tt.${tokens.column} ${tokens.order} LIMIT ${+limit} OFFSET ${+limit * (+page - 1)}\n      `\n    );\n\n    const finalData = rows.map(\n      ({\n        conversation_id,\n        date_created,\n        last_updated,\n        id,\n        likes,\n        replies_count,\n        retweets,\n        score,\n        text,\n        user_id,\n        verified,\n        handle,\n        name,\n        profile_photo,\n      }) => ({\n        conversation_id,\n        date_created,\n        last_updated,\n        id,\n        likes,\n        replies_count,\n        retweets,\n        score,\n        text,\n        user: {\n          user_id,\n          verified,\n          handle,\n          name,\n          profile_photo,\n        },\n      })\n    );\n\n    return void Response('', 'successful', finalData, res);\n  } catch (e) {\n    console.log(e);\n  } finally {\n    await pool.end();\n  }\n\n  return void Response('unable-to-get-threads-list', '', [], res);\n}\n\n/**\n * @param {string} error\n * @param {string} message\n * @param {any[]} data\n * @param {import('express').Response} res\n */\nfunction Response(error, message, data, res) {\n  return res.json({ error, message, data });\n}\n\nmodule.exports = { getThreadsLists };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/helpers/connection.js",
    "content": "// @ts-check\nconst { createPool } = require('mysql2');\nconst fs = require('fs').promises;\n\n/**\n * Initializes connection and ensures minimum required structures exist\n */\nasync function dbInstance() {\n  const conn = createPool({\n    host: process.env.MYSQL_HOST,\n    user: process.env.MYSQL_USER,\n    password: process.env.MYSQL_PASSWORD,\n    waitForConnections: true,\n    connectionLimit: 50,\n    queueLimit: 0,\n    multipleStatements: true,\n  }).promise();\n\n  const commands = await fs.readFile(__dirname + '/setup-db.sql', 'utf-8');\n\n  await conn.query(commands);\n\n  return { pool: conn };\n}\n\nmodule.exports = { dbInstance };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/helpers/error.js",
    "content": "// @ts-check\n\nclass UserError extends Error {\n  /**\n   *\n   * @param {string} message Message to show to the user\n   * @param {string} name Code of the error\n   */\n  constructor(name, message) {\n    super(message);\n\n    this.name = name;\n  }\n}\n\nmodule.exports = { UserError };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/helpers/score.js",
    "content": "// @ts-check\n\n/**\n * Source: https://jkchu.com/2016/02/17/designing-and-implementing-a-ranking-algorithm/\n * @param {number} likes\n * @param {number} retweets\n * @param {number} repliesCount\n * @param {string} dateCreatedStr\n * @param {string} lastUpdatedStr\n */\nfunction calculateTwitterScore(likes, retweets, repliesCount, dateCreatedStr, lastUpdatedStr) {\n  const timeCreatedDiff = +new Date() - +new Date(dateCreatedStr);\n  const lastUpdateTimeDiff = +new Date() - +new Date(lastUpdatedStr);\n\n  const numerator = likes + 0.5 * repliesCount + 0.2 * retweets;\n\n  const denominator = 1 + timeCreatedDiff ** 1.8 - (timeCreatedDiff - lastUpdateTimeDiff) ** 1.2;\n\n  return ((numerator / denominator) * 1e4).toFixed(10);\n}\n\nmodule.exports = { calculateTwitterScore };\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/helpers/setup-db.sql",
    "content": "CREATE DATABASE IF NOT EXISTS twindle_threads;\nUSE twindle_threads;\nCREATE TABLE IF NOT EXISTS twitter_users (\n  user_id VARCHAR(20) NOT NULL PRIMARY KEY,\n  handle VARCHAR(30) NOT NULL,\n  name VARCHAR(40) NOT NULL,\n  profile_photo VARCHAR(200) NOT NULL,\n  verified ENUM('true', 'false') NOT NULL\n);\nCREATE TABLE IF NOT EXISTS twitter_threads (\n  id INT NOT NULL AUTO_INCREMENT,\n  conversation_id VARCHAR(45) NOT NULL,\n  user_id VARCHAR(20) NOT NULL,\n  text VARCHAR(300) NOT NULL,\n  likes INT(20) NULL DEFAULT 0,\n  retweets INT(20) NULL DEFAULT 0,\n  replies_count INT(20) NULL DEFAULT 0,\n  score FLOAT(40) NULL DEFAULT 0,\n  date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n  last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (id),\n  FOREIGN KEY (user_id) REFERENCES twitter_users(user_id),\n  UNIQUE INDEX conversation_id_UNIQUE (conversation_id ASC) VISIBLE\n);"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/constants.js",
    "content": "const TWEET_FIELDS =\n  \"&tweet.fields=attachments,author_id,context_annotations,conversation_id,created_at,\" +\n  \"entities,geo,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,\" +\n  \"source,withheld\";\nconst EXPANSIONS = \"&expansions=author_id,attachments.media_keys\";\nconst USER_FIELDS =\n  \"&user.fields=created_at,description,entities,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified,withheld\";\nconst MEDIA_FIELDS =\n  \"&media.fields=duration_ms,height,preview_image_url,url,media_key,public_metrics,width\";\nconst PLACE_FIELDS = \"&place.fields=contained_within,country,country_code,geo,name,place_type\";\nconst POLL_FIELDS = \"&poll.fields=duration_minutes,end_datetime,voting_status\";\nconst MAX_RESULTS = \"&max_results=100\";\n\nconst getCommonFields = () =>\n  `${TWEET_FIELDS}${EXPANSIONS}${USER_FIELDS}${MEDIA_FIELDS}${PLACE_FIELDS}${POLL_FIELDS}`;\n\nmodule.exports = {\n  getCommonFields,\n  MAX_RESULTS,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/helpers/fetch.js",
    "content": "// @ts-check\nconst nodeFetch = require(\"node-fetch\").default;\nconst { ApiErrors } = require(\"../../error\");\n\n/**\n * @param {string} url Twitter endpoint to send request to\n * @param {string} token Bearer token. Should be provided from the .env file\n */\nconst fetch = async (url, token) => {\n  if (!token) throw new ApiErrors.TokenNotProvidedError();\n\n  const response = await nodeFetch(url, {\n    method: \"GET\",\n    headers: { Authorization: `Bearer ${token}` },\n    redirect: \"follow\",\n  });\n\n  if ([401, 403].includes(response.status)) throw new ApiErrors.InvalidTokenError();\n\n  if ([400].includes(response.status)) throw new ApiErrors.BadTwitterRequestError();\n\n  if ([429, 500, 503].includes(response.status)) throw new ApiErrors.TwitterServiceError();\n\n  if (response.status !== 200) throw new ApiErrors.NetworkRequestError();\n\n  /** @type {TwitterConversationResponse} */\n  const data = await response.json();\n\n  return {\n    status: \"ok\",\n    data,\n  };\n};\n\nmodule.exports = {\n  fetch,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/index.js",
    "content": "const { getTweetById } = require('./twitter-endpoints/tweets');\n\nmodule.exports = {\n  getTweetById,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/twitter-endpoints/tweets.js",
    "content": "const { fetch } = require(\"../helpers/fetch\");\nconst { getCommonFields } = require(\"../constants\");\nconst { ApiErrors } = require(\"../../error\");\n\nconst BASE_ENDPOINT = \"https://api.twitter.com/2/tweets?ids=\";\n\n/**\n * @param {string} id tweet ID\n * @param {string} token Bearer token\n */\nconst getTweetById = (id, token) => {\n  if (!id) throw new ApiErrors.TweetIDNotProvidedError();\n\n  const COMMON_FIELDS = getCommonFields();\n  const url = `${BASE_ENDPOINT}${id}${COMMON_FIELDS}`;\n\n  return fetch(url, token);\n};\n\nmodule.exports = {\n  getTweetById,\n};\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/types.d.ts",
    "content": "export interface Mention {\n  start: number;\n  end: number;\n  username: string;\n}\n\nexport interface Hashtag {\n  start: number;\n  end: number;\n  tag: string;\n}\n\nexport interface EntityUrl {\n  start: number;\n  end: number;\n  /** format: `https://t.co/[REST]` */\n  url: string;\n  expanded_url: string;\n  /** The possibly truncated URL */\n  display_url: string;\n  status: number;\n  title: string;\n  description: string;\n  unwound_url: string;\n  images?: {\n    url: string;\n    height: number;\n    width: number;\n  }[];\n}\n\nexport interface Attachments {\n  poll_id?: string[];\n  media_keys?: string[];\n}\n\nexport interface User {\n  username: string;\n  description: string;\n  profile_image_url: string;\n  verified: boolean;\n  location: string;\n  created_at: string;\n  name: string;\n  protected: boolean;\n  id: string;\n  url?: string;\n  public_metrics: {\n    followers_count: number;\n    following_count: number;\n    tweet_count: number;\n    listed_count: number;\n  };\n  entities?: {\n    url?: {\n      urls: EntityUrl[];\n    };\n    description?: {\n      urls?: EntityUrl[];\n      mentions?: Mention[];\n      hashtags?: Hashtag[];\n    };\n  };\n}\n\nexport interface ConversationResponseData {\n  conversation_id: string;\n  id: string;\n  text: string;\n  author_id: string;\n  created_at: string;\n  in_reply_to_user_id: string;\n  public_metrics: {\n    retweet_count: number;\n    reply_count: number;\n    like_count: number;\n    quote_count: number;\n  };\n  entities?: {\n    mentions?: Mention[];\n    hashtags?: Hashtag[];\n    urls?: EntityUrl[];\n  };\n  referenced_tweets?: {\n    type: \"retweeted\" | \"quoted\" | \"replied_to\";\n    id: string;\n  }[];\n  attachments?: Attachments;\n  /** Not returned by twitter API. Added by the code */\n  includes?: ConversationIncludes;\n\n  customMedia?: CustomMedia;\n\n  linkWithImage?: Partial<LinkWithImage>;\n\n  embeddedTweet?: ConversationResponseData;\n  embeddedTweetUser?: User;\n}\n\nexport interface IncludesMedia {\n  height: number;\n  width: number;\n  type: \"photo\" | \"video\" | \"animated_gif\";\n  url: string;\n  preview_image_url: string;\n  media_key: string;\n}\n\nexport interface ConversationIncludes {\n  media: IncludesMedia[];\n  users: User[];\n}\n\n/**\n * Types from response after cleanup\n */\nexport interface ConversationResponse {\n  data: ConversationResponseData[];\n  includes: ConversationIncludes;\n  meta: {\n    newest_id: string;\n    oldest_id: string;\n    result_count: number;\n  };\n  errors?: any;\n}\n\nexport interface CustomMedia {\n  [media: \"photo\" | \"video\" | \"animated_gif\"]: {\n    height: number;\n    width: number;\n    preview_img_url: string;\n    /** Format: pic.twitter.com/*. No HTTPS in the link */\n    link: string;\n  }[];\n}\n\nexport interface LinkWithImage {\n  expanded_url: string;\n  images: {\n    url: string;\n    width: number;\n    height: number;\n  }[];\n  title: string;\n  description: string;\n  domain: string;\n}\n\nexport interface CustomTweetData {\n  id: string;\n  created_at: string;\n  tweet: string;\n  customMedia?: CustomMedia;\n\n  linkWithImage?: Partial<LinkWithImage>;\n\n  embeddedTweet?: ConversationResponseData;\n  embeddedTweetUser?: User;\n}\n\nexport interface CustomTweets {\n  common: {\n    id: string;\n    created_at: string;\n    count: number;\n    user: Partial<User>;\n  };\n  data: CustomTweetData[];\n}\n"
  },
  {
    "path": "misc/twindle-thread/backend/src/twitter/types.js",
    "content": "/**\n * @typedef {import(\"./types\").ConversationResponse} TwitterConversationResponse\n * @typedef {import(\"./types\").ConversationResponseData} TwitterConversationData\n * @typedef {import(\"./types\").CustomTweets} CustomTweetsObject\n */\n"
  },
  {
    "path": "misc/twindle-thread/jsconfig.json",
    "content": ""
  },
  {
    "path": "misc/twindle-thread/package.json",
    "content": "{\n  \"scripts\": {\n    \"start\": \"snowpack dev\",\n    \"build\": \"snowpack build\",\n    \"post-build\": \"workbox generateSW workbox-config.js\"\n  },\n  \"dependencies\": {\n    \"@mdi/js\": \"^5.8.55\",\n    \"fetch-retry\": \"^4.0.1\",\n    \"lazysizes\": \"^5.2.2\",\n    \"svelte\": \"^3.31.0\",\n    \"svelte-routing\": \"^1.4.2\"\n  },\n  \"devDependencies\": {\n    \"@snowpack/plugin-dotenv\": \"^2.0.5\",\n    \"@snowpack/plugin-optimize\": \"^0.2.10\",\n    \"@snowpack/plugin-svelte\": \"^3.3.1\",\n    \"snowpack\": \"^2.18.2\",\n    \"workbox-cli\": \"^6.0.2\"\n  }\n}\n"
  },
  {
    "path": "misc/twindle-thread/public/css/global.css",
    "content": "@import './theme.css';\n\nhtml {\n  font-size: 100%;\n\n  height: 100%;\n}\n\nbody {\n  margin: 0;\n  padding: 0;\n\n  min-height: 100%;\n\n  display: flex;\n\n  font-family: var(--app-main-font-family);\n\n  background-color: var(--app-color-shell);\n}\n\n* {\n  transition: background-color 150ms var(--app-easing), background 150ms var(--app-easing),\n    box-shadow 150ms var(--app-easing);\n\n  box-sizing: border-box;\n}\n\n*:focus {\n  outline: none !important;\n}\n\n*:focus-visible {\n  outline: none !important;\n  -webkit-tap-highlight-color: transparent;\n\n  box-shadow: 0 0 0 1.5px var(--app-color-dark);\n}\n\n::selection {\n  background-color: rgba(var(--app-color-primary-rgb), 0.2);\n}\n"
  },
  {
    "path": "misc/twindle-thread/public/css/theme.css",
    "content": "body {\n  /* heading font */\n  --app-heading-font-family: 'Comfortaa', 'Helevtica', 'Arial' sans-serif;\n\n  /* Main font */\n  --app-main-font-family: -apple-system, BlinkMacSystemFont, Roboto, 'Segoe UI', Roboto, Oxygen,\n    Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n\n  /* Border radii */\n  --app-border-radius-small: 8px;\n\n  --app-easing: cubic-bezier(0, 0, 0.2, 1);\n}\n\nbody.light {\n  --app-color-primary: #cc444b;\n  --app-color-primary-rgb: 204, 68, 75;\n  --app-color-primary-tint: #db7c81;\n  --app-color-primary-shade: #8f3035;\n  --app-color-primary-contrast: #fff;\n\n  --app-color-light: #f4f5f8;\n  --app-color-light-rgb: 244, 244, 244;\n  --app-color-light-contrast: #000000;\n\n  --app-color-dark: #222428;\n  --app-color-dark-rgb: 34, 34, 34;\n  --app-color-dark-contrast: #ffffff;\n\n  /* The background color of shell */\n  --app-color-shell: white;\n}\n\nbody.dark {\n  --app-color-primary: #cc444b;\n  --app-color-primary-rgb: 204, 68, 75;\n  --app-color-primary-tint: #db7c81;\n  --app-color-primary-shade: #8f3035;\n  --app-color-primary-contrast: #fff;\n\n  --app-color-dark: #f4f5f8;\n  --app-color-dark-rgb: 244, 244, 244;\n  --app-color-dark-contrast: #000000;\n\n  --app-color-light: #222428;\n  --app-color-light-rgb: 34, 34, 34;\n  --app-color-light-contrast: #ffffff;\n\n  /* The background color of shell */\n  --app-color-shell: #222428;\n}\n"
  },
  {
    "path": "misc/twindle-thread/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"description\" content=\"Web site created using create-snowpack-app\" />\n    <title>Snowpack App</title>\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" />\n\n    <link rel=\"stylesheet\" href=\"./css/global.css\" />\n  </head>\n  <body class=\"light\">\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <script type=\"module\" src=\"/_dist_/index.js\"></script>\n    <script>\n      if ('%MODE%' === 'nothing') {\n        if ('serviceWorker' in navigator) {\n          window.addEventListener('load', function () {\n            navigator.serviceWorker.register('/sw.js');\n          });\n        }\n      }\n    </script>\n\n    <link\n      href=\"https://fonts.googleapis.com/css2?family=Comfortaa:wght@300;400;500;600&family=Roboto:wght@300;400;500;600&display=swap\"\n      rel=\"stylesheet\"\n    />\n  </body>\n</html>\n"
  },
  {
    "path": "misc/twindle-thread/public/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "misc/twindle-thread/snowpack.config.js",
    "content": "/** @type {import(\"snowpack\").SnowpackUserConfig } */\nmodule.exports = {\n  mount: {\n    public: '/',\n    src: '/_dist_',\n  },\n  plugins: [\n    '@snowpack/plugin-svelte',\n    '@snowpack/plugin-dotenv',\n    [\n      '@snowpack/plugin-optimize',\n      {\n        target: 'es2015',\n        preloadModules: true,\n      },\n    ],\n  ],\n  install: [\n    /* ... */\n  ],\n  installOptions: {\n    /* ... */\n  },\n  devOptions: {\n    /* ... */\n  },\n  buildOptions: {\n    /* ... */\n  },\n  proxy: {\n    /* ... */\n  },\n  alias: {\n    /* ... */\n  },\n};\n"
  },
  {
    "path": "misc/twindle-thread/src/App.svelte",
    "content": "<script>\n  // @ts-check\n  import { Router, Route } from 'svelte-routing';\n  import Feed from './pages/Feed.svelte';\n</script>\n\n<style>\n  /** This is the direct child of `body`*/\n  main {\n    /** Always keep this to make it stretch cross-axis\n     * https://stackoverflow.com/questions/29467660/how-to-stretch-children-to-fill-cross-axis\n     */\n    flex: 1;\n\n    display: flex;\n    justify-content: center;\n  }\n</style>\n\n<main>\n  <Router>\n    <Route path=\"/\" component={Feed} />\n  </Router>\n</main>\n"
  },
  {
    "path": "misc/twindle-thread/src/components/Avatar.svelte",
    "content": "<script>\n  import { onMount } from 'svelte';\n\n  /** @type {string} */\n  export let src;\n\n  /** @type {boolean} */\n  export let lazy = true;\n\n  /** @type {number} */\n  export let size = 48;\n\n  onMount(() => {\n    if (lazy) {\n      import('lazysizes');\n    }\n  });\n</script>\n\n<style>\n  * {\n    user-select: none;\n  }\n\n  div {\n    --placeholder-bgcolor: rgba(var(--app-color-dark-rgb), 0.15);\n    --avatar-color-primary: var(--app-color-primary);\n\n    --easing: var(--app-easing, cubic-bezier(0, 0, 0.2, 1));\n\n    display: inline-block;\n\n    /** --size here comes from inline styling in style attribute of element itself, not defined here\n     * See the root element of this component\n     */\n    width: var(--size);\n    height: var(--size);\n\n    background-color: var(--placeholder-bgcolor);\n\n    border-radius: 50%;\n    /* box-shadow: 0 0 0 4px var(--avatar-color-primary); */\n  }\n\n  img {\n    width: 100%;\n\n    object-fit: cover;\n\n    border-radius: inherit;\n\n    transition: opacity 150ms var(--easing);\n  }\n\n  img.lazyload {\n    opacity: 0;\n  }\n\n  div :global(img.lazyloaded) {\n    opacity: 1;\n  }\n</style>\n\n<div style=\"--size: {size}px\">\n  <img class:lazyload={lazy} alt=\"User Avatar\" src={!lazy && src} data-src={lazy && src} />\n</div>\n"
  },
  {
    "path": "misc/twindle-thread/src/components/Button.svelte",
    "content": "<script>\n  // @ts-check\n\n  // PROPS\n  /** @type {'text' | 'contained' | 'outlined'} */\n  export let variant = 'text';\n\n  /** @type {'primary' | 'light' | 'default'} */\n  export let color = 'default';\n\n  /** @type {boolean} */\n  export let disabled = false;\n\n  /** @type {string} */\n  let classes;\n\n  $: {\n    // basic processing\n    const externalClasses = $$props.class;\n    delete $$props.class;\n\n    classes = `button-variant-${variant} button-color-${color} ${externalClasses || ''}`;\n  }\n</script>\n\n<style>\n  button {\n    /* Private variables. Shouldn't be set from outside */\n    --bgcolor: none;\n    --color: var(--button-color-dark);\n    --border-color: transparent;\n    --focus-ring-color: var(--button-color-dark);\n\n    /* Redefining variables here so they can defined for this specific component */\n    --button-main-font-family: var(--app-main-font-family);\n    --button-border-radius: var(--app-border-radius-small);\n\n    --button-color-light: var(--app-color-light);\n    --button-color-light-contrast: var(--app-color-light-contrast);\n    --button-color-light-rgb: var(--app-color-light-rgb);\n\n    --button-color-dark: var(--app-color-dark);\n    --button-color-dark-rgb: var(--app-color-dark-rgb);\n\n    --button-color-primary: var(--app-color-primary);\n    --button-color-primary-contrast: var(--app-color-primary-contrast);\n    --button-color-primary-rgb: var(--app-color-primary-rgb);\n\n    display: flex;\n    align-items: center;\n\n    width: max-content;\n\n    font-family: var(--button-main-font-family);\n    font-size: 0.875rem;\n    font-weight: 500;\n    line-height: 1.75;\n    letter-spacing: 0.04em;\n    text-transform: uppercase;\n    color: var(--color);\n    text-align: center;\n    text-decoration: none;\n    text-overflow: ellipsis;\n\n    white-space: nowrap;\n\n    user-select: none;\n    vertical-align: top;\n    vertical-align: -webkit-baseline-middle;\n    pointer-events: auto;\n\n    font-kerning: none;\n\n    transition: background-color 150ms ease-in, box-shadow 150ms ease-in;\n\n    cursor: pointer;\n\n    padding: 0.375rem 1rem;\n    margin: 8px;\n\n    background-color: var(--bgcolor);\n\n    border-radius: var(--button-border-radius);\n    border: solid 1px var(--border-color);\n  }\n\n  button:focus {\n    box-shadow: 0 0 0 4px var(--focus-ring-color) !important;\n  }\n\n  button.button-variant-contained.button-color-light {\n    --color: var(--button-color-light);\n    --bgcolor: var(--button-color-light-contrast);\n  }\n\n  button.button-variant-contained.button-color-default {\n    --color: var(--button-color-light-contrast);\n    --bgcolor: var(--button-color-light);\n  }\n\n  button.button-variant-contained.button-color-primary {\n    --color: var(--button-color-primary-contrast);\n    --bgcolor: var(--button-color-primary);\n  }\n\n  button.button-variant-outlined.button-color-light {\n    --color: var(--button-color-light);\n    --border-color: var(--button-color-light);\n  }\n\n  button.button-variant-outlined.button-color-default {\n    --color: var(--button-color-dark);\n    --border-color: var(--button-color-dark);\n  }\n\n  button.button-variant-outlined.button-color-primary {\n    --color: var(--button-color-primary);\n    --border-color: var(--button-color-primary);\n  }\n\n  button.button-variant-text.button-color-light {\n    --color: var(--button-color-light);\n  }\n\n  button.button-variant-text.button-color-default {\n    --color: var(--button-color-dark);\n  }\n\n  button.button-variant-text.button-color-primary {\n    --color: var(--button-color-primary);\n  }\n\n  /* Disabled states */\n  button.disabled {\n    --color: rgba(var(--button-color-dark-rgb), 0.3) !important;\n\n    cursor: not-allowed;\n  }\n\n  button.disabled.button-variant-contained {\n    --bgcolor: rgba(var(--button-color-dark-rgb), 0.15);\n  }\n\n  button.disabled.button-variant-outlined {\n    --border-color: rgba(var(--button-color-dark-rgb), 0.15);\n  }\n\n  /* Hover states */\n  button.button-variant-text.button-color-light:hover,\n  button.button-variant-outlined.button-color-light:hover {\n    --bgcolor: rgba(var(--button-color-light-rgb), 0.08);\n  }\n\n  button.button-variant-text.button-color-default:hover,\n  button.button-variant-outlined.button-color-default:hover {\n    --bgcolor: rgba(var(--button-color-dark-rgb), 0.08);\n  }\n\n  button.button-variant-text.button-color-primary:hover,\n  button.button-variant-outlined.button-color-primary:hover {\n    --bgcolor: rgba(var(--button-color-primary-rgb), 0.08);\n  }\n\n  button.button-variant-contained.button-color-light:hover {\n    --bgcolor: rgba(var(--button-color-light-rgb), 0.85);\n  }\n\n  button.button-variant-contained.button-color-default:hover {\n    --bgcolor: rgba(var(--button-color-dark-rgb), 0.85);\n  }\n\n  button.button-variant-contained.button-color-primary:hover {\n    --bgcolor: rgba(var(--button-color-primary-rgb), 0.85);\n  }\n\n  button.button-color-light:focus {\n    --focus-ring-color: rgba(var(--button-color-light-rgb), 0.4);\n  }\n\n  button.button-color-default:focus {\n    --focus-ring-color: rgba(var(--button-color-dark-rgb), 0.4);\n  }\n\n  button.button-color-primary:focus {\n    --focus-ring-color: rgba(var(--button-color-primary-rgb), 0.4);\n  }\n\n  :global(button > *, button > *) {\n    display: flex;\n    align-items: center;\n  }\n\n  :global([slot]) {\n    fill: var(--color);\n  }\n\n  :global([slot='icon-start'] svg) {\n    margin-left: -6px;\n    margin-right: 4px;\n  }\n\n  :global([slot='icon-end'] svg) {\n    margin-left: 4px;\n    margin-right: -5px;\n  }\n</style>\n\n<button on:click class={classes} class:disabled {...$$props}>\n  <slot name=\"icon-start\" />\n  <slot />\n  <slot name=\"icon-end\" />\n</button>\n"
  },
  {
    "path": "misc/twindle-thread/src/components/IconButton.svelte",
    "content": "<script>\n  // @ts-check\n\n  // PROPS\n  export let disabled = false;\n\n  /** @type {'small' | 'medium'} */\n  export let size = 'small';\n\n  /** @type {'default' | 'light' | 'primary'} */\n  export let color = 'default';\n\n  /** @type {string} */\n  let classes;\n\n  $: {\n    const externalClass = $$props.class;\n    delete $$props.class;\n\n    classes = `color-${color} ${externalClass || ''}`;\n  }\n</script>\n\n<style>\n  button {\n    /* Internal properties */\n    --font-size: 1.25rem;\n    --color: rgba(var(--button-color-dark-rgb), 0.6);\n    --bgcolor: transparent;\n    --focus-ring-color: transparent;\n\n    /* External properties */\n    --button-color-dark-rgb: var(--app-color-dark-rgb);\n    --button-color-dark-light: var(--app-color-light-rgb);\n\n    --button-color-primary: var(--app-color-primary);\n    --button-color-primary-rgb: var(--app-color-primary-rgb);\n\n    border: 0;\n    border-radius: 50%;\n\n    cursor: pointer;\n\n    margin: 8px;\n    padding: 0.5rem;\n\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    flex: 0 0 auto;\n\n    position: relative;\n\n    user-select: none;\n\n    vertical-align: middle;\n\n    font-size: var(--font-size);\n    color: var(--color);\n    fill: var(--color);\n    text-decoration: none;\n    text-align: center;\n\n    background-color: var(--bgcolor);\n\n    transition: background-color 150ms ease-in, box-shadow 150ms ease-in;\n  }\n\n  button:focus {\n    box-shadow: 0 0 0 3px var(--focus-ring-color);\n  }\n\n  button.size-small {\n    --font-size: 1.25rem;\n  }\n\n  button.size-medium {\n    --font-size: 1.5rem;\n  }\n\n  button.color-light {\n    --color: rgba(var(--button-color-dark-light), 0.6);\n  }\n\n  button.color-primary {\n    --color: var(--button-color-primary);\n  }\n\n  button.color-default:hover {\n    --bgcolor: rgba(var(--button-color-dark-rgb), 0.15);\n  }\n\n  button.color-light:hover {\n    --bgcolor: rgba(var(--button-color-light-rgb), 0.15);\n  }\n\n  button.color-primary:hover {\n    --bgcolor: rgba(var(--button-color-primary-rgb), 0.15);\n  }\n\n  button.color-default:focus {\n    --focus-ring-color: rgba(var(--button-color-dark-rgb), 0.4)\n  }\n\n  button.color-light:focus {\n    --focus-ring-color: rgba(var(--button-color-light-rgb), 0.4)\n  }\n\n  button.color-primary:focus {\n    --focus-ring-color: rgba(var(--button-color-primary-rgb), 0.4)\n  }\n\n  :global(svg) {\n    font-size: var(--font-size)\n  }\n</style>\n\n<button\n  class={classes}\n  class:disabled\n  class:size-medium={size === 'medium'}\n  class:size-small={size === 'small'}\n  {...$$props}\n  >\n  <slot />\n</button>\n"
  },
  {
    "path": "misc/twindle-thread/src/components/list/List.svelte",
    "content": "<script context=\"module\">\n</script>\n\n<script>\n  // @ts-check\n  import { onMount } from 'svelte';\n  import { API } from '../../constants';\n  import { getFetch } from '../../helpers/fetch';\n  import { listElStore } from './listElStore';\n  import ListItem from './ListItem.svelte';\n\n  let list;\n  // let error;\n\n  let listItemsEls = [];\n\n  let focusableIndex = 0;\n\n  async function getThreadsList() {\n    const { data, error } = await getFetch(API.GET_THREADS_LISTS, {\n      page: 1,\n      limit: 30,\n      by: 'popular:desc',\n    });\n\n    if (error === 'invalid-value-of-by') {\n      // Handle errors here\n    }\n\n    if (error === 'invalid-value-of-page') {\n      // Handle errors here\n    }\n\n    if (error === 'invalid-value-of-limit') {\n      // Handle errors here\n    }\n\n    if (error === 'unable-to-get-threads-list') {\n      // Handle errors here\n    }\n\n    // All errors handled. Now do the thing here\n    list = data;\n  }\n\n  /**\n   *\n   * @param {KeyboardEvent} e\n   */\n  function handleKeyboard(e) {\n    const responses = {\n      ArrowUp: -1,\n      ArrowDown: +1,\n    };\n\n    if (e.key in responses) {\n      const num = responses[e.key];\n\n      focusableIndex = Math.min(\n        Object.keys($listElStore).length - 1,\n        Math.max(0, focusableIndex + num)\n      );\n\n      $listElStore[focusableIndex].focus();\n    }\n  }\n\n  onMount(async () => {\n    await getThreadsList();\n  });\n</script>\n\n<style>\n  ul {\n    list-style-type: none;\n\n    margin: 0;\n    padding: 0;\n  }\n</style>\n\n{#if list}\n  <ul>\n    {#each list as listItem, i}\n      <ListItem\n        index={i}\n        focusAble={focusableIndex === i}\n        bind:this={listItemsEls[i]}\n        on:keydown={handleKeyboard}\n        {listItem} />\n    {/each}\n  </ul>\n{/if}\n"
  },
  {
    "path": "misc/twindle-thread/src/components/list/ListItem.svelte",
    "content": "<script>\n  // @ts-check\n\n  import { createEventDispatcher } from 'svelte';\n  import Avatar from '../Avatar.svelte';\n  import { listElStore } from './listElStore';\n\n  /** @type {IListItemData}*/\n  export let listItem;\n\n  export let selectable = true;\n\n  export let selected = false;\n\n  export let focusAble = false;\n\n  /** @type {number} */\n  export let index;\n\n  /** @type {HTMLDivElement}*/\n  let ref;\n\n  const { user, text } = listItem;\n\n  // EVENTS\n  const dispatch = createEventDispatcher();\n\n  $: focusAble && ref?.focus();\n\n  $: $listElStore[index] = ref;\n</script>\n\n<style>\n  .container {\n    display: flex;\n    align-items: stretch;\n\n    border-radius: 0.5rem;\n\n    margin: 0.4rem 0.3rem;\n    padding: 0.1rem 0.2rem;\n  }\n\n  .container.selectable:hover,\n  .container.selectable:focus-visible {\n    background-color: rgba(var(--app-color-primary-rgb), 0.2);\n  }\n\n  .container.selectable:focus-visible {\n    box-shadow: 0 0 0 4px rgba(var(--app-color-primary-rgb), 0.5);\n  }\n\n  .text-area {\n    height: 100%;\n\n    white-space: nowrap;\n    text-overflow: ellipsis;\n\n    overflow: hidden;\n\n    width: 100%;\n  }\n\n  .avatar {\n    border: none;\n\n    display: flex;\n    align-items: center;\n\n    margin: 0;\n    padding: 0;\n\n    height: fit-content;\n    background-color: transparent;\n\n    border-radius: 50%;\n\n    cursor: pointer;\n  }\n</style>\n\n<div\n  tabindex={selectable && focusAble ? 0 : -1}\n  bind:this={ref}\n  class:selectable\n  class:selected\n  on:keydown\n  role=\"listitem\"\n  class=\"container\">\n  <div class=\"avatar\">\n    <Avatar size={54} src={user.profile_photo} />\n  </div>\n  <div class=\"text-area\">{text}</div>\n</div>\n"
  },
  {
    "path": "misc/twindle-thread/src/components/list/listElStore.js",
    "content": "import { writable } from 'svelte/store';\n\n/** @type {import(\"svelte/store\").Writable<{[key:number]: HTMLDivElement}>} */\nexport const listElStore = writable({});\n"
  },
  {
    "path": "misc/twindle-thread/src/components/utils/AppIcon.svelte",
    "content": "<script>\n  // @ts-check\n\n  /** @type {number} */\n  export let size = 24;\n\n  /** @type {string} */\n  export let path;\n</script>\n\n<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  style=\"width: {size / 16}rem; height: auto;\"\n  viewBox={`0 0 24 24`}\n  {...$$props}>\n  <path d={path} />\n</svg>\n"
  },
  {
    "path": "misc/twindle-thread/src/constants.js",
    "content": "export const baseURL = 'http://localhost:3800';\n\nexport const API = {\n  GET_THREADS_LISTS: `${baseURL}/threads`,\n};\n"
  },
  {
    "path": "misc/twindle-thread/src/helpers/fetch.js",
    "content": "// @ts-check\n\nimport fetchRetry from 'fetch-retry';\n\nconst Fetch = fetchRetry(fetch);\n\n/**\n * Custom GET implementation of fetch in web worker\n * @param {string} url The base URL to which to make the requests\n * @param {RequestInit} options Options to be passed to the fetch API\n * @param {{[key: string]: string | number}} params\n */\nexport async function getFetch(url, params = {}, options = {}) {\n  // Construct params from the `params` object\n  const param =\n    '?' +\n    Object.keys(params)\n      .map((prop) => `${encodeURI(prop)}=${encodeURI(params[prop] + '')}`)\n      .join('&');\n\n  return await Fetch(url + param, { ...options }).then((res) => res.json());\n}\n\n/**\n * Custom web worker implementation of fetch this app primarily needs\n * @param {string} url The URL to make the request to\n * @param {any} data Data to pass as body. Should be a key value pair\n * @param {RequestInit} options Options to be passed to the fetch API\n */\nexport async function postFetch(url, data, options = {}) {\n  const response = await Fetch(url, {\n    mode: 'cors',\n    method: 'POST',\n    body: JSON.stringify(data),\n    headers: new Headers({\n      'Access-Control-Allow-Origin': '*',\n      'Content-Type': 'application/json',\n      'Access-Control-Allow-Credentials': 'true',\n    }),\n    ...options,\n  });\n\n  return await response.json();\n}\n"
  },
  {
    "path": "misc/twindle-thread/src/index.js",
    "content": "import App from './App.svelte';\n\nvar app = new App({\n  target: document.body,\n});\n\nexport default app;\n\n// To whomever is reading this - DO NOT REMOVE IT.\n// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.\n// Learn more: https://www.snowpack.dev/#hot-module-replacement\nif (import.meta.hot) {\n  import.meta.hot.accept();\n  // window.location.reload();\n  import.meta.hot.dispose(() => {\n    app.$destroy();\n  });\n}\n"
  },
  {
    "path": "misc/twindle-thread/src/pages/Feed.svelte",
    "content": "<script>\n  import List from '../components/list/List.svelte';\n</script>\n\n<style>\n  .container {\n    --width: 61.8%;\n\n    width: var(--width);\n    min-height: 100%;\n  }\n\n  @media screen and (max-width: 961px) {\n    .container {\n      --width: 100%;\n    }\n  }\n</style>\n\n<section class=\"container\">\n  <List />\n</section>\n"
  },
  {
    "path": "misc/twindle-thread/types.d.ts",
    "content": "export interface IListItemData {\n  id: string;\n  conversation_id: string;\n  date_created: string;\n  last_updated: string;\n  score: number;\n  likes: number;\n  retweets: number;\n  replies_count: number;\n  text: string;\n  user: IListItemUser;\n}\n\nexport interface IListItemUser {\n  user_id: string;\n  handle: string;\n  name: string;\n  verified: 'true' | 'false';\n  profile_photo: string;\n}\n\ndeclare namespace svelte.JSX {\n  interface HTMLProps<HTMLUListElement> {\n    // If you want to use on:beforeinstallprompt\n    onfocusChange ?: (event: any) => any;\n  }\n}\n"
  },
  {
    "path": "misc/twindle-thread/types.js",
    "content": "/**\n * @typedef {import('./types').IListItemData} IListItemData\n * @typedef {import('./types').IListItemUser} IListItemUser\n */\n"
  },
  {
    "path": "misc/twindle-thread/workbox-config.js",
    "content": "module.exports = {\n  \"globDirectory\": \"build/\",\n  \"globPatterns\": [\n    \"**/*.{js,css,ico,html,svg}\"\n  ],\n  \"swDest\": \"build/sw.js\"\n};"
  },
  {
    "path": "misc/twindle-web/.prettierrc",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": false,\n  \"printWidth\": 100,\n  \"arrowParens\": \"always\"\n}\n"
  },
  {
    "path": "misc/twindle-web/README.md",
    "content": "## Our Twindle-web landing page\n\n![image](https://user-images.githubusercontent.com/37118877/101273098-9f3cbc80-37b8-11eb-809b-a963a44ded2a.png)\n\n---\n\n![image](https://user-images.githubusercontent.com/37118877/101273106-aa8fe800-37b8-11eb-8694-e81057994db8.png)\n\n---\n\n![image](https://user-images.githubusercontent.com/37118877/101273109-ad8ad880-37b8-11eb-9339-8996282de5c3.png)\n\n---\n\n![image](https://user-images.githubusercontent.com/37118877/101273112-b11e5f80-37b8-11eb-8a07-530290d7be82.png)\n\n---\n\n#### Want to contribute in Twindle-web. Go to the [Issues](https://github.com/twindle-co/twindle/issues) page & see `twindle-web` Issues to get started with your contributions."
  },
  {
    "path": "misc/twindle-web/dark-theme.css",
    "content": ":root {\r\n  --primary-heading-text: #eee;\r\n  --primary-background-color: #060606;\r\n  --secondary-background-color: #0c0c0b;\r\n  --scrollbar-track-color: #121211;\r\n  --scrollbar-thumb-color: #3b3c38;\r\n}\r\n\r\nhtml,\r\nbody {\r\n  background: var(--primary-background-color);\r\n  scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);\r\n}\r\n\r\nbody::-webkit-scrollbar-track {\r\n  background-color: var(--scrollbar-track-color);\r\n}\r\n\r\nbody::-webkit-scrollbar-thumb {\r\n  background-color: var(--scrollbar-thumb-color);\r\n}\r\n\r\n.line {\r\n  background-color: rgba(255, 255, 255, 0.2);\r\n}\r\n\r\nheader {\r\n  background-color: var(--primary-background-color);\r\n}\r\n\r\n.sticky {\r\n  box-shadow: 0 2px 4px #353632;\r\n}\r\n\r\n.nav-links,\r\n.logo {\r\n  color: #fff;\r\n}\r\n\r\n.main-nav li a {\r\n  color: #fff;\r\n}\r\n\r\n.logo {\r\n  color: #dbdbdb;\r\n}\r\n\r\n.navbar-toggle {\r\n  color: #fff;\r\n}\r\n\r\n@media screen and (min-width: 768px) {\r\n  .nav-links:hover {\r\n    color: #fff;\r\n    background-color: rgba(4, 95, 165, 0.2);\r\n  }\r\n}\r\n\r\nmain .page_heading .sub_heading {\r\n  color: var(--primary-heading-text);\r\n}\r\n\r\n.email_input {\r\n  background-color: #292a27;\r\n}\r\n\r\nmain .email_subs .email_input:focus,\r\nmain .email_subs .email_input:active,\r\nmain .email_subs .email_input:hover {\r\n  outline: none;\r\n  border: 2px solid #0b91f7;\r\n  box-shadow: 0 1px 3px #13598f;\r\n}\r\n\r\nmain .email_subs .sign_up_btn {\r\n  background: #0984e3;\r\n  border: white;\r\n  color: #f9f9f9;\r\n  box-shadow: 0 0 2px #dbdbdb;\r\n}\r\n\r\nmain .email_subs .sign_up_btn:hover {\r\n  background: #0a74c5;\r\n}\r\n\r\n#text > h1 {\r\n  font-weight: 800;\r\n  color: var(--primary-heading-text);\r\n}\r\n\r\n.text_area h1 {\r\n  color: var(--primary-heading-text);\r\n}\r\n\r\n.text_area p {\r\n  color: #c8c8c8e6;\r\n}\r\n\r\n#testimonials h1 {\r\n  color: var(--primary-heading-text);\r\n}\r\n\r\n#tD {\r\n  background-color: var(--secondary-background-color);\r\n  box-shadow: 3px 3px 5px 3px #faebdc;\r\n}\r\n\r\n#color {\r\n  background-color: var(--secondary-background-color);\r\n}\r\n\r\n#tCard #userImg {\r\n  border: 2px solid blue;\r\n}\r\n\r\nfooter {\r\n  color: #f4f4f4;\r\n}\r\n"
  },
  {
    "path": "misc/twindle-web/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <link rel=\"stylesheet\" href=\"style.css\" />\n    <title>Twindle | Create books out of your tweets</title>\n    <link href=\"https://fonts.googleapis.com/icon?family=Material+Icons\" rel=\"stylesheet\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\"\n    />\n    <script src=\"https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js\"></script>\n\n    <script defer src=\"./scripts/testimonials.js\"></script>\n    <script defer src=\"./scripts/header.js\"></script>\n    <script defer src=\"./index.js\"></script>\n  </head>\n\n  <body>\n    <header>\n      <nav class=\"navbar\">\n        <span class=\"navbar-toggle\" id=\"js-navbar-toggle\">\n          <i class=\"fa fa-bars\"></i>\n        </span>\n        <a href=\"#\" class=\"logo\">\n          <h2>TWINDLE</h2>\n        </a>\n\n        <i class=\"material-icons\" style=\"margin-left: auto; order: 0\">nights_stay</i>\n        <ul class=\"main-nav\" id=\"js-menu\">\n          <li><a href=\"#home\" class=\"nav-links\">Home</a></li>\n          <li><a href=\"#features\" class=\"nav-links\">Features</a></li>\n          <li><a href=\"#who_we_are\" class=\"nav-links\">About</a></li>\n          <li><a href=\"./team_details/team_details.html\" class=\"nav-links\">Team</a></li>\n          <li><a href=\"#testimonials\" class=\"nav-links\">Testimonials</a></li>\n        </ul>\n      </nav>\n\n      <!-- <nav>\n            <a href=\"#home\">\n                <h2>TWINDLE</h2>\n            </a>\n            \n    <i class=\"material-icons\">nights_stay</i>\n            <span class=\"navbar-toggle\" id=\"js-navbar-toggle\">\n                <i class=\"fa fa-bars\"></i>\n            </span>\n            <ul class=\"main-nav\" id=\"js-menu\">\n                <li><a href=\"#home\" class=\"nav-links\">Home</a></li>\n                <li><a href=\"#features\" class=\"nav-links\">Features</a></li>\n                <li><a href=\"#who_we_are\" class=\"nav-links\">About</a></li>\n                <li><a href=\"./team_details/team_details.html\" class=\"nav-links\">Team</a></li>\n                <li><a href=\"#testimonials\" class=\"nav-links\">Testimonials</a></li>\n            </ul>\n        </nav> -->\n    </header>\n\n    <!-- Main section -->\n    <main id=\"home\">\n      <section class=\"page_heading\">\n        <p class=\"sub_heading\">A better reading experience of twitter threads</p>\n      </section>\n      <section class=\"coming_soon\">\n        <img class=\"function_image\" src=\"./images/function.svg\" alt=\"What we do here?\" />\n        <p class=\"coming_soon\">Coming Soon. Please sign up to know more!!!</p>\n      </section>\n      <section class=\"email_subs\">\n        <form id=\"email_sub_form\">\n          <input type=\"email\" class=\"email_input\" placeholder=\"Enter your email to sign up\" />\n          <button class=\"sign_up_btn\" type=\"submit\">Sign Up</button>\n        </form>\n      </section>\n      <section class=\"what_we_do\">\n        <h2 class=\"heading_two\">What is Twindle?</h2>\n        <br />\n        <p>Twindle converts your favourite Twitter threads & present it right in your mobile</p>\n      </section>\n    </main>\n\n    <!-- Feature Section -->\n    <section id=\"features\">\n      <div class=\"container\">\n        <lottie-player\n          src=\"https://assets8.lottiefiles.com/packages/lf20_e9of4o97.json\"\n          background=\"transparent\"\n          speed=\"1\"\n          style=\"width: 350px; height: 350px\"\n          loop\n          autoplay\n        ></lottie-player>\n        <div id=\"text\" class=\"text_area\">\n          <h1>A little about our features</h1>\n          <br />\n          <p>\n            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor\n            incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud\n            exercitation ullamco laboris\n          </p>\n        </div>\n      </div>\n    </section>\n\n    <!-- Line Division between sections -->\n    <div class=\"line\"></div>\n\n    <!-- Who We Are Section -->\n    <section id=\"who_we_are\">\n      <div class=\"container\">\n        <div class=\"text_area gap\">\n          <h1>We're a strong opensource community</h1>\n          <br />\n          <p>\n            Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet\n            appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis\n            constituto, mel ne molestie recusabo inciderint\n          </p>\n        </div>\n        <div id=\"remote_design\">\n          <lottie-player\n            src=\"https://assets9.lottiefiles.com/packages/lf20_spvnlrri.json\"\n            background=\"transparent\"\n            speed=\"1\"\n            style=\"width: 450px; height: 450px\"\n            loop\n            autoplay\n          ></lottie-player>\n        </div>\n      </div>\n    </section>\n\n    <!-- Testimonials Section -->\n    <section id=\"testimonials\">\n      <h1>Testimonials</h1>\n      <div id=\"tD\">\n        <button class=\"arrow\" id=\"back\">&lt;</button>\n        <div id=\"tCard\">\n          <div id=\"color\">\n            <img id=\"quot1\" src=\"./images/format_quote-24px.svg\" />\n            <p id=\"ptext\">\n              This project was started to encourage people who just started to learn programming and\n              it took off like a rocket. People from diverse backgrounds engaged together like a\n              football team to make this possible.\n            </p>\n            <img id=\"quot2\" src=\"./images/format_quote-24px.svg\" alt=\"\" />\n            <p id=\"user\">- Random1</p>\n          </div>\n          <div id=\"userimgD\">\n            <img\n              id=\"userImg\"\n              src=\"https://www.bestfunnies.com/wp-content/uploads/2015/06/Pictures-of-Ugly-People-18-570x428.jpg\"\n            />\n          </div>\n        </div>\n        <button class=\"arrow\" id=\"front\">&gt;</button>\n      </div>\n    </section>\n\n    <!-- Line Division between sections -->\n    <div class=\"line\"></div>\n\n    <!-- Footer Section Code -->\n    <footer>\n      <nav>\n        <ul>\n          <li>\n            <a href=\"https://twitter.com/twindleco\" target=\"_blank\"\n              ><img src=\"./images/twitter.svg\" alt=\"Twitter Link\"\n            /></a>\n          </li>\n          <li>\n            <a href=\"https://github.com/twindle-co\" target=\"_blank\"\n              ><img src=\"./images/github.svg\" alt=\"GitHub Link\"\n            /></a>\n          </li>\n          <li>\n            <a href=\"https://www.youtube.com/channel/UCKxUmbHq5P5pd5IyUiZ8MHA\" target=\"_blank\"\n              ><img src=\"./images/youtube.svg\" alt=\"YouTube Link\"\n            /></a>\n          </li>\n        </ul>\n        <p>&copy; Copyright TwindleCo 2020</p>\n      </nav>\n    </footer>\n\n    <script>\n      let mainNav = document.getElementById(\"js-menu\");\n      let navBarToggle = document.getElementById(\"js-navbar-toggle\");\n\n      navBarToggle.addEventListener(\"click\", function () {\n        mainNav.classList.toggle(\"active\");\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/twindle-web/index.js",
    "content": "// Variables for DOM events\nconst emailEntry = document.querySelector(\"#email_entry\");\nconst form = document.forms[\"email_sub_form\"];\n\n/** Bypass CORS Policy */\nconst proxyUrl = \"https://cors-anywhere.herokuapp.com/\"; // localhost CORS bypass\nconst scriptUrl = `https://script.google.com/macros/s/AKfycbxSk46xwxe9nTlEdr7L3zabI7Au1puj7BTMk9d_WibZkS0m5Gs/exec`; // Script of google form - Twindle needs to create it later. Currently my email is set.\n\n// Event Listeners\nform.addEventListener(\"submit\", addEmailToGoogleSheet);\n\n// Functions\nfunction addEmailToGoogleSheet(event) {\n  event.preventDefault();\n  const email = emailEntry.value;\n  const isEmail = validateEmail(email);\n\n  if (!isEmail) {\n    alert(`Enter valid email here`); // this can be enhanced using flash messages\n    return false;\n  }\n\n  fetch(proxyUrl + scriptUrl, { method: \"POST\", body: new FormData(form) })\n    .then(\n      (response) => alert(`Thanks for subscribing to our service! We will contact you soon...`) // again, this can be enhanced using flash messages\n    )\n    .catch((error) => console.error(\"Error!\", error.message));\n}\n\nfunction validateEmail(email) {\n  const re = new RegExp(\"[^@ \\t\\r\\n]+@[^@ \\t\\r\\n]+.[^@ \\t\\r\\n]+\");\n  return re.test(email);\n}\n"
  },
  {
    "path": "misc/twindle-web/scripts/header.js",
    "content": "const header = document.querySelector(\"header\");\n\nwindow.addEventListener(\"scroll\", (e) => {\n  let sticky = 35 + header.offsetTop;\n\n  if (window.pageYOffset > sticky) {\n    header.classList.add(\"sticky\");\n  } else {\n    header.classList.remove(\"sticky\");\n  }\n});\n"
  },
  {
    "path": "misc/twindle-web/scripts/testimonials.js",
    "content": "let pinfo = document.getElementById(\"ptext\");\nlet name = document.getElementById(\"user\");\nlet imgD = document.getElementById(\"userimgD\");\n\nconst data = [\n  {\n    id: 0,\n    name: \"Random1\",\n    text:\n      \"This project was started to encourage people who just started to learn programming and it took off like a rocket. People from diverse backgrounds engaged together like a football team to make this possible.\",\n    img: \"https://avatars0.githubusercontent.com/u/354596?s=64&v=4g\",\n  },\n  {\n    id: 1,\n    name: \"Random2\",\n    text:\n      \"Random 2- Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis constituto, mel ne molestie recusabo inciderint\",\n    img:\n      \"https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/09f66320974015.562f438db41be.jpg\",\n  },\n  {\n    id: 2,\n    name: \"Random3\",\n    text:\n      \"Random 3 - Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis constituto, mel ne molestie recusabo inciderint\",\n    img: \"https://avatars0.githubusercontent.com/u/354596?s=64&v=4\",\n  },\n  {\n    id: 3,\n    name: \"Random4\",\n    text:\n      \"Random 4- Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis constituto, mel ne molestie recusabo inciderint\",\n    img:\n      \"https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/09f66320974015.562f438db41be.jpg\",\n  },\n  {\n    id: 4,\n    name: \"Random5\",\n    text:\n      \"Random 5 - Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis constituto, mel ne molestie recusabo inciderint\",\n    img: \"https://avatars0.githubusercontent.com/u/19240564?s=64&v=4\",\n  },\n  {\n    id: 5,\n    name: \"Random\",\n    text:\n      \"Random - Pri id phaedrum tincidunt reformidans, qui ponderum antiopam gloriatur ut. Amet appellantur et eum, habeo debet eu mel. Et denique explicari quo. Ut vel erant euripidis constituto, mel ne molestie recusabo inciderint\",\n    img:\n      \"https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/09f66320974015.562f438db41be.jpg\",\n  },\n];\n\nasync function goFront(d) {\n  let i = await d.filter((x) => {\n    if (name.innerText == \"- \" + x.name) {\n      return x;\n    }\n  });\n\n  let info = (await d[i[0].id + 1]) ? d[i[0].id + 1] : null;\n  if (info != null) {\n    pinfo.innerText = \"\";\n    pinfo.innerText = info.text;\n    name.innerText = \"- \" + info.name;\n    imgD.innerHTML = \"<img id='userImg' src=\" + info.img + \"/>\";\n  }\n}\n\nasync function goBack(d) {\n  let i = await d.filter((x) => {\n    if (pinfo.innerText == x.text) {\n      return x;\n    }\n  });\n  let info = (await d[i[0].id - 1]) ? d[i[0].id - 1] : null;\n\n  if (info != null) {\n    pinfo.innerText = info.text;\n    name.innerText = \"- \" + info.name;\n    imgD.innerHTML = \"<img id='userImg' src=\" + info.img + \" alt=''/>\";\n  }\n}\n\ndocument.getElementById(\"front\").setAttribute(\"onclick\", \"goFront(data)\");\ndocument.getElementById(\"back\").setAttribute(\"onclick\", \"goBack(data)\");\n"
  },
  {
    "path": "misc/twindle-web/style.css",
    "content": "@import url(\"https://fonts.googleapis.com/css2?family=Mulish:wght@400;800&display=swap\");\n/* CSS Variables in the root element */\n\n:root {\n  --primary-heading-text: #181818;\n  --primary-background-color: #fffdf9;\n  --secondary-background-color: #faeee7;\n  --scrollbar-track-color: #eee;\n  --scrollbar-thumb-color: #c8c8c8;\n}\n\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nhtml,\nbody {\n  background: var(--primary-background-color);\n  font-family: \"Mulish\", sans-serif;\n  scroll-behavior: smooth;\n  /* Specifically for firefox browser */\n  scrollbar-width: thin;\n  scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color);\n}\n\nbody::-webkit-scrollbar {\n  width: 0.7rem;\n}\n\nbody::-webkit-scrollbar-track {\n  background-color: var(--scrollbar-track-color);\n}\n\nbody::-webkit-scrollbar-thumb {\n  border-radius: 3px;\n  background-color: var(--scrollbar-thumb-color);\n}\n\n/* Creates a line to divide the sections */\n\n.line {\n  max-width: 85vw;\n  height: 2px;\n  background-color: rgba(0, 0, 0, 0.2);\n  margin: 0 auto;\n  border-radius: 10px;\n}\n\n/* Navigation Bar */\n\nheader {\n  display: inline;\n  justify-content: space-evenly;\n  align-items: center;\n  padding: 1.5rem;\n  width: 100%;\n  position: fixed;\n  background-color: var(--primary-background-color);\n  z-index: 2;\n  box-shadow: none;\n  transition: all 0.2s ease-in-out;\n}\n\n.sticky {\n  padding: 0.7rem 1.5rem;\n  box-shadow: 0 2px 4px #ddd;\n}\n\n/* \nheader nav a {\n  font-family: Mulish;\n  font-style: normal;\n  color: #202020;\n  text-decoration: none;\n  margin-left: 20px;\n  display: flex;\n  align-items: center;\n}\n\nheader nav ul {\n  display: block;\n  justify-content: flex-end;\n  list-style: none;\n  margin-right: 2rem;\n}\n\nheader nav ul li a {\n  color: #000000;\n  text-decoration: none;\n  padding: 0.5rem 1rem;\n  border-radius: 2rem;\n  margin-left: 1rem;\n  transition: 0.3s ease-in-out all;\n}\n\nheader li {\n  display: flex;\n  align-items: center;\n}\n\nheader nav ul li a:hover,\nheader nav ul li a:focus {\n  background-color: rgba(9, 133, 227, 0.2);\n} */\n\n.navbar {\n  font-size: 18px;\n}\n\n.main-nav {\n  list-style-type: none;\n  display: none;\n}\n\n.nav-links,\n.logo {\n  text-decoration: none;\n  color: black;\n}\n\n.main-nav li {\n  text-align: center;\n  margin: 15px auto;\n}\n\n.main-nav li a {\n  color: #000000;\n  text-decoration: none;\n  padding: 0.5rem 1rem;\n  border-radius: 2rem;\n  margin-left: 1rem;\n  transition: 0.3s ease-in-out all;\n}\n\n.logo {\n  display: inline-block;\n  margin-left: 20px;\n  font-family: Mulish;\n  font-style: normal;\n  color: #202020;\n  text-decoration: none;\n}\n\n.navbar-toggle {\n  position: absolute;\n  top: 10px;\n  right: 20px;\n  cursor: pointer;\n  color: black;\n  font-size: 24px;\n}\n\n.active {\n  display: block;\n}\n\n@media screen and (min-width: 768px) {\n  .navbar {\n    display: flex;\n    justify-content: space-between;\n    padding-bottom: 0;\n    height: 30px;\n    align-items: center;\n  }\n  .main-nav {\n    display: flex;\n    margin-right: 30px;\n    flex-direction: row;\n    justify-content: flex-end;\n  }\n  .main-nav li {\n    margin: 0;\n  }\n  .nav-links {\n    margin-left: 40px;\n  }\n  .logo {\n    margin-top: 0;\n  }\n  .navbar-toggle {\n    display: none;\n  }\n  .nav-links:hover {\n    color: black;\n    background-color: rgba(9, 133, 227, 0.2);\n  }\n}\n\n/* Main Section / HomePage */\n\nmain {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-content: center;\n  align-items: center;\n  padding-top: 5rem;\n}\n\nmain .page_heading {\n  text-align: center;\n  padding: 2rem 4rem;\n  margin-top: 1.5rem;\n}\n\nmain .page_heading .sub_heading {\n  font-size: 2.25rem;\n  margin-bottom: 0.25rem;\n  font-weight: 600;\n  margin-top: 2.3rem;\n  color: var(--primary-heading-text);\n}\n\nmain .coming_soon {\n  text-align: center;\n  padding: 2rem 4rem;\n  font-size: 1.5rem;\n}\n\nmain .coming_soon img.function_image {\n  width: 100%;\n  height: auto;\n  margin: 0 auto;\n  margin-bottom: 0.5rem;\n}\n\nmain .coming_soon p.coming_soon {\n  margin-top: 0.5rem;\n  padding: 3px;\n  font-size: 14px;\n}\n\nmain .email_subs {\n  text-align: center;\n  padding: 2rem 4rem;\n}\n\nmain .email_subs {\n  border-radius: 5px;\n  width: 80vw;\n  height: 35px;\n  border: none;\n  box-shadow: none;\n  padding: 1rem;\n  font-size: 15px;\n  transition: border 0.1s ease-in, box-shadow 0.1s ease-in;\n}\n\n.email_input {\n  background-color: #e7e7e7;\n  border-radius: 5px;\n  width: 50vw;\n  height: 35px;\n  border: none;\n  box-shadow: none;\n  padding: 1rem;\n  font-size: 15px;\n  transition: border 0.1s ease-in, box-shadow 0.1s ease-in;\n}\n\nmain .email_subs .email_input:focus,\nmain .email_subs .email_input:active,\nmain .email_subs .email_input:hover {\n  outline: none;\n  border: 2px solid #0b91f7;\n  box-shadow: 0 1px 3px #13598f;\n}\n\nmain .email_subs .sign_up_btn {\n  font-size: 15px;\n  background: #0984e3;\n  width: 99px;\n  height: 35px;\n  border-radius: 5px;\n  border: white;\n  color: white;\n  box-shadow: 0 0 2px #333;\n  cursor: pointer;\n  outline: none;\n  position: relative;\n  transition: background 0.1s ease-in;\n}\n\nmain .email_subs .sign_up_btn:hover {\n  background: #0a74c5;\n}\n\nmain .email_subs .sign_up_btn:active {\n  top: 1.5px;\n}\n\nmain .what_we_do {\n  text-align: center;\n  padding: 2rem 0rem;\n}\n\nmain .what_we_do .heading_two {\n  font-size: 20px;\n  font-weight: 400;\n  line-height: 25px;\n}\n\nmain .what_we_do p {\n  font-weight: 200;\n  font-size: 15px;\n  width: 80vw;\n}\n\n/* Features Section */\n\n#features {\n  max-width: 100vw;\n  padding-top: 4rem;\n  padding-bottom: 4rem;\n}\n\n.container {\n  margin: 0 auto;\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n  max-width: 75vw;\n}\n\n@media only screen and (max-width: 768px) {\n  .container {\n    flex-direction: column;\n  }\n}\n\n#text > h1 {\n  font-weight: 800;\n  color: var(--primary-heading-text);\n}\n\n#text > h4 {\n  font-weight: 400;\n}\n\n/* Who we are / About Section */\n\n#who_we_are {\n  /* height: auto; */\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n  padding-bottom: 0.5rem;\n}\n\n.text_area h1 {\n  font-weight: 600;\n  font-size: 30px;\n  width: 516px;\n  height: auto;\n  color: var(--primary-heading-text);\n}\n\n.text_area p {\n  font-size: 20px;\n  width: 543px;\n  height: 75px;\n  color: rgba(12, 12, 12, 0.9);\n}\n\n@media only screen and (max-width: 768px) {\n  .gap {\n    margin-top: 30px;\n  }\n  .text_area {\n    width: 80vw;\n  }\n  .text_area p {\n    width: auto;\n  }\n}\n\n#remote_design {\n  width: auto;\n  /* height: 436px; */\n}\n\n/* Testimonials Section */\n\n#testimonials {\n  max-width: 100vw;\n  max-height: 60vh;\n  padding-bottom: 3rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  flex-direction: column;\n}\n\n#testimonials h1 {\n  font-size: 2.5rem;\n  padding-bottom: 1rem;\n  color: var(--primary-heading-text);\n}\n\n#testimonials button {\n  background: none;\n  font-weight: 800;\n  font-size: 3rem;\n  height: 80px;\n  width: 80px;\n  border-radius: 50%;\n}\n\n#tD {\n  max-width: 80vw;\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n  padding: 1rem;\n  background-color: var(--secondary-background-color);\n  border-radius: 10px;\n  -webkit-box-shadow: 3px 3px 5px 3px #fde5cb;\n  box-shadow: 3px 3px 5px 3px #fde5cb;\n}\n\n#tCard {\n  margin-top: 80px;\n  width: 600px;\n  height: auto;\n  padding-left: 30px;\n  padding-right: 15px;\n  text-align: center;\n  display: flex;\n  flex-direction: column;\n}\n\n#color {\n  background-color: var(--secondary-background-color);\n  display: flex;\n  flex-direction: column;\n  padding-top: 15px;\n  padding-bottom: 15px;\n}\n\n#user {\n  text-align: right;\n  margin-right: 40px;\n}\n\n#tCard #quot1 {\n  width: 50px;\n  transform: rotate(180deg);\n}\n\n#tCard #quot2 {\n  width: 50px;\n  align-self: flex-end;\n}\n\n#tCard #userImg {\n  width: 80px;\n  height: 80px;\n  border-radius: 50%;\n  border: 2px solid blue;\n}\n\n#userimgD {\n  margin-top: -40px;\n  width: 100%;\n  height: 80px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background-color: none;\n}\n\n.arrow {\n  outline: none;\n}\n\n.arrow:hover {\n  cursor: pointer;\n}\n\n/* Footer Section */\n\nfooter {\n  color: black;\n  display: flex;\n  justify-content: center;\n  align-content: center;\n}\n\nfooter nav {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-content: center;\n  padding: 2rem 0;\n}\n\nfooter nav ul {\n  list-style: none;\n  display: flex;\n  justify-content: space-evenly;\n}\n\nfooter nav p {\n  position: relative;\n  top: 1rem;\n}\n"
  },
  {
    "path": "misc/twindle-web/team_details/README.md",
    "content": "# Twindle Teams Details\n\nThis page contains the list of Twindle Teammates that our working & contributing to Twindle.\n\n## How to add your name to the list\n\nTo add your name to the list, simply open `data.json` file, and then add your own data at the bottom, using the below template:\n( **Don't include the square brackets \"[ ]\" !!! They are just there to indicate an example placeholder.** )\n\n```moonscript\n  ,{\n    \"id\": \"[next_number]\",\n    \"name\": \"[YOUR_FULL_NAME]\",\n    \"img\": \"[YOUR_IMG_URL]\",\n    \"links\": {\n      \"website\": \"[YOUR_WEBSITE_URL]\",\n      \"linkedin\": \"[YOUR_LINKEDIN_URL]\",\n      \"github\": \"[YOUR_GITHUB_URL]\",\n      \"twitter\": \"[YOUR_TWITTER_URL]\",\n    },\n    \"title\": \"[YOUR_TITLE]\",\n    \"location\": {\n      \"city\": \"[YOUR_CITY]\",\n      \"state\": \"[YOUR_STATE_OR_PROVINCE]\",\n      \"country\": \"[YOUR_COUNTRY]\"\n    }\n  }\n```\n\n### Please note!\n\n- **If you do not want to fill some of the fields, leave them blank (e.g. `\"state\": \"\",`)**.\n\n\n### How to add profile image using Github avatars\n\n1. Go to your profile page on github.com\n2. Append to your GitHub profile url “.png”, so it will look like this: \n\n    https://github.com/akshay2996.png\n\n3. Hit enter and the browser will generate a page with your image, it look like this:\n    \n    https://avatars0.githubusercontent.com/u/37118877?v=4\n\n4. Copy url of this page and paste it in persons.js file"
  },
  {
    "path": "misc/twindle-web/team_details/data.json",
    "content": " {\n    \"users\": [\n    {\n      \"id\": 1,\n      \"name\": \"Akshay Sharma\",\n      \"img\": \"https://avatars0.githubusercontent.com/u/37118877?v=4\",\n      \"links\": {\n        \"website\": \"https://www.developeratease.com/\",\n        \"linkedin\": \"https://www.linkedin.com/in/akshay-sharma-7962ab13a/\",\n        \"github\": \"https://github.com/Akshay2996\",\n        \"twitter\": \"https://twitter.com/AkshayS2909\"\n      },\n      \"title\": \"FrontEnd Developer\",\n      \"location\": {\n        \"city\": \"Bangalore\",\n        \"state\": \"Karnataka\",\n        \"country\": \"India\"\n      }\n    }, \n    {\n      \"id\": 2,\n      \"name\": \"Satyaki Bose\",\n      \"img\": \"https://avatars1.githubusercontent.com/u/25426670?s=400&u=7cc72ca148ae88bf19a10b8f36b21806504ffe34&v=4\",\n      \"links\": {\n        \"website\": \"\",\n        \"linkedin\": \"https://www.linkedin.com/in/satyaki07/\",\n        \"github\": \"https://github.com/satyaki07\",\n        \"twitter\": \"https://twitter.com/satyaki_07\"\n      },\n      \"title\": \"Developer\",\n      \"location\": {\n        \"city\": \"Kolkata\",\n        \"state\": \"West Bengal\",\n        \"country\": \"India\"\n      }\n    }, \n    {\n      \"id\": 3,\n      \"name\": \"Rafael Rodrigues\",\n      \"img\": \"https://avatars3.githubusercontent.com/u/46648727?v=4\",\n      \"links\": {\n        \"website\": \"\",\n        \"linkedin\": \"https://www.linkedin.com/in/rafaelrodrigues55/\",\n        \"github\": \"https://github.com/RafaelBatman55\",\n        \"twitter\": \"https://twitter.com/rafa_55\"\n      },\n      \"title\": \"FrontEnd Developer\",\n      \"location\": {\n        \"city\": \"Resende\",\n        \"state\": \"Rio de Janeiro\",\n        \"country\": \"Brazil\"\n      }\n    },\n    {\n      \"id\": 4,\n      \"name\": \"Ameen Shafeeq\",\n      \"img\": \"https://avatars0.githubusercontent.com/u/49345531?v=4\",\n      \"links\": {\n        \"website\": \"https://ameencodes.tech/\",\n        \"linkedin\": \"\",\n        \"github\": \"https://github.com/unevencoder\",\n        \"twitter\": \"https://twitter.com/crafter_coder\"\n      },\n      \"title\": \"Student\",\n      \"location\": {\n        \"city\": \"Jeddah\",\n        \"state\": \"\",\n        \"country\": \"Saudi Arabia\"\n      }\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Joel Vinay Kumar\",\n      \"img\": \"https://avatars0.githubusercontent.com/u/16177724?s=460&u=e309b78e0ea859ec30fa897b65e7625f150f3615&v=4\",\n      \"links\": {\n        \"website\": \"http://joel.swecha.io/\",\n        \"linkedin\": \"\",\n        \"github\": \"https://github.com/JoelVinayKumar\",\n        \"twitter\": \"https://twitter.com/JoelVinayKumar\"\n      },\n      \"title\": \"Software Engineer\",\n      \"location\": {\n        \"city\": \"Hyderabad\",\n        \"state\": \"Telangana\",\n        \"country\": \"India\"\n      }\n    },\n    {\n      \"id\": 6,\n      \"name\": \"Devarshi Doshi\",\n      \"img\": \"https://avatars2.githubusercontent.com/u/63865514?s=460&u=e295ee204b98c925b3a21c441e9ef6673cfe0bac&v=4\",\n      \"links\": {\n        \"website\": \"https://devarshidoshi.github.io/Portfolio/\",\n        \"linkedin\": \"https://www.linkedin.com/in/devarshi-doshi-45571a13b/\",\n        \"github\": \"https://github.com/devarshidoshi\",\n        \"twitter\": \"https://twitter.com/DoshiDevarshi\"\n      },\n      \"title\": \"Web Developer\",\n      \"location\": {\n        \"city\": \"Vadodara\",\n        \"state\": \"Gujarat\",\n        \"country\": \"India\"\n      }\n    } \n  ]\n}\n"
  },
  {
    "path": "misc/twindle-web/team_details/team_details.css",
    "content": "@import url(\"https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&display=swap\");\n\n:root {\n  font-family: \"Montserrat\", sans-serif;\n}\n\n* {\n  padding: 0;\n  margin: 0;\n  box-sizing: border-box;\n}\n\na {\n  outline: none;\n  text-decoration: none;\n  color: #010101;\n}\n\n.heading {\n  text-align: center;\n  padding: 1rem;\n}\n\n/* Automate the styling for the CSS Card */\n\n.card-gallery {\n  margin: 0 auto;\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));\n  grid-auto-flow: row;\n  /* grid-template-columns: 1fr 1fr; */\n  grid-gap: 3rem;\n  width: 80vw;\n  justify-items: space-evenly;\n  align-content: center;\n  /* border: 5px solid black; */\n  padding: 5px;\n}\n\n.card {\n  position: relative;\n  padding: 10px;\n  width: 100%;\n  border-radius: 5px;\n  transition: all 0.3s ease-in-out;\n  padding: 2rem;\n  background-color: #e9e9e9;\n  -webkit-box-shadow: 1px 1px 2px 1px rgba(189, 189, 189, 0.75);\n  -moz-box-shadow: 1px 1px 2px 1px rgba(189, 189, 189, 0.75);\n  box-shadow: 1px 1px 2px 1px rgba(189, 189, 189, 0.75);\n}\n\n.card:hover {\n  -webkit-box-shadow: 0px 5px 5px -3px rgba(150, 150, 150, 0.5),\n    1px 1px 5px 3px rgba(150, 150, 150, 0.5);\n  -moz-box-shadow: 0px 5px 5px -3px rgba(150, 150, 150, 0.5),\n    1px 1px 5px 3px rgba(150, 150, 150, 0.5);\n  box-shadow: 0px 5px 5px -3px rgba(150, 150, 150, 0.5), 1px 1px 5px 3px rgba(150, 150, 150, 0.5);\n\n  background-color: rgba(21, 155, 218, 0.8);\n}\n\n.card:hover .image {\n  border: 5px solid rgb(241, 209, 27);\n}\n\n.card:hover .title {\n  color: #fefffe;\n  font-weight: 600;\n}\n\n/* Card Header */\n\n.card-heading {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  text-align: center;\n  padding-bottom: 2rem;\n}\n\n.image {\n  display: block;\n  height: 150px;\n  width: 150px;\n  border-radius: 50%;\n  border: 5px solid rgb(32, 160, 219);\n  transition: border 0.3s ease-in-out;\n  object-fit: cover;\n}\n\n.name {\n  padding: 20px 0;\n  font-weight: 700;\n}\n\n.title {\n  font-weight: 500;\n  font-size: 1rem;\n}\n\nhr {\n  margin-bottom: 20px;\n  border: 0.05px solid #4e4e4e;\n}\n\n/* Card Links */\n\n.card-links {\n  display: flex;\n  justify-content: center;\n}\n\n.card-links a {\n  margin: 0 3px;\n  padding: 7px;\n  border-radius: 50%;\n  /* background: transparent; */\n  filter: invert(0%);\n  transition: all 0.25s ease-in-out;\n}\n\n.card-links a:hover {\n  /* can comment out the below line👇 to try a different background*/\n  background: rgba(209, 209, 209, 0.9);\n  filter: invert(100%);\n}\n\n/* Card Location */\n\n.card-location {\n  display: block;\n  margin-top: 10px;\n}\n\n.card-location h5 {\n  font-size: 15px;\n  font-weight: 500;\n  text-align: center;\n}\n"
  },
  {
    "path": "misc/twindle-web/team_details/team_details.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Teams Page</title>\n    <link rel=\"stylesheet\" href=\"team_details.css\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css\"\n    />\n    <script defer src=\"team_details.js\"></script>\n  </head>\n  <body>\n    <header class=\"header\">\n      <h1 class=\"heading\">Meet Our Team</h1>\n    </header>\n    <div class=\"card-gallery\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "misc/twindle-web/team_details/team_details.js",
    "content": "const cards = document.querySelector(\".card-gallery\");\n\nfunction generateCard(info) {\n  return `\n    <div class=\"card\">\n        <div class=\"card-heading\">\n          <img\n            src=${info.img}\n            class=\"image\"\n          />\n          <h2 class=\"name\">${info.name}</h2>\n          <h5 class=\"title\">${info.title}</h5>\n        </div>\n        <hr />\n        <div class=\"card-links\">\n          <a\n            href=${info.links.github}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            ><i class=\"fa fa-github fa-2x\" aria-hidden=\"true\"></i\n          ></a>\n          <a\n            href=${info.links.twitter}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            ><i class=\"fa fa-twitter fa-2x\" aria-hidden=\"true\"></i\n          ></a>\n          <a href=${info.links.linkedin} target=\"_blank\" rel=\"noopener noreferrer\"\n            ><i class=\"fa fa-linkedin fa-2x\" aria-hidden=\"true\"></i\n          ></a>\n          <a\n            href=${info.links.website}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            ><i class=\"fa fa-globe fa-2x\" aria-hidden=\"true\"></i\n          ></a>\n        </div>\n        <div class=\"card-location\">\n          <h5 class=\"location\">${info.location.city} ${info.location.state} ${info.location.country}</h5>\n        </div>\n      </div>\n    `;\n}\n\nasync function pushToDom() {\n  let fetchData = await fetch(\n    \"https://raw.githubusercontent.com/twindle-co/twindle/main/twindle-web/team_details/data.json\"\n  );\n  let data = await fetchData.json();\n  let users = data.users;\n  let html = \"\";\n  users.forEach((info) => (html += generateCard(info)));\n  cards.innerHTML = html;\n}\n\npushToDom();\n"
  },
  {
    "path": "twindle-cli/.gitignore",
    "content": "TwindleLibrary\nnodemailer.config.json\ngenerated-mock\n!src/renderer/main\njsonLibrary\ninput\n"
  },
  {
    "path": "twindle-cli/.prettierrc",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": false,\n  \"printWidth\": 100,\n  \"arrowParens\": \"always\"\n}\n"
  },
  {
    "path": "twindle-cli/README.md",
    "content": "## To start\r\n\r\n    Usage: -i <tweet id> -f <file format> -o <filename>\r\n\r\n```\r\nOptions:\r\n      --help     Show help                                             [boolean]\r\n      --version  Show version number                                   [boolean]\r\n  -i, --tweetId  First tweet's tweet id in of the twitter thread\r\n                                                             [string] [required]\r\n  -f, --format   Output file format\r\n                      [string] [choices: \"mobi\", \"epub\", \"pdf\"] [default: \"pdf\"]\r\n  -o, --output   Filename for the output file                [string] [required]\r\n```\r\n\r\nIt only supports epub & pdf for now.\r\n\r\n[![tutorial](https://img.youtube.com/vi/KWqNm7FBFcI/0.jpg)](https://www.youtube.com/watch?v=KWqNm7FBFcI).\r\n\r\n## Example\r\nTested on 8th Oct 2021 (Mac)\r\n```\r\nnode src/index.js -e https://fasterthanli.me/articles/frustrated-its-not-you-its-rust -s\r\n```\r\n\r\n\r\n## Info for developers\r\n\r\n### Testing\r\n\r\nRun `npm run test` to run tests. Run `npm run test:watch` to have the test watch. Also before commit, it will run tests and commit will fail if any tests are broken.\r\n\r\n### Logging\r\n\r\n`console.devLog` function should be used to log everything.\r\nIt is a wrapper around `console.log` which only prints the input when DEV environment var is set true\r\n\r\n### Sending mails\r\n\r\nThere are two options,\r\n\r\n- **nodemailer**: Sending attachments to normal mails, send to kindle working\r\n"
  },
  {
    "path": "twindle-cli/jsconfig.json",
    "content": "{\n  \"typeAcquisition\": {\n    \"include\": [\"jest\"]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/package.json",
    "content": "{\n  \"name\": \"twindle\",\n  \"version\": \"0.0.1\",\n  \"description\": \"This cli fetches a twitter thread and converts it into PDF, ePub, Mobi, etc\",\n  \"main\": \"src/index.js\",\n  \"scripts\": {\n    \"start:mock\": \"node src/index.js -m\",\n    \"start:thread\": \"node src/index.js -i 1323618091511607296\",\n    \"test\": \"jest\",\n    \"test:coverage\": \"jest --coverage\",\n    \"test:watch\": \"npm run test -- --watch\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/twindle-co/twindle-cli.git\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/twindle-co/twindle-cli/issues\"\n  },\n  \"homepage\": \"https://github.com/twindle-co/twindle-cli#readme\",\n  \"dependencies\": {\n    \"@mozilla/readability\": \"^0.4.1\",\n    \"date-fns\": \"^2.25.0\",\n    \"dotenv\": \"^10.0.0\",\n    \"epub-gen\": \"^0.1.0\",\n    \"handlebars\": \"^4.7.7\",\n    \"kleur\": \"^4.1.4\",\n    \"node-calibre\": \"^2.1.1\",\n    \"node-fetch\": \"^2.6.1\",\n    \"nodemailer\": \"^6.6.5\",\n    \"ora\": \"^5.3.0\",\n    \"prompt\": \"^1.2.0\",\n    \"puppeteer\": \"^10.4.0\",\n    \"showdown\": \"^1.9.1\",\n    \"svelte\": \"^3.43.1\",\n    \"twemoji\": \"13.1.0\",\n    \"yargs\": \"^17.2.1\"\n  },\n  \"devDependencies\": {\n    \"@types/jest\": \"^27.0.2\",\n    \"@types/node-fetch\": \"^3.0.2\",\n    \"hacker-news-api-types\": \"^1.0.1\",\n    \"jest\": \"^27.2.4\",\n    \"prettier\": \"^2.4.1\"\n  }\n}"
  },
  {
    "path": "twindle-cli/src/cli.js",
    "content": "const { format } = require(\"date-fns\");\nconst yargs = require(\"yargs\");\nconst { UserError } = require(\"./helpers/error\");\nconst { createLibraryIfNotExists } = require(\"./utils/library\");\nconst sendEmail = require(\"./utils/send-email\");\nconst { isValidEmail } = require(\"./utils/helpers\");\nconst { getEmailConfig, getBearerToken } = require(\"./utils/env\");\nconst { readFile } = require(\"fs\").promises;\nconst emailConfig = getEmailConfig();\n\nconst getCommandlineArgs = (processArgv) =>\n  yargs(processArgv)\n    .usage(\n      \"Usage: -i <tweet id or a comma separated list of tweet ids> -r <include replies by author of thread to other users>\" +\n        \" -u <user_id> -n <default_num_tweets>\" +\n        \" -f <file format - pdf | epub>\" +\n        \" -o <filename> -a<if o is not present a file name is calculated and this a text can be appended to it\" +\n        \" -s <send to kindle email| Optionally pass kindle email here>\"\n    )\n    .option({\n      f: {\n        alias: \"format\",\n        demandOption: false,\n        describe: \"Output file format\",\n        choices: [\"mobi\", \"epub\", \"pdf\"],\n        type: \"string\",\n        default: \"pdf\",\n      },\n      o: {\n        alias: \"outputFilename\",\n        demandOption: false,\n        describe: \"Filename for the output file\",\n        type: \"string\",\n      },\n      a: {\n        alias: \"appendToFilename\",\n        demandOption: false,\n        describe: \"Append string to the filename\",\n        type: \"string\",\n      },\n      i: {\n        alias: \"tweetId\",\n        demandOption: false,\n        describe: \"First tweet's tweet id in of the twitter thread\",\n        type: \"string\",\n      },\n      r: {\n        alias: \"includeReplies\",\n        demandOption: false,\n        describe: \"include replies by thread author\",\n        type: \"boolean\",\n        default: false,\n      },\n      s: {\n        alias: \"kindleEmail\",\n        demandOption: false,\n        describe:\n          \"Send document to your kindle email. Optionally pass kindle email here if not configured in .env file\",\n        type: \"string\",\n        default: emailConfig.kindleEmail,\n      },\n      m: {\n        alias: \"mock\",\n        demandOption: false,\n        describe: \"If set, will run in mock mode\",\n        type: \"boolean\",\n      },\n      ms: {\n        alias: \"mockSource\",\n        demandOption: false,\n        describe: \"To be used with mock flag\",\n        type: \"string\",\n        default: \"twitter\",\n      },\n      \"generate-mock\": {\n        alias: \"generateMock\",\n        demandOption: false,\n        describe: \"generates a mock json file of the current data\",\n        type: \"boolean\",\n        default: true,\n      },\n      p: {\n        alias: \"shouldUsePuppeteer\",\n        demandOption: false,\n        describe: \"Should use Puppeteer or not\",\n        type: \"boolean\",\n      },\n      u: {\n        alias: \"userId\",\n        demandOption: false,\n        describe:\n          \"The Twitter ID of the user whose timeline of recent tweets you are trying to read\",\n        type: \"string\",\n      },\n      n: {\n        alias: \"numTweetOrComments\",\n        demandOption: false,\n        describe: \"Used together with u/h option to specify the number of tweets/comments to be read\",\n        type: \"integer\",\n        default: 10,\n      },\n      g: {\n        alias: \"gitHubURL\",\n        demandOption: false,\n        describe: \"The README.md file of git Repo\",\n        type: \"string\",\n      },\n      h: {\n        alias: \"storyId\",\n        demandOption: false,\n        describe: \"Hackernews story ids\",\n        type: \"string\",\n      },\n\n      d: {\n        alias: \"numCommentLevels\",\n        demandOption: false,\n        describe:\n          \"Used together with h option to specify the number of levels of comments to be picked up\",\n        type: \"integer\",\n        default: 3,\n      },\n      e: {\n        alias: \"articleUrl\",\n        demandOption: false,\n        describe: \"URL to read from\",\n        type: \"string\",\n      },\n    }).argv;\n\n// Intends to do such things for one time for the user, like config creating, main outputdir creation\nfunction prepareCli() {\n  // creating the TwindleLibrary if not exists\n  createLibraryIfNotExists();\n}\n\nasync function getCommandLineObject() {\n  const {\n    format,\n    outputFilename,\n    tweetId,\n    includeReplies,\n    kindleEmail,\n    mock,\n    mockSource,\n    shouldUsePuppeteer,\n    appendToFilename,\n    userId,\n    numTweetOrComments,\n    generateMock,\n    gitHubURL,\n    storyId,\n    //numTopComments,\n    numCommentLevels,\n    articleUrl,\n  } = getCommandlineArgs(process.argv);\n  const cliObject = {};\n  appendFileFormat(cliObject, format);\n  appendOutputFileName(cliObject, outputFilename, appendToFilename);\n  appendKindleEmail(cliObject, kindleEmail);\n  appendTwitterSource(cliObject, tweetId, includeReplies, userId, numTweetOrComments);\n  await appendGithubSource(cliObject, gitHubURL);\n  appendHackernewsSource(cliObject, storyId, numTweetOrComments, numCommentLevels);\n  appendArticleSource(cliObject, articleUrl);\n  appendMock(cliObject, mock, mockSource);\n  validateCliObject(cliObject);\n  cliObject.generateMock = process.argv.includes(\"-generate-mock\") && generateMock;\n  return cliObject;\n}\n\nconst appendFileFormat = (cliObject, format) => {\n  if (format != \"pdf\" && format != \"epub\" && format != \"mobi\")\n    throw new UserError(\n      \"file-format-not-supported\",\n      \"Currently only pdf and epub formats are supported. Please choose one of the two\"\n    );\n  cliObject.format = format;\n  return cliObject;\n};\n\nconst appendOutputFileName = (cliObject, outputFilename, appendToFilename) => {\n  if (outputFilename || appendToFilename) {\n    cliObject.fileName = {};\n    if (outputFilename) cliObject.fileName.outputFilename = outputFilename;\n    else cliObject.fileName.appendToFilename = appendToFilename;\n  }\n  return cliObject;\n};\n\nconst appendKindleEmail = (cliObject, kindleEmail) => {\n  if (process.argv.includes(\"-s\")) {\n    if (!emailConfig.host || !emailConfig.senderEmail || !emailConfig.password)\n      throw new UserError(\n        \"mail-server-config-error\",\n        \"Please setup the credentials for the mail server to send the email to Kindle\"\n      );\n    if (!kindleEmail) {\n      throw new UserError(\n        \"empty-kindle-email\",\n        \"Pass your kindle email address with -s or configure it in the .env file\"\n      );\n    }\n\n    if (!isValidEmail(kindleEmail)) {\n      const errorMessage = !!process.argv[process.argv.indexOf(\"-s\") + 1]\n        ? \"Enter a valid email address\"\n        : \"Kindle Email configured in .env file is invalid\";\n      throw new UserError(\"invalid-email\", errorMessage);\n    }\n    cliObject.kindleEmail = kindleEmail;\n  }\n  return cliObject;\n};\n\nconst appendTwitterSource = (cliObject, tweetId, includeReplies, userId, numTweetOrComments) => {\n  if (process.argv.includes(\"-i\") && process.argv.includes(\"-u\"))\n    throw new UserError(\n      \"invalid-combination-of-twitter-params\",\n      \"Please choose either tweet ids or user ids\"\n    );\n  if (process.argv.includes(\"-i\") || process.argv.includes(\"-u\")) {\n    if (!getBearerToken())\n      throw new UserError(\n        \"bearer-token-not-provided\",\n        \"Please ensure that you have a .env file containing a value for TWITTER_AUTH_TOKEN\"\n      );\n\n    cliObject.dataSource = \"twitter\";\n    cliObject.twitter = {};\n    if (tweetId) {\n      if (process.argv.includes(\"-n\"))\n        throw new UserError(\n          \"invalid-combination-of-twitter-params\",\n          \"Cannot include -n for the tweet id option\"\n        );\n      cliObject.twitter.tweetId = tweetId;\n      cliObject.twitter.includeReplies = process.argv.includes(\"-r\") && includeReplies;\n    } else {\n      if (process.argv.includes(\"-r\"))\n        throw new UserError(\n          \"invalid-combination-of-twitter-params\",\n          \"Cannot include -r for the user id option\"\n        );\n      cliObject.twitter.userId = userId;\n      cliObject.twitter.numTweetOrComments = numTweetOrComments;\n    }\n  }\n  return cliObject;\n};\n\nconst appendGithubSource = async (cliObject, gitHubURL) => {\n  if (cliObject.dataSource && process.argv.includes(\"-g\")) {\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Cannot include -g together with twitter related params\"\n    );\n  }\n  if (process.argv.includes(\"-g\")) {\n    cliObject.dataSource = \"github\";\n    cliObject.github = {};\n    cliObject.github.githubURL = await validateGithubURL(gitHubURL);\n  }\n  return cliObject;\n};\n\nasync function validateGithubURL(gitHubURL) {\n  if (!(gitHubURL.toLowerCase().endsWith(\".md\") || gitHubURL.toLowerCase().endsWith(\".txt\")))\n    throw new UserError(\n      \"invalid-value-for-input-argument\",\n      \"Can only include an MD file or a txt file for -g parameter\"\n    );\n  if (gitHubURL.toLowerCase().endsWith(\".md\")) return gitHubURL;\n  else {\n    const reposText = await readFile(`${__dirname}/github/input/${gitHubURL}`, \"utf-8\");\n    return reposText.split(/\\r?\\n/).join(\",\");\n  }\n}\n\nconst appendHackernewsSource = (cliObject, storyId, numTopComments, numCommentLevels) => {\n  if (cliObject.dataSource && process.argv.includes(\"-h\")) {\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Cannot include -h together with twitter or github related params\"\n    );\n  }\n  if (process.argv.includes(\"-h\")) {\n    cliObject.dataSource = \"hackernews\";\n    cliObject.hackernews = {};\n    cliObject.hackernews.storyId = storyId;\n    cliObject.hackernews.numTopComments = numTopComments;\n    cliObject.hackernews.numCommentLevels = numCommentLevels;\n  }\n  return cliObject;\n};\n\nconst appendArticleSource = (cliObject, articleUrl) => {\n  if (cliObject.dataSource && process.argv.includes(\"-read\")) {\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Cannot include -read together with twitter or github or hackernews related params\"\n    );\n  }\n  if (process.argv.includes(\"-e\")) {\n    cliObject.dataSource = \"article\";\n    cliObject.article = {};\n    cliObject.article.articleUrl = articleUrl;\n  }\n  return cliObject;\n};\n\nconst appendMock = (cliObject, mock, mockSource) => {\n  if (cliObject.dataSource && process.argv.includes(\"-m\")) {\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Cannot include -m together with twitter or github or hackernews related params\"\n    );\n  }\n  if (cliObject.mock && !process.argv.includes(\"-ms\")) {\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Cannot use -m option without -ms which is the source for the mock option\"\n    );\n  }\n  if (process.argv.includes(\"-m\")) {\n    cliObject.mockSource = mockSource;\n  }\n};\n\nconst validateCliObject = (cliObject) => {\n  if (!cliObject.dataSource && !cliObject.mock)\n    throw new UserError(\n      \"invalid-combination-of-input-arguments\",\n      \"Either one of the sources information must be specified or the mock flag must be set, otherwise the application will not run\"\n    );\n};\n\nmodule.exports = {\n  getCommandlineArgs,\n  prepareCli,\n  getCommandLineObject,\n  validateGithubURL,\n};\n"
  },
  {
    "path": "twindle-cli/src/common.d.ts",
    "content": "declare namespace NodeJS {\n  export interface ProcessEnv {\n    /** The bearer token from Twitter Developers dashboard. Required */\n    TWITTER_AUTH_TOKEN: string;\n\n    /** Kindle email address to send the generated file to automatically */\n    KINDLE_EMAIL?: string;\n\n    /** Host of your smtp server for ex: `smtp.gmail.com`, if its a G-Mail account */\n    HOST?: string;\n    /** Port of the server. `465` for Gmail */\n    PORT?: string;\n    /** The email from which to send to kindle. Must be whitelisted in your Amazon Devices Dashboard */\n    EMAIL?: string;\n    /** Password to the account from which to send the mail */\n    PASS?: string;\n\n    /** Whether to show descriptive error messages or short, prettified ones */\n    DEV?: \"true\" | \"false\";\n\n    /** Scrape tweets or not */\n    SCRAPE_TWEETS?: \"true\" | \"false\";\n  }\n}\ninterface Console {\n  /** Logs only when env var DEV === \"true\" */\n  devLog: (...data: any[]) => void;\n}\n"
  },
  {
    "path": "twindle-cli/src/env.js",
    "content": "//  possibly include env related errors in a single location here\nconst { UserError } = require(\"../../helpers/error\");\n\nconst twitterAuthToken = process.env.TWITTER_AUTH_TOKEN;\n\nif (!twitterAuthToken)\n  throw new UserError(\n    \"bearer-token-not-provided\",\n    \"Please ensure that you have a .env file containing a value for TWITTER_AUTH_TOKEN\"\n  );\n\nconst BEARER_TOKEN = \"Bearer \" + twitterAuthToken;\nmodule.exports = { BEARER_TOKEN };\n"
  },
  {
    "path": "twindle-cli/src/fileOpener.js",
    "content": "const childProcess = require(\"child_process\");\n\nconst openFile = function (outputFilePath) {\n  const result = childProcess.exec(command() + \" \" + `${outputFilePath}`);\n\n  return result;\n};\n\nfunction command() {\n  switch (process.platform) {\n    case \"darwin\":\n      return \"open\";\n    case \"win64\":\n      return \"start\";\n    default:\n      return \"xdg-open\";\n  }\n}\n\nmodule.exports = { openFile };\n"
  },
  {
    "path": "twindle-cli/src/github/githubparse/app.js",
    "content": "const { convertHTML } = require(\"./convert\");\nconst { jsonfetchData } = require(\"./jsonFetchData\");\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst { UserError } = require(\"../../helpers/error\");\n\nasync function getHtml(urlData) {\n  const dataArray = [];\n  var data = [];\n  if (/[,\\-]/.test(urlData)) {\n    dataArray.push(urlData.split(\",\"));\n  }\n  for (let element of dataArray[0]) {\n    const url = new URL(element);\n    const urlExtension = path.extname(url.pathname);\n    if (urlExtension !== \".md\") {\n      throw new UserError(\"invalid-file-format-github\", \"Can currently only read MD files\");\n    }\n    const readmeFileName = path.basename(url.pathname, \".md\");\n    const gituser = url.pathname.split(\"/\")[1];\n    const repoName = url.pathname.split(\"/\")[2];\n    const branch = url.pathname.split(\"/\")[4];\n    const fileName = url.pathname.substring(url.pathname.lastIndexOf(\"/\") + 1);\n    const repoURL = `https://api.github.com/repos/${gituser}/${repoName}`;\n    const repoResponse = {\n      common: await jsonfetchData(repoURL),\n      htmlValue: await convertHTML(element),\n    };\n    repoResponse.common.branch = branch;\n    repoResponse.common.fileName = fileName;\n\n    data.push(repoResponse);\n  }\n  //fs.writeFileSync(`${__dirname}/jsonLibrary/test.json`, JSON.stringify(data,null,2) )\n\n  return data;\n}\n\n//getHtml(\"https://github.com/ryanmcdermott/clean-code-javascript/blob/master/README.md , https://github.com/kentcdodds/react-fundamentals/blob/main/src/exercise/02.md\")\n\nmodule.exports = { getHtml };\n"
  },
  {
    "path": "twindle-cli/src/github/githubparse/convert.js",
    "content": "const showdown = require(\"showdown\");\nconst path = require(\"path\");\nconst fetch = require(\"node-fetch\");\nconst converter = new showdown.Converter();\n\nfunction convertHTML(fetchURL) {\n  var url = new URL(fetchURL);\n  const extension = path.extname(url.pathname);\n  if (extension !== \".md\") {\n    return console.log(\"Please enter another URL as it does not have markdown extension\");\n  }\n  const gituser = url.pathname.split(\"/\")[1];\n  const repoName = url.pathname.split(\"/\")[2];\n  const branch = url.pathname.split(\"/\")[4];\n  const rawFile = fetchURL\n    .replace(\"github.com\", \"raw.githubusercontent.com\")\n    .replace(\"/blob/\", \"/\");\n  const replaceURL = `https://raw.githubusercontent.com/${gituser}/${repoName}/${branch}/`;\n  return fetch(rawFile)\n    .then((response) => response.text())\n    .then((body) => {\n      body = body.replace(/\\.\\//g, replaceURL);\n      const dataJSON = converter.makeHtml(body);\n\n      return dataJSON;\n    })\n    .catch((e) => console.log(e));\n}\n\nmodule.exports = { convertHTML };\n"
  },
  {
    "path": "twindle-cli/src/github/githubparse/jsonFetchData.js",
    "content": "const fetch = require(\"node-fetch\");\nconst { formatTimestamp } = require(\"../../utils/date\");\n\nconst jsonfetchData = (url) =>\n  fetch(url)\n    .then((res) => res.json())\n    .then((jsonData) => {\n      const datajson = {\n        repoName: jsonData.name,\n        created_at: formatTimestamp(jsonData.created_at),\n        user: {\n          username: jsonData.owner.login,\n          profile_image_url: jsonData.owner.avatar_url,\n        },\n      };\n\n      return datajson;\n    });\n\nmodule.exports = { jsonfetchData };\n"
  },
  {
    "path": "twindle-cli/src/hacker-news/code.js",
    "content": "// @ts-check\nconst cheerio = require(\"cheerio\");\nconst fetch = require(\"node-fetch\").default;\nconst { formatTimestamp } = require(\"../utils/date\");\n\n/**\n * @typedef {{\n *  common: {\n *    id: number;\n *    created_at: string;\n *    title: string;\n *    url: string;\n *    siteTitle: string;\n *    image: string;\n *    user: {\n *      username: string;\n *      desc: string;\n *      karma: number;\n *    }\n *   numComments: number;\n *   score: number;\n * }\n * kids: number[];\n * comments: any[];\n * }} Story\n */\n\n/**\n * @param {string} url\n */\nasync function fetchItem(url) {\n  const response = await fetch(url);\n\n  if (!response.ok) {\n    throw new Error(`HTTP error! status: ${response.status}`);\n  }\n  const result = await response.json();\n\n  return result;\n}\n\n/**\n *\n * @param {string} storyId\n * @param {number} numTopComments\n * @param {number} numCommentLevels\n */\nasync function getStories(storyId, numTopComments, numCommentLevels) {\n  let ids = storyId.split(\",\");\n\n  /**\n   * @type {Story[]}\n   */\n  let stories = [];\n\n  /** @type {import('hacker-news-api-types').IItem[]} */\n  const filter = [];\n\n  for (let id of ids) {\n    /** @type {import('hacker-news-api-types').IItem} */\n    const filterData = await fetch(\n      `https://hacker-news.firebaseio.com/v0/item/${id}.json`\n    ).then((req) => req.json());\n\n    filter.push(filterData);\n  }\n\n  for (let result of filter) {\n    const story = await getStoryObject(result);\n    await appendComments(story, numTopComments, 1, numCommentLevels);\n    stories.push(story);\n  }\n\n  return stories;\n}\n\n/**\n *\n * @param {Story} parent\n * @param {number} numTopComments\n * @param {number} iterationLevel\n * @param {number} numCommentLevels\n */\nasync function appendComments(parent, numTopComments, iterationLevel, numCommentLevels) {\n  if (iterationLevel <= numCommentLevels && parent.kids && parent.kids.length > 0) {\n    const filter = await getCommentResponses(parent, numTopComments);\n\n    for (let result of filter) {\n      let comment = await getCommentObject(result, iterationLevel);\n      // @ts-ignore\n      comment = await appendComments(comment, numTopComments, iterationLevel + 1, numCommentLevels);\n      comment.index = parent.comments.length;\n      parent.comments.push(comment);\n    }\n  }\n\n  return parent;\n}\n\n/**\n * @param {Story} parent\n * @param {number} numTopComments\n */\nasync function getCommentResponses(parent, numTopComments) {\n  if (parent.kids.length > 0) {\n    const kids =\n      parent.kids.length <= numTopComments ? parent.kids : parent.kids.slice(0, numTopComments);\n\n    const requests = kids.map((id) =>\n      fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)\n    );\n\n    while (parent.kids.length) {\n      parent.kids.shift();\n    }\n\n    const response = await Promise.all(requests);\n\n    /** @type {import('hacker-news-api-types').IItem[]} */\n    let filter = await Promise.all(response.map((res) => res.json()));\n\n    filter = filter.filter((result) => !result.deleted);\n\n    if (filter.length == numTopComments || parent.kids.length == 0) {\n      return filter;\n    } else {\n      let newFilter = await getCommentResponses(parent, numTopComments - filter.length);\n      newFilter.forEach((result) => filter.push(result));\n      return filter;\n    }\n  }\n}\n\n/**\n *\n * @param {import('hacker-news-api-types').IItem} result\n */\nasync function getStoryObject(result) {\n  const story = {};\n\n  story.common = {};\n\n  story.common.id = result.id;\n  story.common.created_at = formatTimestamp(new Date(result.time * 1000) + \"\");\n  story.common.title = result.title;\n  story.common.url = result.url;\n\n  const response = await fetch(result.url);\n  const responseText = await response.text();\n\n  const $ = cheerio.load(responseText);\n\n  let title = $('meta[property=\"og:title\"]').attr(\"content\");\n  if (!title) title = $(\"title\").attr(\"content\");\n\n  story.common.siteTitle = title;\n\n  let image = $('meta[property=\"og:image\"]').attr(\"content\");\n\n  if (!image) image = $(\"image\").attr(\"content\");\n  if (!image) image = \"https://news.ycombinator.com/favicon.ico\";\n\n  story.common.image = image;\n  story.common.user = await getUser(result.by);\n  story.common.numComments = result.descendants;\n  story.common.score = result.score;\n\n  story.kids = result.kids;\n  story.comments = [];\n\n  return story;\n}\n\n/**\n *\n * @param {import(\"hacker-news-api-types\").IItem} result\n * @param {number} iterationLevel\n */\nasync function getCommentObject(result, iterationLevel) {\n  const comment = {};\n\n  comment.id = result.id;\n  comment.username = result.by;\n  comment.kids = result.kids;\n  comment.parent = result.parent;\n  comment.text = result.text;\n  comment.created_at = formatTimestamp(new Date(result.time * 1000) + \"\");\n  comment.level = iterationLevel;\n  comment.comments = [];\n  return comment;\n}\n\n/**\n * Takes in author ID, churns out data about the author\n * @param {string} author\n */\nasync function getUser(author) {\n  const url = `https://hacker-news.firebaseio.com/v0/user/${author}.json?print=pretty`;\n\n  /** @type {import('hacker-news-api-types').IUser} */\n  const result = await fetchItem(url);\n\n  return { username: result.id, desc: result.about, karma: result.karma };\n}\n\nmodule.exports = { getStories, getUser };\n"
  },
  {
    "path": "twindle-cli/src/hacker-news/hn.test.js",
    "content": "const { getUser } = require(\"./code\");\n\n//1\ntest(\"fetch function is returning json\", async () => {\n  const data = await getUser(\"pg\");\n  expect(data).toMatchObject(/{}/);\n});\n\n//2\ntest(\"tested for fetch error \", async () => {\n  try {\n    await getUser(\"pg\");\n  } catch (e) {\n    expect(e).toEqual({\n      error: \"User with pg not found.\",\n    });\n  }\n});\n"
  },
  {
    "path": "twindle-cli/src/helpers/error.js",
    "content": "// @ts-check\n\nclass UserError extends Error {\n  /**\n   *\n   * @param {string} message Message to show to the user\n   * @param {string} name Code of the error\n   */\n  constructor(name, message) {\n    super(message);\n\n    this.name = name;\n  }\n}\n\nmodule.exports = { UserError };\n"
  },
  {
    "path": "twindle-cli/src/helpers/logger.js",
    "content": "function devLog(...args) {\n  if (process.env.DEV === \"true\") {\n    console.log(...args);\n  }\n}\n\n// this adds a new function to console which only prints the input\n// when environment variable DEV=true\nconsole.devLog = devLog;\n"
  },
  {
    "path": "twindle-cli/src/index.js",
    "content": "require(\"./helpers/logger\");\nrequire(\"dotenv\").config();\nconst { getCommandLineObject, prepareCli } = require(\"./cli\");\nconst Renderer = require(\"./renderer\");\nconst { getTweetsFromUser, getTweetsFromThreads } = require(\"./twitter\");\nconst { getOutputFilePath } = require(\"./utils/path\");\nconst { sendToKindle } = require(\"./utils/send-to-kindle\");\nconst { red, cyan } = require(\"kleur\");\nconst { formatLogColors } = require(\"./utils/helpers\");\nconst { formatTimestamp } = require(\"./utils/date\");\nconst spinner = require(\"./spinner\");\nconst { writeFile, mkdir } = require(\"fs\").promises;\nconst { getHtml } = require(\"./github/githubparse/app\");\nconst { getStories } = require(\"./hacker-news/code\");\nconst { readURL } = require(\"./readability\");\nconst { getBearerToken } = require(\"./utils/env\");\nconst token = getBearerToken();\nconst { openFile } = require(\"./fileOpener.js\");\n\nasync function main() {\n  try {\n    prepareCli();\n    spinner.start();\n\n    let cliObject = await getCommandLineObject();\n    //console.log(cliObject);\n    const data = await getDataFromSource(cliObject);\n    //console.log(JSON.stringify(data));\n\n    let outputFilename =\n      cliObject.fileName && cliObject.fileName.outputFilename\n        ? cliObject.fileName.outputFilename\n        : \"\";\n\n    if (!outputFilename) outputFilename = calculateFileName(cliObject, data);\n\n    await writeToMockFile(cliObject, outputFilename, data);\n\n    const outputFilePath = getOutputFilePath(outputFilename, cliObject.format);\n\n    await Renderer.render(data, cliObject.dataSource, cliObject.format, outputFilePath);\n\n    if (cliObject.kindleEmail) {\n      console.devLog(\"sending to kindle\", cliObject.kindleEmail);\n      await sendToKindle(cliObject.kindleEmail, outputFilePath);\n    }\n\n    const [fileName] = outputFilePath.split(\"/\").reverse();\n    \n    spinner.succeed(\n      \"Your \" + cyan(\"data\") + \" saved into \" + formatLogColors[cliObject.format](fileName)\n    );\n\n    openFile(outputFilePath);\n\n    //console.log(\"Your \" + cyan(\"tweets\") + \" are saved into \" + formatLogColors[format](fileName));\n  } catch (e) {\n    if (process.env.DEV === \"true\") {\n      console.error(e);\n    } else {\n      console.log(`${red(e.name)}: ${e.message}`);\n    }\n  }\n\n  // If not for this line, the script never finishes\n  process.exit();\n}\n\nasync function getDataFromSource(cliObject) {\n  if (cliObject.dataSource) {\n    if (cliObject.dataSource === \"github\") return getDataFromGithub(cliObject.github);\n    else if (cliObject.dataSource === \"twitter\") return getTweets(cliObject.twitter);\n    else if (cliObject.dataSource === \"hackernews\")\n      return getDataFromHackernews(cliObject.hackernews);\n    else if (cliObject.dataSource === \"article\") return getDataFromArticle(cliObject.article);\n  }\n  if (cliObject.mock) {\n    return getDataFromMock(cliObject.mock);\n  }\n}\n\n/**\n *\n * @param {Object} param\n * @param {string} param.tweetId\n * @param {boolean} param.mock\n * @param {boolean} param.shouldUsePuppeteer\n * @param {string} param.userId\n * @param {number} param.numTweetOrComments\n */\nasync function getTweets(cliObject) {\n  /** @type {CustomTweetsObject[]} */\n  let tweets;\n\n  if (cliObject.userId) {\n    tweets = await getTweetsFromUser(cliObject.userId, token);\n\n    if (tweets[0].data.length > cliObject.numTweetOrComments) {\n      tweets[0].data = tweets[0].data.slice(0, cliObject.numTweetOrComments);\n      tweets[0].common.count = tweets[0].data.length;\n    }\n\n    return tweets;\n  }\n\n  tweets = await getTweetsFromThreads(cliObject.tweetId, cliObject.includeReplies, token);\n  return tweets;\n}\n\nasync function getDataFromGithub({ githubURL }) {\n  return await getHtml(githubURL);\n}\n\nasync function getDataFromHackernews({ storyId, numTopComments, numCommentLevels }) {\n  return await getStories(storyId, numTopComments, numCommentLevels);\n}\n\nasync function getDataFromArticle({ articleUrl }) {\n  return await readURL(articleUrl);\n}\n\nfunction calculateFileName(cliObject, data) {\n  if (cliObject.dataSource == \"twitter\") {\n    return calculateFileNameForTwitter(cliObject, data);\n  } else if (cliObject.dataSource == \"github\") {\n    return calculateFileNameForGitHub(cliObject, data);\n  } else if (cliObject.dataSource == \"hackernews\") {\n    return calculateFileNameForHackernews(cliObject, data);\n  } else if (cliObject.dataSource == \"article\") {\n    return calculateFileNameForArticle(cliObject, data);\n  }\n}\n\nfunction calculateGenericFileName(cliObject, component1, component2) {\n  const intelligentOutputFileName = `${component1 || \"twindle\"}-${component2 || \"thread\"}${\n    cliObject.appendToFilename ? \"-\" + cliObject.appendToFilename : \"\"\n  }`;\n  return intelligentOutputFileName;\n}\n\nfunction calculateFileNameForTwitter(cliObject, data) {\n  let username = (\n    (data[0] && data[0].common && data[0].common.user && data[0].common.user.username) ||\n    \"\"\n  ).replace(\"@\", \"\");\n\n  let date =\n    data[0] && data[0].common && data[0].common.created_at.replace(/,/g, \"\").replace(/ /g, \"-\");\n  return calculateGenericFileName(cliObject, username, date);\n}\n\nfunction calculateFileNameForGitHub(cliObject, data) {\n  let username = (\n    data[0] &&\n    data[0].common &&\n    data[0].common.user &&\n    data[0].common.user.username\n  ).replace(\"@\", \"\");\n  let repoName = data[0] && data[0].common && data[0].common.repoName;\n  let fileName = data[0] && data[0].common && data[0].common.fileName;\n  fileName = fileName.substring(0, fileName.indexOf(\".\"));\n  let date = new Date();\n  date = formatTimestamp(date).replace(/,/g, \"\").replace(/ /g, \"-\");\n  return calculateGenericFileName(cliObject, `${username}-${repoName}-${fileName}`, date);\n}\n\nfunction calculateFileNameForHackernews(cliObject, data) {\n  let username = (\n    data[0] &&\n    data[0].common &&\n    data[0].common.user &&\n    data[0].common.user.username\n  ).replace(\"@\", \"\");\n  let title = (data[0] && data[0].common && data[0].common.title)\n    .replace(/\\W/g, \"-\")\n    .substring(0, 10);\n  let date =\n    data[0] && data[0].common && data[0].common.created_at.replace(/,/g, \"\").replace(/ /g, \"-\");\n  return calculateGenericFileName(cliObject, `${username}-${title}`, date);\n}\n\nfunction calculateFileNameForArticle(cliObject, data) {\n  let title = (data[0] && data[0].title).replace(/\\W/g, \"-\").substring(0, 10);\n  let date = new Date();\n  date = formatTimestamp(date).replace(/,/g, \"\").replace(/ /g, \"-\");\n  return calculateGenericFileName(cliObject, `${title}`, date);\n}\n\nasync function writeToMockFile(cliObject, outputFilename, data) {\n  if (cliObject.generateMock) {\n    try {\n      await mkdir(\"./generated-mock\");\n    } catch {}\n\n    // Create mock file with the appropriate name\n    await writeFile(\n      `./generated-mock/@CUSTOM-OUTPUT_${outputFilename}.json`,\n      JSON.stringify(data, null, 2)\n    );\n  }\n}\n// Execute it\nmain();\n"
  },
  {
    "path": "twindle-cli/src/readability/index.js",
    "content": "const { Readability } = require(\"@mozilla/readability\");\nconst image = require(\"../utils/image\");\nconst JSDOM = require(\"jsdom\").JSDOM;\nconst { UserError } = require(\"../helpers/error\");\n\nasync function readURL(testUrl) {\n  let urls = testUrl.split(\",\");\n  let threads = [];\n  for (let url of urls) {\n    let windowDocument = await getJSDOM(url);\n    let article = getParsedArticle(windowDocument);\n    if(article === null)\n      throw new UserError(\n        \"mozilla-readability-error\",\n        \"Failed to load article from page\"\n      );\n    threads.push(getArticleJSON(article, url, windowDocument));\n  }\n  return threads;\n}\n\nasync function getJSDOM(url) {\n  let doc = await JSDOM.fromURL(url, {});\n  return doc.window.document;\n}\n\nfunction getParsedArticle(windowDocument) {\n  let reader = new Readability(windowDocument);\n  return reader.parse();\n}\n\nfunction getArticleJSON(article, url, windowDocument) {\n  let articleJSON = {};\n  articleJSON.title = article.title;\n  articleJSON.url = url;\n  articleJSON.urldomain = getURLDomain(url);\n  articleJSON.html = article.content;\n  if (article.byline) articleJSON.author = article.byline;\n  let imageTag = Array.from(windowDocument.head.getElementsByTagName(\"meta\")).filter(\n    (element) => element.getAttribute(\"property\") === \"og:image\"\n  )[0];\n  if (imageTag) articleJSON.image = imageTag.getAttribute(\"content\");\n  return articleJSON;\n}\n\nfunction getURLDomain(url) {\n  let urldetails = url.substring(url.indexOf(\"://\") + 3);\n  return urldetails.substring(0, urldetails.indexOf(\"/\"));\n}\n\nmodule.exports = { readURL };\n"
  },
  {
    "path": "twindle-cli/src/renderer/epub/epub.js",
    "content": "const epub = require(\"epub-gen\");\n\nconst options = {\n  title: \"The Hello World\",\n  author: \"Hello MacWorld\",\n  content: [\n    {\n      title: \"Chapter 1: First Hello\",\n      data: `<p>\n       Lorem Ipsum dolor sit amet, consectetur adipsicing elite. Got it? Neither did we. In the publishing and design industries, “lorem ipsum” is used as dummy text in visual designs. Using placeholder copy like this helps designers and clients alike focus on layout, imagery, typography, and design rather than on the actual wording of content.\n\n       Dummy copy is great, but what’s with all the Latin? Turns out, the original Lorem Ipsum comes from bits and pieces of Cicero’s De Finibus bonorum et Malorum (On the Ends of Goods and Evils).\n       </p>`,\n    },\n  ],\n};\n\nfunction generateEpub(directory) {\n  return new epub(options, directory);\n}\n\nmodule.exports = { generateEpub };\n"
  },
  {
    "path": "twindle-cli/src/renderer/epub/index.js",
    "content": "const { renderTemplate } = require(\"./render-template\");\nconst { join } = require(\"path\");\nconst Epub = require(\"epub-gen\");\n\nconst createOptions = ({ title, author, html, tocPath, css }) => ({\n  title,\n  author,\n  cover: join(__dirname, \"..\", \"main\", \"resources\", \"twindle_logo.jpg\"),\n  content: [{ title: title, data: html }],\n  appendChapterTitles: false,\n  verbose: false,\n  tocTitle: \"Contents\",\n  publisher: \"Twindle\",\n  customHtmlTocTemplatePath: tocPath,\n  css,\n});\n\n/**\n * @param {CustomTweetsObject[]} srcData\n * @param {string} src\n * @param {string} outputPath\n */\nasync function generateEpub(srcData, src, outputPath) {\n  const parameter = { threads: srcData };\n  const optionDetails = await renderTemplate(parameter, src);\n  const options = createOptions(optionDetails);\n  try {\n    const book = new Epub(options, outputPath).promise;\n    await book;\n  } catch (error) {\n    console.log(error);\n  }\n}\n\nmodule.exports = { generateEpub };\n"
  },
  {
    "path": "twindle-cli/src/renderer/epub/render-template.js",
    "content": "// @ts-check\nconst { render } = require(\"../main\");\nconst { renderTOC } = require(\"../main/toc\");\nconst { writeFile, readFile } = require(\"fs\").promises;\nconst { tmpdir } = require(\"os\");\nconst { join } = require(\"path\");\nconst hbs = require(\"handlebars\");\n\n/**\n * Renders the html template with the given data and returns the html string\n * @param {CustomTweetsObject} data\n * @param {string} src\n */\nasync function renderTemplate(data, src) {\n  if (src == \"twitter\") return await renderTwitterTemplate(data);\n  else if (src == \"github\") return await renderGithubTemplate(data);\n  else if (src == \"hackernews\") return await renderHackernewsTemplate(data);\n  else if (src == \"article\") return await renderArticleTemplate(data);\n}\n\nasync function renderTwitterTemplate(data) {\n  // const css = getCSS(\"epub\");\n\n  // renders the html template with the given data\n  let { html, css } = render(data);\n\n  html = `<!doctype html> \n          <html> \n            <head>\n              <meta charset=\"UTF-8\" /\n              <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n              <style>${css.code}</style>\n            </head>\n            <body>\n              ${html}\n            </body>\n          </html>\n`;\n\n  // renders the html template with the given data\n  const { html: tocContent } = renderTOC(data);\n\n  const tempPath = join(tmpdir(), `toc.html`);\n\n  await writeFile(tempPath, tocContent, \"utf-8\");\n\n  console.devLog(\"toc saved to \", tempPath);\n\n  const authors = [];\n  for (let thread of data.threads) {\n    if (thread.common && thread.common.user && thread.common.user.name)\n      authors.push(thread.common.user.name);\n  }\n  const authorNames = authors.join(\",\");\n  const optionDetails = {\n    title: authorNames + \"'s Thread\",\n    author: authorNames,\n    html,\n    tocPath: tempPath,\n  };\n  return optionDetails;\n}\n\nasync function renderGithubTemplate(data) {\n  const css = await readFile(`${__dirname}/../main/github/style.css`, \"utf-8\");\n\n  const reposHtml = await readFile(`${__dirname}/../main/github/repos-partial.hbs`, \"utf-8\");\n  const userInfohtml = await readFile(`${__dirname}/../main/github/user-info-partial.hbs`, \"utf-8\");\n  const repohtml = await readFile(`${__dirname}/../main/github/repo-partial.hbs`, \"utf-8\");\n  hbs.registerPartial(\"user-info-partial\", userInfohtml);\n  hbs.registerPartial(\"repo-partial\", repohtml);\n\n  const reposTemplate = hbs.compile(reposHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const threadContent = reposTemplate(data.threads);\n\n  const tocHtml = await readFile(`${__dirname}/../main/github/toc-template.hbs`, \"utf-8\");\n\n  // creates the Handlebars template object\n  const tocTemplate = hbs.compile(tocHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const tocContent = tocTemplate(data.threads);\n\n  const tempPath = join(tmpdir(), `toc.html`);\n  await writeFile(tempPath, tocContent, \"utf-8\");\n  console.devLog(\"toc saved to \", tempPath);\n\n  const authors = [];\n  for (let thread of data.threads) {\n    if (thread.common && thread.common.user && thread.common.user.username)\n      authors.push(thread.common.username);\n  }\n  const authorNames = authors.join(\",\");\n  const optionDetails = {\n    title: authorNames + \"'s Repositories\",\n    author: authorNames,\n    html: threadContent,\n    css,\n    tocPath: tempPath,\n  };\n  return optionDetails;\n}\n\nasync function renderHackernewsTemplate(data) {\n  const css = await readFile(`${__dirname}/../main/hackernews/style.css`, \"utf-8\");\n\n  const articlesHtml = await readFile(\n    `${__dirname}/../main/hackernews/articles-partial.hbs`,\n    \"utf-8\"\n  );\n  const commonInfoHtml = await readFile(\n    `${__dirname}/../main/hackernews/common-info-partial.hbs`,\n    \"utf-8\"\n  );\n  const commentHtml = await readFile(\n    `${__dirname}/../main/hackernews/comment-partial.hbs`,\n    \"utf-8\"\n  );\n  hbs.registerPartial(\"common-info-partial\", commonInfoHtml);\n  hbs.registerPartial(\"comment-partial\", commentHtml);\n  hbs.registerHelper(\"levelcalculator\", function (comment) {\n    if (comment.level - 1 == 0 && comment.index == 0) return \"\";\n    else if (comment.level - 1 == 0 && comment.index != 0) {\n      if (comment.comments.length > 0) return \"style='page-break-before:always;'\";\n      else return \"style='border-top: 1px solid #ddd;'\";\n    } else if (comment.level - 1 > 0) return \"style='padding-left:35px'\";\n  });\n\n  const articlesTemplate = hbs.compile(articlesHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const threadContent = articlesTemplate(data.threads);\n\n  const tocHtml = await readFile(`${__dirname}/../main/hackernews/toc-template.hbs`, \"utf-8\");\n\n  // creates the Handlebars template object\n  const tocTemplate = hbs.compile(tocHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const tocContent = tocTemplate(data.threads);\n\n  const tempPath = join(tmpdir(), `toc.html`);\n  await writeFile(tempPath, tocContent, \"utf-8\");\n  console.devLog(\"toc saved to \", tempPath);\n\n  const authors = [];\n  for (let thread of data.threads) {\n    if (thread.common && thread.common.user && thread.common.user.username)\n      authors.push(thread.common.username);\n  }\n  const authorNames = authors.join(\",\");\n  const optionDetails = {\n    title: authorNames + \"'s articles\",\n    author: authorNames,\n    html: threadContent,\n    css,\n    tocPath: tempPath,\n  };\n  return optionDetails;\n}\n\nasync function renderArticleTemplate(data) {\n  const css = await readFile(`${__dirname}/../main/article/style.css`, \"utf-8\");\n  const articlesHtml = await readFile(`${__dirname}/../main/article/articles-partial.hbs`, \"utf-8\");\n\n  const articlesTemplate = hbs.compile(articlesHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const threadContent = articlesTemplate(data.threads);\n\n  const tocHtml = await readFile(`${__dirname}/../main/article/toc-template.hbs`, \"utf-8\");\n\n  // creates the Handlebars template object\n  const tocTemplate = hbs.compile(tocHtml, {\n    strict: true,\n  });\n  // renders the html template with the given data\n  const tocContent = tocTemplate(data.threads);\n\n  const tempPath = join(tmpdir(), `toc.html`);\n  await writeFile(tempPath, tocContent, \"utf-8\");\n  console.devLog(\"toc saved to \", tempPath);\n\n  const authors = [];\n  for (let thread of data.threads) {\n    if (thread.author) authors.push(thread.author);\n  }\n  const authorNames = authors.join(\",\");\n  const optionDetails = {\n    title: authorNames + \"'s articles\",\n    author: authorNames,\n    html: threadContent,\n    tocPath: tempPath,\n  };\n  return optionDetails;\n}\nmodule.exports = { renderTemplate };\n"
  },
  {
    "path": "twindle-cli/src/renderer/index.js",
    "content": "const { generateEpub } = require(\"./epub/index\");\nconst { generatePDF } = require(\"./pdf\");\nconst spinner = require(\"../spinner\");\nconst { generateMobi } = require(\"./mobi\");\n\nconst render = async (data, src, format, outputFilePath) => {\n  switch (format) {\n    case \"pdf\":\n      return generatePDF(data, src, outputFilePath);\n    case \"epub\":\n      return generateEpub(data, src, outputFilePath);\n    case \"mobi\":\n      return generateMobi(data, src, outputFilePath);\n    default:\n      spinner.fail(\"Error: This renderer is not implemented yet\");\n    //console.error(\"Error: This renderer is not implemented yet\");\n  }\n};\n\nmodule.exports = {\n  render,\n};\n"
  },
  {
    "path": "twindle-cli/src/renderer/md/README.md",
    "content": "# Markdown related changes\n"
  },
  {
    "path": "twindle-cli/src/renderer/md/index.js",
    "content": "const markdown = (name, tweet, url, username) => {\n  return \"### \" + tweet + \"\\n\" + \"![img](\" + url + \")\" + \"\\n\" + name + \" | \" + username;\n};\nconsole.devLog(\n  markdown(\n    \"naval\",\n    \"How to Get Rich (without getting lucky)\",\n    \"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\",\n    \"@naval\"\n  )\n);\n"
  },
  {
    "path": "twindle-cli/src/renderer/mobi/README.md",
    "content": "# Mobi Creation\n\n## How we did it?\n\nWe're using the `calibre`'s cli tool to convert epub to mobi.\n\n## Requirements\n\n`calibre cli tools` need to be in users `PATH`, otherwise the conversion can't be done. It would be nice to create a tutorial (article/video) on how to add calibre to `PATH`\n"
  },
  {
    "path": "twindle-cli/src/renderer/mobi/index.js",
    "content": "const { Calibre } = require(\"node-calibre\");\nconst { generateEpub } = require(\"../epub\");\nconst { tmpdir } = require(\"os\");\nconst { copyFile, unlink } = require(\"fs\").promises;\nconst { join } = require(\"path\");\n\n/**\n * @param {CustomTweetsObject[]} srcData\n * @param {string} src\n * @param {string} outputPath\n */\nasync function generateMobi(srcData, src, outputPath) {\n  // first creating a epub in the temp folder\n  const tempEpubPath = join(tmpdir(), \"temp_epub.epub\");\n  await generateEpub(srcData, src, tempEpubPath);\n\n  // Converting the epub to mobi\n  // Create Calibre instance\n  const calibre = new Calibre();\n  const tempMobiPath = await calibre.ebookConvert(tempEpubPath, \"mobi\");\n\n  // copying the mobi file to TwindleLibrary\n  await copyFile(tempMobiPath, outputPath);\n\n  // deleting the temp files\n  await unlink(tempMobiPath);\n  await unlink(tempEpubPath);\n}\n\nmodule.exports = { generateMobi };\n"
  },
  {
    "path": "twindle-cli/src/renderer/pdf/create-pdf.js",
    "content": "// @ts-check\nconst puppeteer = require(\"puppeteer\");\n\nfunction footerMarkup() {\n  return `\n  <div class=\"footer\" style=\"width: 100%;font-size: 10px !important;display: flex;justify-content: center;\">\n    <span>\n      <span style=\"font-size: 10px !important;\" class=\"pageNumber\"></span>\n        of\n      <span style=\"font-size: 10px !important;\" class=\"totalPages\"></span>\n    </span>\n  </div>\n  `;\n}\n\n/**\n * Creates a pdf document from htmlContent and saves it to outputPath\n * @param {string} outputPath\n * @param {string} htmlContent\n */\nasync function createPdf(outputPath, htmlContent) {\n  try {\n    // launches a headless puppeteer browser instance and opens a new page\n    const browser = await puppeteer.launch({\n      args: [\"--no-sandbox\"],\n      headless: true,\n    });\n\n    const page = await browser.newPage();\n\n    // sets the html of the page to htmlContent argument\n    await page.setContent(htmlContent);\n\n    // Prints the html page to pdf document and saves it to given outputPath\n    await page.emulateMediaType(\"print\");\n    await page.addStyleTag({\n      content: \"@page { size: auto; }\",\n    });\n    await page.pdf({\n      path: outputPath,\n      format: \"A5\",\n      margin: {\n        bottom: 52, // minimum required for footer msg to display\n        left: 20,\n        right: 20,\n        top: 10,\n      },\n      printBackground: true,\n      displayHeaderFooter: true,\n      footerTemplate: footerMarkup(),\n      headerTemplate: \"<div></div>\",\n    });\n\n    // Closing the puppeteer browser instance\n    await browser.close();\n  } catch (error) {\n    console.error(error);\n  }\n}\n\nmodule.exports = { createPdf };\n"
  },
  {
    "path": "twindle-cli/src/renderer/pdf/examples/Twindle.html",
    "content": "<style type=\"text/css\">\n  * {\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell,\n      \"Open Sans\", \"Helvetica Neue\", sans-serif;\n  }\n\n  body {\n    margin: 5% auto;\n  }\n\n  #title {\n    text-align: center;\n  }\n\n  ul {\n    list-style-type: none;\n    display: flex;\n    align-items: center;\n    flex-direction: column;\n  }\n\n  ul li {\n    margin: 20px;\n  }\n\n  .tweetContainer {\n    display: flex;\n    flex-direction: column;\n  }\n\n  .tweetContainer * {\n    margin: 0;\n    padding: 0;\n  }\n\n  .tweetContainer .header {\n    display: flex;\n    flex-direction: row;\n    align-items: center;\n    margin-bottom: 10px;\n  }\n\n  .tweetContainer .header img {\n    width: 75px;\n    height: 75px;\n    border-radius: 50%;\n\n    margin-right: 10px;\n  }\n\n  .tweetContainer .header > div {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    justify-content: center;\n  }\n\n  .tweetContainer .header > div p {\n    font-size: 14px;\n  }\n\n  .tweetContainer .header > div span {\n    font-style: italic;\n    color: rgb(45, 45, 45);\n  }\n</style>\n<html>\n  <body>\n    <h1 id=\"title\">Twindle Thread</h1>\n\n    <ul>\n      <li>\n        <div class=\"tweetContainer\">\n          <div class=\"header\">\n            <img\n              src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\"\n              alt=\"naval\"\n            />\n            <div>\n              <h3>Naval - <span>@naval</span></h3>\n              <p><span>2018-05-31</span></p>\n            </div>\n          </div>\n          <p>How to Get Rich (without getting lucky):</p>\n        </div>\n      </li>\n      <li>\n        <div class=\"tweetContainer\">\n          <div class=\"header\">\n            <img\n              src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\"\n              alt=\"naval\"\n            />\n            <div>\n              <h3>Naval - <span>@naval</span></h3>\n              <p><span>2018-05-31</span></p>\n            </div>\n          </div>\n          <p>How to Get Rich (without getting lucky):</p>\n        </div>\n      </li>\n      <li>\n        <div class=\"tweetContainer\">\n          <div class=\"header\">\n            <img\n              src=\"https://pbs.twimg.com/profile_images/1256841238298292232/ycqwaMI2_400x400.jpg\"\n              alt=\"naval\"\n            />\n            <div>\n              <h3>Naval - <span>@naval</span></h3>\n              <p><span>2018-05-31</span></p>\n            </div>\n          </div>\n          <p>How to Get Rich (without getting lucky):</p>\n        </div>\n      </li>\n    </ul>\n  </body>\n</html>\n"
  },
  {
    "path": "twindle-cli/src/renderer/pdf/index.js",
    "content": "const { renderTemplate } = require(\"./render-template\");\nconst { createPdf } = require(\"./create-pdf\");\n\n// const mockData = require(\"../twitter/output/twitter-api-response.json\");\n\nasync function generatePDF(srcData, src, outputPath) {\n  const parameter = { threads: srcData };\n  // creates the html content\n  const htmlContent = await renderTemplate(parameter, src);\n\n  // creates the pdf from html and saves it to Twindle.pdf\n  if (srcData.length > 0) await createPdf(outputPath, htmlContent);\n\n  return;\n}\n\nmodule.exports = { generatePDF };\n"
  },
  {
    "path": "twindle-cli/src/renderer/pdf/render-template.js",
    "content": "// @ts-check\nrequire(\"svelte/register\");\nconst { writeFile, readFile } = require(\"fs\").promises;\nconst { render } = require(\"../main\");\nconst { tmpdir } = require(\"os\");\nconst { join, resolve } = require(\"path\");\n\nconst GithubApp = require(\"../main/github/components/App.svelte\").default;\nconst MozReadabilityApp = require(\"../main/article/components/App.svelte\").default;\nconst HackerNewsApp = require(\"../main/hackernews/components/App.svelte\").default;\n\n/**\n * Renders the html template with the given data and returns the html string\n * @param {CustomTweetsObject} data\n * @param {string} src\n */\nasync function renderTemplate(data, src) {\n  if (src == \"twitter\") return await renderTwitterTemplate(data);\n  else if (src == \"github\") return await renderGithubTemplate(data);\n  else if (src == \"hackernews\") return await renderHackernewsTemplate(data);\n  else if (src == \"article\") return await renderArticleTemplate(data);\n}\n\nasync function renderTwitterTemplate(data) {\n  // rendering the svelte component to html\n  let { html, css } = render(data);\n\n  html = `<!doctype html> \n          <html> \n            <head>\n              <meta charset=\"UTF-8\" /\n              <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n              <style>${css.code}</style>\n            </head>\n            <body>\n              ${html}\n            </body>\n          </html>\n`;\n\n  const tmpPath = join(tmpdir(), \"twitter.html\");\n  await writeFile(tmpPath, html, \"utf-8\");\n  await writeFile(tmpdir() + \"/twitter.json\", JSON.stringify(data, null, 2), \"utf-8\");\n  console.devLog(\"rendered saved to \", tmpPath);\n  return html;\n}\n\nasync function renderGithubTemplate(data) {\n  // @ts-ignore\n  let { html, css } = GithubApp.render(data);\n\n  html = `<!doctype html> \n          <html> \n            <head>\n              <meta charset=\"UTF-8\" /\n              <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n              <style>${css.code}</style>\n            </head>\n            <body>\n              ${html}\n            </body>\n          </html>\n`;\n\n  const tmpPath = join(tmpdir(), \"github.html\");\n  await writeFile(tmpPath, html, \"utf-8\");\n  await writeFile(tmpdir() + \"/github.json\", JSON.stringify(data, null, 2), \"utf-8\");\n  console.devLog(\"rendered saved to \", tmpPath);\n  return html;\n}\n\nasync function renderHackernewsTemplate(data) {\n  // @ts-ignore\n  let { html, css } = HackerNewsApp.render(data);\n\n  // Augment with global CSS\n  const globalCSS = await readFile(resolve(__dirname + \"/../main/hackernews/style.css\"), \"utf-8\");\n\n  css = `${css.code} \\n ${globalCSS}`;\n\n  html = `<!doctype html> \n          <html> \n            <head>\n              <meta charset=\"UTF-8\" /\n              <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n              <style>${css}</style>\n              <link rel=\"stylesheet\" type=\"text/css\" href=\"http://fonts.googleapis.com/css?family=Wellfleet\">\n            </head>\n            <body>\n              ${html}\n            </body>\n          </html>\n`;\n\n  const tmpPath = join(tmpdir(), \"hackernews.html\");\n  await writeFile(tmpPath, html, \"utf-8\");\n  await writeFile(tmpdir() + \"/hackernews.json\", JSON.stringify(data, null, 2), \"utf-8\");\n  console.devLog(\"rendered saved to \", tmpPath);\n  return html;\n}\n\nasync function renderArticleTemplate(data) {\n  // @ts-ignore\n  let { html, css } = MozReadabilityApp.render(data);\n\n  html = `<!doctype html> \n          <html> \n            <head>\n              <meta charset=\"UTF-8\" /\n              <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n              <style>${css.code}</style>\n            </head>\n            <body>\n              ${html}\n            </body>\n          </html>\n`;\n\n  const tmpPath = join(tmpdir(), \"readability.html\");\n  await writeFile(tmpPath, html, \"utf-8\");\n  await writeFile(tmpdir() + \"/readability.json\", JSON.stringify(data, null, 2), \"utf-8\");\n  console.devLog(\"rendered saved to \", tmpPath);\n  return html;\n}\n\nmodule.exports = { renderTemplate };\n"
  },
  {
    "path": "twindle-cli/src/setup/init.js",
    "content": "const prompt = require('prompt');\nconst fs = require(\"fs\");\nconst path = require('path');\nconst homeDirectory = (process.env.APPDATA || \n                        (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : \n                            process.env.HOME + \"/.local/share\")) + '/twindle.json';\n    \nconsole.log('Welcome to Twindle setup!');\nconsole.log('--------------------------------------------------------------------------------------');\nconsole.log('To convert Twitter threads/tweets to PDF you need a Twitter API Bearer Token.\\n'\n            + 'Please apply for Twitter Developer Access to obtain one.\\n' \n            + 'You can follow this link for more directions: https://github.com/twindle-co/twindle/wiki/Applying-for-Developer-Access-from-Twitter');\n\nconsole.log('--------------------------------------------------------------------------------------');\nconsole.log('Any file generated using Twindle can be sent to your Kindle device for leisure reading\\n'\n            + 'To be able to send the files to Kindle, you must configure your Kindle account to whitelist a sender email address.\\n'\n            + 'Follow this link for more directions: https://www.amazon.com/gp/sendtokindle/email');\nconsole.log('--------------------------------------------------------------------------------------');\n\n\nconst envResult = {};\nconst properties = [\n    {\n        name: 'token',\n        description: 'Please enter the Twitter Bearer Token that you have obtained'\n    },\n    {\n        name: 'kindleEmail',\n        description: 'Please enter the Kindle email address of the device where you want to receive the document',\n        validator: /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/i,\n        warning: 'Please enter a valid email address'\n    },\n    {\n        name: 'senderEmail',\n        description: 'Please enter the whitelisted sender email address that has been authorized on the Kindle account',\n        validator: /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/i,\n        warning: 'Please enter a valid email address'\n    },\n    {\n        name: 'password',\n        description: 'Please enter the password of the above email address',\n        hidden: true\n    },\n    {\n        name: 'host',\n        description: 'Please enter the smtp host that you want to use to send the email(smtp.gmail.com)',\n        validator: /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$/,\n        warning: 'Please enter a valid host name',\n        default: 'smtp.gmail.com'\n    }, \n    {\n        name: 'port',\n        description: 'Please enter the port that you want to use to send the email(465)',\n        type: 'number',\n        warning: 'Port number must be valid',\n        default: 465\n    },\n    {\n        name: 'twindleLibrary',\n        description: 'Please enter the Twindle Library Path where the documents must be stored',\n        default:''\n    }\n];\n\nprompt.start();\n\nprompt.get(properties, function (err, result) {\n    if (err) { return onErr(err); }\n    //console.log('Command-line input received:');\n    //console.log(result);\n    //console.log(homeDirectory);\n    fs.writeFileSync(homeDirectory, JSON.stringify(result,null,2) );\n    //const json = JSON.parse(fs.readFileSync(homeDirectory));\n    //console.log(json);\n});\n\nfunction onErr(err) {\n    console.log(err);\n    return 1;\n}"
  },
  {
    "path": "twindle-cli/src/spinner.js",
    "content": "const ora = require(\"ora\");\n\nconst spinner = ora({ text: \"Loading    \", color: \"green\", spinner: \"dots\" });\n\nmodule.exports = spinner;\n"
  },
  {
    "path": "twindle-cli/src/twindle.js",
    "content": "// possibly initialize twindle cli here\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/constants.js",
    "content": "const TWEET_FIELDS =\n  \"&tweet.fields=attachments,author_id,context_annotations,conversation_id,created_at,\" +\n  \"entities,geo,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,referenced_tweets,\" +\n  \"source,withheld\";\nconst EXPANSIONS = \"&expansions=author_id,attachments.media_keys\";\nconst USER_FIELDS =\n  \"&user.fields=created_at,description,entities,location,pinned_tweet_id,profile_image_url,protected,public_metrics,url,verified,withheld\";\nconst MEDIA_FIELDS =\n  \"&media.fields=duration_ms,height,preview_image_url,url,media_key,public_metrics,width\";\nconst PLACE_FIELDS = \"&place.fields=contained_within,country,country_code,geo,name,place_type\";\nconst POLL_FIELDS = \"&poll.fields=duration_minutes,end_datetime,voting_status\";\nconst MAX_RESULTS = \"&max_results=100\";\n\nconst getCommonFields = () =>\n  `${TWEET_FIELDS}${EXPANSIONS}${USER_FIELDS}${MEDIA_FIELDS}${PLACE_FIELDS}${POLL_FIELDS}`;\n\nmodule.exports = {\n  getCommonFields,\n  MAX_RESULTS,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/helpers/fetch.js",
    "content": "// @ts-check\nconst nodeFetch = require(\"node-fetch\").default;\nconst { ApiErrors } = require(\"../../error\");\n\n/**\n * @param {string} url Twitter endpoint to send request to\n * @param {string} token Bearer token. Should be provided from the .env file\n */\nconst fetch = async (url, token) => {\n  if (!token) throw new ApiErrors.TokenNotProvidedError();\n\n  const response = await nodeFetch(url, {\n    method: \"GET\",\n    headers: { Authorization: `Bearer ${token}` },\n    redirect: \"follow\",\n  });\n\n  if ([401, 403].includes(response.status)) throw new ApiErrors.InvalidTokenError();\n\n  if ([400].includes(response.status)) throw new ApiErrors.BadTwitterRequestError();\n\n  if ([429, 500, 503].includes(response.status)) throw new ApiErrors.TwitterServiceError();\n\n  if (response.status !== 200) throw new ApiErrors.NetworkRequestError();\n\n  /** @type {TwitterConversationResponse} */\n  const data = await response.json();\n\n  return {\n    status: \"ok\",\n    data,\n  };\n};\n\nmodule.exports = {\n  fetch,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/index.js",
    "content": "const { getTweetById } = require(\"./twitter-endpoints/tweets\");\nconst { getConversationById } = require(\"./twitter-endpoints/search\");\n\nmodule.exports = {\n  getTweetById,\n  getConversationById,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/twitter-endpoints/search.js",
    "content": "const { fetch } = require(\"../helpers/fetch\");\nconst { getCommonFields, MAX_RESULTS } = require(\"../constants\");\nconst { ApiErrors } = require(\"../../error\");\n\nconst BASE_ENDPOINT =\n  \"https://api.twitter.com/2/tweets/search/recent?query=conversation_id:<conversation_id>+from:<screen_name>\";\n\n/**\n * @param {string} conversation_id\n * @param {string} screen_name\n */\nconst getUrl = (conversation_id, screen_name) => {\n  let url = BASE_ENDPOINT;\n  url = url.replace(\"<conversation_id>\", conversation_id);\n  url = url.replace(\"<screen_name>\", screen_name);\n\n  const COMMON_FIELDS = getCommonFields();\n  return `${url}${COMMON_FIELDS}${MAX_RESULTS}`;\n};\n\n/**\n * @param {string} id\n * @param {string} screenName\n * @param {string} token\n */\nconst getConversationById = (id, screenName, token) => {\n  if (!id) throw new ApiErrors.TweetIDNotProvidedError();\n\n  if (!screenName) throw new ApiErrors.UserScreenNameInvalid();\n\n  // Remove @ in case mutated\n  screenName = screenName.replace(\"@\", \"\");\n\n  const url = getUrl(id, screenName);\n  return fetch(url, token);\n};\n\nmodule.exports = {\n  getConversationById,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/twitter-endpoints/tweets.js",
    "content": "const { fetch } = require(\"../helpers/fetch\");\nconst { getCommonFields } = require(\"../constants\");\nconst { ApiErrors } = require(\"../../error\");\n\nconst BASE_ENDPOINT = \"https://api.twitter.com/2/tweets?ids=\";\n\n/**\n * @param {string} id tweet ID\n * @param {string} token Bearer token\n */\nconst getTweetById = (id, token) => {\n  if (!id) throw new ApiErrors.TweetIDNotProvidedError();\n\n  const COMMON_FIELDS = getCommonFields();\n  const url = `${BASE_ENDPOINT}${id}${COMMON_FIELDS}`;\n\n  return fetch(url, token);\n};\n\nmodule.exports = {\n  getTweetById,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/api/twitter-endpoints/user_timeline.js",
    "content": "const { fetch } = require(\"../helpers/fetch\");\nconst { getCommonFields, MAX_RESULTS } = require(\"../constants\");\n\nconst BASE_ENDPOINT =\n  \"https://api.twitter.com/2/tweets/search/recent?query=from:<screen_name>+-is:retweet\";\n\n/**\n * @param {string} screen_name\n */\nconst getUrl = (screen_name) => {\n  let url = BASE_ENDPOINT;\n  url = url.replace(\"<screen_name>\", screen_name);\n\n  const COMMON_FIELDS = getCommonFields();\n  return `${url}${COMMON_FIELDS}${MAX_RESULTS}`;\n};\n\n/**\n * @param {string} id\n * @param {string} screenName\n * @param {string} token\n */\nconst getUserTweets = (screenName, token) => {\n  const url = getUrl(screenName);\n  return fetch(url, token);\n};\n\nmodule.exports = {\n  getUserTweets,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/error/api.js",
    "content": "const { TwitterError } = require(\"./base\");\n\nclass NetworkRequestError extends TwitterError {\n  constructor() {\n    super(\"request-failed\", \"Request failed. Check your network and try again\");\n  }\n}\n\nclass TokenNotProvidedError extends TwitterError {\n  constructor() {\n    super(\"token-not-passed\", \"Please provide the token\");\n  }\n}\n\nclass InvalidTokenError extends TwitterError {\n  constructor() {\n    super(\n      \"invalid-token\",\n      \"Inavalid Bearer token. Please see if your bearer token in the .env file is valid\"\n    );\n  }\n}\n\nclass BadTwitterRequestError extends TwitterError {\n  constructor() {\n    super(\n      \"bad-request\",\n      \"Invalid request parameters. This error happens most likely because of invalid tweet id\"\n    );\n  }\n}\n\nclass TwitterServiceError extends TwitterError {\n  constructor() {\n    super(\n      \"twitter-service-error\",\n      \"Try later. Twitter is facing issues with too many requests or the access token limits were reached.\"\n    );\n  }\n}\n\nclass TweetIDNotProvidedError extends TwitterError {\n  constructor() {\n    super(\"tweet-id-not-passed\", \"Please provide the tweet id\");\n  }\n}\n\nclass TweetDoesNotExist extends TwitterError {\n  constructor() {\n    super(\"tweet-does-not-exists\", \"Tweet does not exists. PLease check the tweet ID you entered\");\n  }\n}\n\nclass UserScreenNameInvalid extends TwitterError {\n  constructor() {\n    super(\"user-screen-name-invalid\", \"Invalid user screen user name\");\n  }\n}\n\nmodule.exports = {\n  NetworkRequestError,\n  TokenNotProvidedError,\n  TweetIDNotProvidedError,\n  InvalidTokenError,\n  BadTwitterRequestError,\n  TwitterServiceError,\n  TweetDoesNotExist,\n  UserScreenNameInvalid,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/error/base.js",
    "content": "const { UserError } = require(\"../../helpers/error\");\n\nclass TwitterError extends UserError {\n  constructor(name, message) {\n    super(\"twitter-api:\" + name, message);\n  }\n}\n\nmodule.exports = {\n  TwitterError,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/error/index.js",
    "content": "const ApiErrors = require(\"./api\");\nconst ValidationErrors = require(\"./validation\");\n\nmodule.exports = {\n  ApiErrors,\n  ValidationErrors,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/error/validation.js",
    "content": "const { TwitterError } = require(\"./base\");\n\nclass TweetDeletedError extends TwitterError {\n  constructor() {\n    super(\"tweet-deleted\", \"Cannot fetch details of this tweet.\");\n  }\n}\n\nclass TweetOlderThan7DaysError extends TwitterError {\n  constructor() {\n    super(\"tweet-older-than-7-days\", \"The tweet must not be older than 7 days.\");\n  }\n}\n\nclass TweetNotFirstOfThreadError extends TwitterError {\n  constructor() {\n    super(\"tweet-not-first-of-thread\", \"The provided tweet is not the first of the thread.\");\n  }\n}\n\nmodule.exports = {\n  TweetDeletedError,\n  TweetOlderThan7DaysError,\n  TweetNotFirstOfThreadError,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/index.js",
    "content": "const twitter = require(\"./twitter\");\nmodule.exports = twitter;\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/delete-if-not-needed/data.js",
    "content": "const finalProcessedTweet = {\n  common: {\n    created_at: \"Nov 6, 2020\",\n    count: 4,\n    user: {\n      url: \"https://t.co/SUkzxmt7Gx\",\n      id: \"391224534\",\n      username: \"johnjacobkenny\",\n      name: \"Kenny Jacob ⚡\",\n      protected: false,\n      public_metrics: {\n        followers_count: 690,\n        following_count: 718,\n        tweet_count: 2651,\n        listed_count: 11,\n      },\n      entities: {\n        url: {\n          urls: [\n            {\n              start: 0,\n              end: 23,\n              url: \"https://t.co/SUkzxmt7Gx\",\n              expanded_url: \"https://kennyj.me/\",\n              display_url: \"kennyj.me\",\n            },\n          ],\n        },\n        description: {\n          mentions: [{ start: 20, end: 30, username: \"twindleco\" }],\n        },\n      },\n      description:\n        'Christian <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/><br />Mentor <a href=\"https://twitter.com/twindleco\">@twindleco</a><br />DM me for any help <img class=\"emoji\" draggable=\"false\" alt=\"👨‍💻\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f468-200d-1f4bb.svg\"/> <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> JS, React, Typescript<br /><img class=\"emoji\" draggable=\"false\" alt=\"🎹\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f3b9.svg\"/> Musician <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> Linux <img class=\"emoji\" draggable=\"false\" alt=\"🎨\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f3a8.svg\"/> Inkscape<br /><img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> Arsenal FC fan <img class=\"emoji\" draggable=\"false\" alt=\"😅\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f605.svg\"/>',\n      profile_image_url: \"https://pbs.twimg.com/profile_images/1115624775290839040/9TlEqVgi.jpg\",\n      verified: false,\n      pinned_tweet_id: \"1287729624068132866\",\n      location: \"Find me online here 👉\",\n      created_at: \"2011-10-15T07:29:31.000Z\",\n    },\n  },\n  data: [\n    {\n      id: \"1324561342292897792\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        'Don\\'t let perfection and fear of rejection hold you back from making progress! A thread <img class=\"emoji\" draggable=\"false\" alt=\"👇\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f447.svg\"/><img class=\"emoji\" draggable=\"false\" alt=\"🧵\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f9f5.svg\"/><br /><br />1/ I used to be a perfectionist. I would polish and polish and polish. No, I would polish and get frustrated by my lack of progress and quit.',\n    },\n    {\n      id: \"1324561344935284736\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        \"2/ The frustration and my fear of not being perfect held me back all these years. From writing, from building useful products. I feared being ridiculed for being imperfect.<br /><br />Now I understand that there is no perfection (at least in the short term).\",\n    },\n    {\n      id: \"1324561347330273281\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        \"3/  What we create will always have flaws in someone else's view because of their different experiences in life. So how do we make something perfect?<br /><br />The only thing that matters is to make progress. Iteratively improve, refine your thought processes, your coding, etc.\",\n    },\n    {\n      id: \"1324561349662236673\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        '4/ Make \"progress\" your metric. Perfection will follow in its own time. <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/>',\n    },\n  ],\n};\n\nconst firstTweet = {\n  status: \"ok\",\n  data: {\n    data: [\n      {\n        conversation_id: \"1324561342292897792\",\n        text:\n          \"Don't let perfection and fear of rejection hold you back from making progress! A thread 👇🧵\\n\\n1/ I used to be a perfectionist. I would polish and polish and polish. No, I would polish and get frustrated by my lack of progress and quit.\",\n        lang: \"en\",\n        created_at: \"2020-11-06T03:56:47.000Z\",\n        source: \"Twitter Web App\",\n        public_metrics: {\n          retweet_count: 8,\n          reply_count: 3,\n          like_count: 30,\n          quote_count: 0,\n        },\n        possibly_sensitive: false,\n        id: \"1324561342292897792\",\n        author_id: \"391224534\",\n      },\n    ],\n    includes: {\n      users: [\n        {\n          location: \"Find me online here 👉\",\n          name: \"Kenny Jacob ⚡\",\n          username: \"johnjacobkenny\",\n          pinned_tweet_id: \"1287729624068132866\",\n          description:\n            \"Christian ❤️\\nMentor @twindleco\\nDM me for any help 👨‍💻 ❤️ JS, React, Typescript\\n🎹 Musician ❤️ Linux 🎨 Inkscape\\n❤️ Arsenal FC fan 😅\",\n          verified: false,\n          protected: false,\n          profile_image_url:\n            \"https://pbs.twimg.com/profile_images/1115624775290839040/9TlEqVgi_normal.jpg\",\n          url: \"https://t.co/SUkzxmt7Gx\",\n          created_at: \"2011-10-15T07:29:31.000Z\",\n          entities: {\n            url: {\n              urls: [\n                {\n                  start: 0,\n                  end: 23,\n                  url: \"https://t.co/SUkzxmt7Gx\",\n                  expanded_url: \"https://kennyj.me/\",\n                  display_url: \"kennyj.me\",\n                },\n              ],\n            },\n            description: {\n              mentions: [{ start: 20, end: 30, username: \"twindleco\" }],\n            },\n          },\n          public_metrics: {\n            followers_count: 690,\n            following_count: 718,\n            tweet_count: 2651,\n            listed_count: 11,\n          },\n          id: \"391224534\",\n        },\n      ],\n    },\n  },\n};\nmodule.exports = {\n  firstTweet,\n  finalProcessedTweet,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/delete-if-not-needed/dataaa.js",
    "content": "const tweetLookupApiResponse = {\n  data: [\n    {\n      conversation_id: \"1324561342292897792\",\n      text:\n        \"Don't let perfection and fear of rejection hold you back from making progress! A thread 👇🧵\\n\\n1/ I used to be a perfectionist. I would polish and polish and polish. No, I would polish and get frustrated by my lack of progress and quit.\",\n      lang: \"en\",\n      created_at: \"2020-11-06T03:56:47.000Z\",\n      source: \"Twitter Web App\",\n      public_metrics: {\n        retweet_count: 8,\n        reply_count: 3,\n        like_count: 30,\n        quote_count: 0,\n      },\n      possibly_sensitive: false,\n      id: \"1324561342292897792\",\n      author_id: \"391224534\",\n    },\n  ],\n  includes: {\n    users: [\n      {\n        location: \"Find me online here 👉\",\n        name: \"Kenny Jacob ⚡\",\n        username: \"johnjacobkenny\",\n        pinned_tweet_id: \"1287729624068132866\",\n        description:\n          \"Christian ❤️\\nMentor @twindleco\\nDM me for any help 👨‍💻 ❤️ JS, React, Typescript\\n🎹 Musician ❤️ Linux 🎨 Inkscape\\n❤️ Arsenal FC fan 😅\",\n        verified: false,\n        protected: false,\n        profile_image_url:\n          \"https://pbs.twimg.com/profile_images/1115624775290839040/9TlEqVgi_normal.jpg\",\n        url: \"https://t.co/SUkzxmt7Gx\",\n        created_at: \"2011-10-15T07:29:31.000Z\",\n        entities: {\n          url: {\n            urls: [\n              {\n                start: 0,\n                end: 23,\n                url: \"https://t.co/SUkzxmt7Gx\",\n                expanded_url: \"https://kennyj.me/\",\n                display_url: \"kennyj.me\",\n              },\n            ],\n          },\n          description: {\n            mentions: [{ start: 20, end: 30, username: \"twindleco\" }],\n          },\n        },\n        public_metrics: {\n          followers_count: 690,\n          following_count: 718,\n          tweet_count: 2649,\n          listed_count: 11,\n        },\n        id: \"391224534\",\n      },\n    ],\n  },\n};\n\nconst tweetLookupApiProcessed = {\n  common: {\n    created_at: \"Nov 6, 2020\",\n    count: 4,\n    user: {\n      description:\n        'Christian <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/><br />Mentor <a href=\"https://twitter.com/twindleco\">@twindleco</a><br />DM me for any help <img class=\"emoji\" draggable=\"false\" alt=\"👨‍💻\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f468-200d-1f4bb.svg\"/> <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> JS, React, Typescript<br /><img class=\"emoji\" draggable=\"false\" alt=\"🎹\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f3b9.svg\"/> Musician <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> Linux <img class=\"emoji\" draggable=\"false\" alt=\"🎨\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f3a8.svg\"/> Inkscape<br /><img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/> Arsenal FC fan <img class=\"emoji\" draggable=\"false\" alt=\"😅\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f605.svg\"/>',\n      location: \"Find me online here 👉\",\n      id: \"391224534\",\n      created_at: \"2011-10-15T07:29:31.000Z\",\n      pinned_tweet_id: \"1287729624068132866\",\n      public_metrics: {\n        followers_count: 690,\n        following_count: 718,\n        tweet_count: 2650,\n        listed_count: 11,\n      },\n      verified: false,\n      username: \"johnjacobkenny\",\n      name: \"Kenny Jacob ⚡\",\n      protected: false,\n      url: \"https://t.co/SUkzxmt7Gx\",\n      profile_image_url: \"https://pbs.twimg.com/profile_images/1115624775290839040/9TlEqVgi.jpg\",\n      entities: {\n        url: {\n          urls: [\n            {\n              start: 0,\n              end: 23,\n              url: \"https://t.co/SUkzxmt7Gx\",\n              expanded_url: \"https://kennyj.me/\",\n              display_url: \"kennyj.me\",\n            },\n          ],\n        },\n        description: {\n          mentions: [{ start: 20, end: 30, username: \"twindleco\" }],\n        },\n      },\n    },\n  },\n  data: [\n    {\n      id: \"1324561342292897792\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        'Don\\'t let perfection and fear of rejection hold you back from making progress! A thread <img class=\"emoji\" draggable=\"false\" alt=\"👇\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f447.svg\"/><img class=\"emoji\" draggable=\"false\" alt=\"🧵\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f9f5.svg\"/><br /><br />1/ I used to be a perfectionist. I would polish and polish and polish. No, I would polish and get frustrated by my lack of progress and quit.',\n    },\n    {\n      id: \"1324561344935284736\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        \"2/ The frustration and my fear of not being perfect held me back all these years. From writing, from building useful products. I feared being ridiculed for being imperfect.<br /><br />Now I understand that there is no perfection (at least in the short term).\",\n    },\n    {\n      id: \"1324561347330273281\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        \"3/  What we create will always have flaws in someone else's view because of their different experiences in life. So how do we make something perfect?<br /><br />The only thing that matters is to make progress. Iteratively improve, refine your thought processes, your coding, etc.\",\n    },\n    {\n      id: \"1324561349662236673\",\n      createdAt: \"Nov 6, 2020  9:26 a.m.\",\n      tweet:\n        '4/ Make \"progress\" your metric. Perfection will follow in its own time. <img class=\"emoji\" draggable=\"false\" alt=\"❤️\" src=\"https://twemoji.maxcdn.com/v/13.0.1/svg/2764.svg\"/>',\n    },\n  ],\n};\nmodule.exports = {\n  tweetLookupApiResponse,\n  tweetLookupApiProcessed,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-mock-responses/images-text.json",
    "content": "{\n  \"common\": {\n    \"created_at\": \"Nov 5, 2020\",\n    \"count\": 5,\n    \"user\": {\n      \"description\": \"Software Engineer ‧ Business Owner ‧ Starter.<br />YouTube → <a href=\\\"http://youtube.com/SimonHoiberg\\\" class=\\\"description-link\\\" rel=\\\"noopener noreferrer\\\">youtube.com/SimonHoiberg</a> <br /><br /><img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔥\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f525.svg\\\"/> Freedom and independence\",\n      \"location\": \"Newsletter →\",\n      \"id\": \"875776212341329920\",\n      \"created_at\": \"2017-06-16T18:04:54.000Z\",\n      \"pinned_tweet_id\": \"1322801496531099650\",\n      \"public_metrics\": {\n        \"followers_count\": 20709,\n        \"following_count\": 237,\n        \"tweet_count\": 5339,\n        \"listed_count\": 157\n      },\n      \"verified\": false,\n      \"username\": \"SimonHoiberg\",\n      \"name\": \"Simon Høiberg\",\n      \"protected\": false,\n      \"url\": \"https://t.co/qO9TnGwEPM\",\n      \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1314813880607166464/rZdd-ubL.jpg\",\n      \"entities\": {\n        \"url\": {\n          \"urls\": [\n            {\n              \"start\": 0,\n              \"end\": 23,\n              \"url\": \"https://t.co/qO9TnGwEPM\",\n              \"expanded_url\": \"https://simonhoiberg.com/newsletter\",\n              \"display_url\": \"simonhoiberg.com/newsletter\"\n            }\n          ]\n        },\n        \"description\": {\n          \"urls\": [\n            {\n              \"start\": 56,\n              \"end\": 79,\n              \"url\": \"https://t.co/ZZrisWRYHe\",\n              \"expanded_url\": \"http://youtube.com/SimonHoiberg\",\n              \"display_url\": \"youtube.com/SimonHoiberg\"\n            }\n          ]\n        }\n      }\n    }\n  },\n  \"data\": [\n    {\n      \"id\": \"1324263512621883393\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"JavaScript <img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"💡\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f4a1.svg\\\"/><br /><br />Let's take a look at the Promise API.<br />4 methods explained with examples below.<br /><br /><img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🧵\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f9f5.svg\\\"/><img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"👇\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f447.svg\\\"/> \",\n      \"customMedia\": {\n        \"photo\": [\n          {\n            \"width\": 660,\n            \"height\": 355,\n            \"preview_img_url\": \"https://pbs.twimg.com/media/EmC6eqIX0AEjRHA.png\",\n            \"link\": \"pic.twitter.com/vtjTcpbmqd\"\n          }\n        ],\n        \"video\": [],\n        \"animated_gif\": []\n      }\n    },\n    {\n      \"id\": \"1324263516589690880\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.all()<br /><br />The Promise.all() method takes an array of promises as an argument and returns a single Promise that resolves to an array of the results of the input promises. \",\n      \"customMedia\": {\n        \"photo\": [\n          {\n            \"width\": 1226,\n            \"height\": 768,\n            \"preview_img_url\": \"https://pbs.twimg.com/media/EmC5WnvWkAAc1bu.jpg\",\n            \"link\": \"pic.twitter.com/LT7bDlmLJp\"\n          }\n        ],\n        \"video\": [],\n        \"animated_gif\": []\n      }\n    },\n    {\n      \"id\": \"1324263520398155778\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.allSettled()<br /><br />The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise. \",\n      \"customMedia\": {\n        \"photo\": [\n          {\n            \"width\": 1260,\n            \"height\": 600,\n            \"preview_img_url\": \"https://pbs.twimg.com/media/EmC5Y6qXYAE3GqO.jpg\",\n            \"link\": \"pic.twitter.com/4JhJbRWwcr\"\n          }\n        ],\n        \"video\": [],\n        \"animated_gif\": []\n      }\n    },\n    {\n      \"id\": \"1324263524206600197\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.any()<br /><br />The Promise.any() method takes an array of promises as an argument and, as soon as one of the promises fulfills, returns a single promise that resolves with the value from that promise. \",\n      \"customMedia\": {\n        \"photo\": [\n          {\n            \"width\": 1260,\n            \"height\": 852,\n            \"preview_img_url\": \"https://pbs.twimg.com/media/EmC5Z1QXIAAkHBi.jpg\",\n            \"link\": \"pic.twitter.com/yzrOiAUgaM\"\n          }\n        ],\n        \"video\": [],\n        \"animated_gif\": []\n      }\n    },\n    {\n      \"id\": \"1324263528874860544\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.race()<br /><br />The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an array fulfills or rejects, with the value or reason from that promise. \",\n      \"customMedia\": {\n        \"photo\": [\n          {\n            \"width\": 1260,\n            \"height\": 726,\n            \"preview_img_url\": \"https://pbs.twimg.com/media/EmC5bMKXgAAiszc.jpg\",\n            \"link\": \"pic.twitter.com/Z9rl80aPlY\"\n          }\n        ],\n        \"video\": [],\n        \"animated_gif\": []\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-mock-responses/mock.json",
    "content": "{\n  \"common\": {\n    \"created_at\": \"Nov 5, 2020\",\n    \"count\": 5,\n    \"user\": {\n      \"verified\": false,\n      \"created_at\": \"2017-06-16T18:04:54.000Z\",\n      \"id\": \"875776212341329920\",\n      \"pinned_tweet_id\": \"1322801496531099650\",\n      \"public_metrics\": {\n        \"followers_count\": 20641,\n        \"following_count\": 237,\n        \"tweet_count\": 5310,\n        \"listed_count\": 157\n      },\n      \"protected\": false,\n      \"description\": \"Software Engineer ‧ Business Owner ‧ Starter.\\nYouTube → https://t.co/ZZrisWRYHe \\n\\n🔥 Freedom and independence\",\n      \"location\": \"Newsletter →\",\n      \"name\": \"Simon Høiberg\",\n      \"entities\": {\n        \"url\": {\n          \"urls\": [\n            {\n              \"start\": 0,\n              \"end\": 23,\n              \"url\": \"https://t.co/qO9TnGwEPM\",\n              \"expanded_url\": \"https://simonhoiberg.com/newsletter\",\n              \"display_url\": \"simonhoiberg.com/newsletter\"\n            }\n          ]\n        },\n        \"description\": {\n          \"urls\": [\n            {\n              \"start\": 56,\n              \"end\": 79,\n              \"url\": \"https://t.co/ZZrisWRYHe\",\n              \"expanded_url\": \"http://youtube.com/SimonHoiberg\",\n              \"display_url\": \"youtube.com/SimonHoiberg\"\n            }\n          ]\n        }\n      },\n      \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1314813880607166464/rZdd-ubL.jpg\",\n      \"url\": \"https://t.co/qO9TnGwEPM\",\n      \"username\": \"SimonHoiberg\"\n    }\n  },\n  \"data\": [\n    {\n      \"id\": \"1324263512621883393\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"JavaScript <img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"💡\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f4a1.svg\\\"/><br /><br />Let's take a look at the Promise API.<br />4 methods explained with examples below.<br /><br /><img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🧵\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f9f5.svg\\\"/><img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"👇\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f447.svg\\\"/> https://t.co/vtjTcpbmqd\"\n    },\n    {\n      \"id\": \"1324263516589690880\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.all()<br /><br />The Promise.all() method takes an array of promises as an argument and returns a single Promise that resolves to an array of the results of the input promises. https://t.co/LT7bDlmLJp\"\n    },\n    {\n      \"id\": \"1324263520398155778\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.allSettled()<br /><br />The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise. https://t.co/4JhJbRWwcr\"\n    },\n    {\n      \"id\": \"1324263524206600197\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.any()<br /><br />The Promise.any() method takes an array of promises as an argument and, as soon as one of the promises fulfills, returns a single promise that resolves with the value from that promise. https://t.co/yzrOiAUgaM\"\n    },\n    {\n      \"id\": \"1324263528874860544\",\n      \"createdAt\": \"Nov 5, 2020  1:43 p.m.\",\n      \"tweet\": \"<img class=\\\"emoji\\\" draggable=\\\"false\\\" alt=\\\"🔹\\\" src=\\\"https://twemoji.maxcdn.com/v/13.0.1/svg/1f539.svg\\\"/> Promise.race()<br /><br />The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an array fulfills or rejects, with the value or reason from that promise. https://t.co/Z9rl80aPlY\"\n    }\n  ]\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-mock-responses/only-links.json",
    "content": "{\n  \"common\": {\n    \"created_at\": \"Nov 5, 2020\",\n    \"count\": 17,\n    \"user\": {\n      \"verified\": true,\n      \"created_at\": \"2012-11-01T03:35:00.000Z\",\n      \"id\": \"918169628\",\n      \"pinned_tweet_id\": \"1324059542343294976\",\n      \"public_metrics\": {\n        \"followers_count\": 320739,\n        \"following_count\": 2268,\n        \"tweet_count\": 16772,\n        \"listed_count\": 5253\n      },\n      \"protected\": false,\n      \"description\": \"Decision Desk HQ provides fast & accurate election night results, in-depth data, and non-partisan election news and analysis. info@decisiondeskhq.com\",\n      \"location\": \"Everywhere.\",\n      \"name\": \"Decision Desk HQ\",\n      \"entities\": {\n        \"url\": {\n          \"urls\": [\n            {\n              \"start\": 0,\n              \"end\": 23,\n              \"url\": \"https://t.co/ZYGkeuww9n\",\n              \"expanded_url\": \"http://decisiondeskhq.com\",\n              \"display_url\": \"decisiondeskhq.com\"\n            }\n          ]\n        }\n      },\n      \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1236418774980612096/dz2M0bIw.png\",\n      \"url\": \"https://t.co/ZYGkeuww9n\",\n      \"username\": \"DecisionDeskHQ\"\n    }\n  },\n  \"data\": [\n    {\n      \"id\": \"1324355635354476545\",\n      \"createdAt\": \"Nov 5, 2020  7:49 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 50% (2,432,426 votes)<br />Biden (D): 49% (2,413,836 votes)<br /><br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324368636249821194\",\n      \"createdAt\": \"Nov 5, 2020  8:41 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.57% (2,432,552 votes)<br />Biden (D): 49.20% (2,413,966 votes)<br /><br />Estimated: &gt; 99% votes in\"\n    },\n    {\n      \"id\": \"1324389777668493313\",\n      \"createdAt\": \"Nov 5, 2020  10:05 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.53% (2,434,354 votes)<br />Biden (D): 49.23% (2,419,589 votes)<br /><br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324406442359771138\",\n      \"createdAt\": \"Nov 5, 2020  11:11 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.52% (2,436,006 votes)<br />Biden (D): 49.25% (2,422,467 votes)<br /><br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324449301494378501\",\n      \"createdAt\": \"Nov 6, 2020  2:01 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.51% (2,438,181 votes)<br />Biden (D): 49.25% (2,425,417 votes)<br /><br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324468859907284992\",\n      \"createdAt\": \"Nov 6, 2020  3:19 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.51% (2,438,216 votes)<br />Biden (D): 49.25% (2,425,445 votes)<br /><br />Trump Margin: +12,771<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324469475668873217\",\n      \"createdAt\": \"Nov 6, 2020  3:21 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.48% (2,439,678 votes)<br />Biden (D): 49.29% (2,430,153 votes)<br />Jorgensen (L): 1.23% (60,821 votes)<br /><br />Trump Margin: +9,525 (-3,246)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324501109902315521\",\n      \"createdAt\": \"Nov 6, 2020  5:27 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.42% (2,445,539 votes)<br />Biden (D): 49.35% (2,441,904 votes)<br /><br />Trump Margin: +3,635 (-5890)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324532001664339971\",\n      \"createdAt\": \"Nov 6, 2020  7:30 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.41% (2,447,015 votes)<br />Biden (D): 49.36% (2,444,518 votes)<br /><br />Trump Margin: +2,497 (-1,138)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324543783351078912\",\n      \"createdAt\": \"Nov 6, 2020  8:17 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.40% (2,447,223 votes)<br />Biden (D): 49.36% (2,445,321 votes)<br /><br />Trump Margin: +1,902 (-595)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324549301163462656\",\n      \"createdAt\": \"Nov 6, 2020  8:38 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.4% (2,447,337 votes)<br />Biden (D): 49.36% (2,445,540 votes)<br /><br />Trump Margin: +1,797 (105)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324588693923995649\",\n      \"createdAt\": \"Nov 6, 2020  11:15 a.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.4% (2,448,056 votes)<br />Biden (D): 49.37% (2,446,577 votes)<br /><br />Trump Margin: +1,479 (-318)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324600180176551936\",\n      \"createdAt\": \"Nov 6, 2020  12:01 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.39% (2,448,081 votes)<br />Biden (D): 49.37% (2,446,814 votes)<br /><br />Trump Margin: +1,267 (-212)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324617816725737473\",\n      \"createdAt\": \"Nov 6, 2020  1:11 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.39% (2,448,183 votes)<br />Biden (D): 49.37% (2,447,518 votes)<br /><br />Trump Margin: +665 (-602)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324632723718311936\",\n      \"createdAt\": \"Nov 6, 2020  2:10 p.m.\",\n      \"tweet\": \"GA Presidential Election Results<br /><br />Trump (R): 49.39% (2,448,232 votes)<br />Biden (D): 49.38% (2,447,769 votes)<br /><br />Trump Margin: +463 (-202)<br />Estimated: &gt; 99% votes in<br /><br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324643210283593728\",\n      \"createdAt\": \"Nov 6, 2020  2:52 p.m.\",\n      \"tweet\": \"GA Presidential Election Results - Biden takes the Lead<br /> <br />Biden (D): 49.39.% ( 2,449,371 votes)<br />Trump (R): 49.37% ( 2,448,454 votes)<br /> <br />Biden Margin:  (+917)<br />Estimated: &gt; 99% votes in<br /> <br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    },\n    {\n      \"id\": \"1324655386339794945\",\n      \"createdAt\": \"Nov 6, 2020  3:40 p.m.\",\n      \"tweet\": \"GA Presidential Election Results <br /> <br />Biden (D): 49.39.% ( 2,449,580 votes)<br />Trump (R): 49.37% ( 2,448,484 votes)<br /> <br />Biden Margin:  (+ 1,096)<br />Estimated: &gt; 99% votes in<br /> <br />More results here: \",\n      \"linkWithImage\": {\n        \"expanded_url\": \"https://results.decisiondeskhq.com/2020/general/georgia\",\n        \"images\": [\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=orig\",\n            \"width\": 1200,\n            \"height\": 600\n          },\n          {\n            \"url\": \"https://pbs.twimg.com/news_img/1323782478759403522/Vn3PKu6p?format=jpg&name=150x150\",\n            \"width\": 150,\n            \"height\": 150\n          }\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/gif-retweet.json",
    "content": "{\n  \"data\": [\n    {\n      \"created_at\": \"2020-11-10T06:27:59.000Z\",\n      \"source\": \"Twitter Web App\",\n      \"id\": \"1326048944078516226\",\n      \"author_id\": \"3436392039\",\n      \"lang\": \"en\",\n      \"text\": \"Random tweet\\n\\nhttps://t.co/EF9oK3WrKW\\n\\nhttps://t.co/4qmTT1NaY9\",\n      \"entities\": {\n        \"urls\": [\n          {\n            \"start\": 14,\n            \"end\": 37,\n            \"url\": \"https://t.co/EF9oK3WrKW\",\n            \"expanded_url\": \"https://twitter.com/puruvjdev/status/1325362881961975808?s=20\",\n            \"display_url\": \"twitter.com/puruvjdev/stat…\"\n          },\n          {\n            \"start\": 39,\n            \"end\": 62,\n            \"url\": \"https://t.co/4qmTT1NaY9\",\n            \"expanded_url\": \"https://twitter.com/puruvjdev/status/1324638941983567872?s=20\",\n            \"display_url\": \"twitter.com/puruvjdev/stat…\"\n          }\n        ]\n      },\n      \"possibly_sensitive\": false,\n      \"public_metrics\": {\n        \"retweet_count\": 0,\n        \"reply_count\": 0,\n        \"like_count\": 0,\n        \"quote_count\": 0\n      },\n      \"referenced_tweets\": [\n        {\n          \"type\": \"quoted\",\n          \"id\": \"1324638941983567872\"\n        }\n      ],\n      \"conversation_id\": \"1326048944078516226\"\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"verified\": false,\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1316284643675578369/Ym8iAim8_normal.jpg\",\n        \"username\": \"puruvjdev\",\n        \"public_metrics\": {\n          \"followers_count\": 300,\n          \"following_count\": 405,\n          \"tweet_count\": 3642,\n          \"listed_count\": 5\n        },\n        \"entities\": {\n          \"url\": {\n            \"urls\": [\n              {\n                \"start\": 0,\n                \"end\": 23,\n                \"url\": \"https://t.co/gdeY0K6smm\",\n                \"expanded_url\": \"https://puruvj.dev\",\n                \"display_url\": \"puruvj.dev\"\n              }\n            ]\n          }\n        },\n        \"url\": \"https://t.co/gdeY0K6smm\",\n        \"protected\": false,\n        \"location\": \"Hogwarts, Narnia, Middle Earth\",\n        \"pinned_tweet_id\": \"1308682456434868225\",\n        \"created_at\": \"2015-08-23T07:46:19.000Z\",\n        \"description\": \"18. Web dev for 5 years. Loves orchestral music.\\nLove Star Wars, Avengers, Harry Potter.\\n\\nHe/Him\\n\\nWhat's the deal with this \\uD83D\\uDC46 cat? \\uD83E\\uDD14\",\n        \"name\": \"{/* puruvj.dev */}\",\n        \"id\": \"3436392039\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/only-images.json",
    "content": "{\n  \"data\": [\n    {\n      \"lang\": \"en\",\n      \"possibly_sensitive\": false,\n      \"conversation_id\": \"1320872258085310466\",\n      \"id\": \"1321087179939057665\",\n      \"created_at\": \"2020-10-27T13:51:42.000Z\",\n      \"source\": \"Twitter Web App\",\n      \"entities\": {\n        \"urls\": [\n          {\n            \"start\": 260,\n            \"end\": 283,\n            \"url\": \"https://t.co/Elnsnvqr4u\",\n            \"expanded_url\": \"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\",\n            \"display_url\": \"motherjones.com/2020-elections…\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1323764821830078465/ybSV2_Z0?format=jpg&name=orig\",\n                \"width\": 1200,\n                \"height\": 630\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1323764821830078465/ybSV2_Z0?format=jpg&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"status\": 200,\n            \"title\": \"Brett Kavanaugh just laid out his plan to help Trump steal the election\",\n            \"description\": \"A Bush v. Gore 2.0 crisis just became much more likely.\",\n            \"unwound_url\": \"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\"\n          }\n        ],\n        \"annotations\": [\n          {\n            \"start\": 68,\n            \"end\": 76,\n            \"probability\": 0.9533,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Kavanaugh\"\n          },\n          {\n            \"start\": 182,\n            \"end\": 186,\n            \"probability\": 0.9978,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Trump\"\n          },\n          {\n            \"start\": 242,\n            \"end\": 254,\n            \"probability\": 0.5928,\n            \"type\": \"Organization\",\n            \"normalized_text\": \"Supreme Court\"\n          }\n        ]\n      },\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"88\",\n            \"name\": \"Political Body\",\n            \"description\": \"A section of a government, like The Supreme Court\"\n          },\n          \"entity\": {\n            \"id\": \"867872043672326144\",\n            \"name\": \"Supreme Court of the United States\",\n            \"description\": \"Conversation about the Supreme Court and justices\"\n          }\n        }\n      ],\n      \"text\": \"I hope people understand how serious a threat to democracy this is: Kavanaugh is now “swing justice” on 6-3 court &amp; he’s spreading same disinformation about counting mail ballots as Trump\\n\\nWe need to vote in record numbers to overcome rigged Supreme Court https://t.co/Elnsnvqr4u\",\n      \"author_id\": \"15952856\",\n      \"public_metrics\": {\n        \"retweet_count\": 377,\n        \"reply_count\": 46,\n        \"like_count\": 925,\n        \"quote_count\": 50\n      },\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320899108606017539\"\n        }\n      ],\n      \"in_reply_to_user_id\": \"15952856\"\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"entities\": {\n          \"description\": {\n            \"urls\": [\n              {\n                \"start\": 77,\n                \"end\": 100,\n                \"url\": \"https://t.co/q5tNcCdQjy\",\n                \"expanded_url\": \"http://amzn.to/2asHld2\",\n                \"display_url\": \"amzn.to/2asHld2\"\n              }\n            ],\n            \"mentions\": [\n              {\n                \"start\": 109,\n                \"end\": 121,\n                \"username\": \"motherjones\"\n              }\n            ]\n          }\n        },\n        \"pinned_tweet_id\": \"1159848529038364672\",\n        \"public_metrics\": {\n          \"followers_count\": 163109,\n          \"following_count\": 2796,\n          \"tweet_count\": 26706,\n          \"listed_count\": 2772\n        },\n        \"username\": \"AriBerman\",\n        \"description\": \"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https://t.co/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1286763565412753409/KkQAmz2N_normal.jpg\",\n        \"verified\": true,\n        \"location\": \"New York\",\n        \"created_at\": \"2008-08-23T01:46:02.000Z\",\n        \"name\": \"Ari Berman\",\n        \"protected\": false,\n        \"id\": \"15952856\",\n        \"url\": \"\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/only-text.json",
    "content": "{\n  \"data\": [\n    {\n      \"public_metrics\": {\n        \"retweet_count\": 111,\n        \"reply_count\": 24,\n        \"like_count\": 1173,\n        \"quote_count\": 19\n      },\n      \"conversation_id\": \"1323545207510630400\",\n      \"possibly_sensitive\": false,\n      \"created_at\": \"2020-11-03T08:39:02.000Z\",\n      \"text\": \"There's something incredibly beautiful about the rise of both podcasts and newsletters in an age dominated by the walled gardens of big tech. Two basic forms of distribution that rely not on proprietary platforms, but on free and open standards, devoid of corporate lock-in ❤️\",\n      \"source\": \"Twitter Web App\",\n      \"id\": \"1323545207510630400\",\n      \"lang\": \"en\",\n      \"author_id\": \"14561327\"\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"name\": \"DHH\",\n        \"username\": \"dhh\",\n        \"description\": \"Creator of Ruby on Rails, Founder & CTO at Basecamp & HEY, NYT best-selling author, and Le Mans 24h class-winning racing driver.\",\n        \"created_at\": \"2008-04-27T20:19:25.000Z\",\n        \"id\": \"14561327\",\n        \"url\": \"https://t.co/472m0ihZkH\",\n        \"public_metrics\": {\n          \"followers_count\": 434043,\n          \"following_count\": 153,\n          \"tweet_count\": 55212,\n          \"listed_count\": 10333\n        },\n        \"protected\": false,\n        \"entities\": {\n          \"url\": {\n            \"urls\": [\n              {\n                \"start\": 0,\n                \"end\": 23,\n                \"url\": \"https://t.co/472m0ihZkH\",\n                \"expanded_url\": \"https://dhh.dk\",\n                \"display_url\": \"dhh.dk\"\n              }\n            ]\n          }\n        },\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/975876868455809024/eK7mDppU_normal.jpg\",\n        \"verified\": true\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/response-version2-tweetthread.json",
    "content": "{\n  \"data\": [\n    {\n      \"public_metrics\": {\n        \"retweet_count\": 137,\n        \"reply_count\": 12,\n        \"like_count\": 347,\n        \"quote_count\": 15\n      },\n      \"text\": \"I hope people understand how serious a threat to democracy this is: Kavanaugh is now “swing justice” on 6-3 court &amp; he’s spreading same disinformation about counting mail ballots as Trump\\n\\nWe need to vote in record numbers to overcome rigged Supreme Court https://t.co/Elnsnvqr4u\",\n      \"lang\": \"en\",\n      \"in_reply_to_user_id\": \"15952856\",\n      \"source\": \"Twitter Web App\",\n      \"created_at\": \"2020-10-27T13:51:42.000Z\",\n      \"possibly_sensitive\": false,\n      \"author_id\": \"15952856\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320899108606017539\"\n        }\n      ],\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"start\": 68,\n            \"end\": 76,\n            \"probability\": 0.9533,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Kavanaugh\"\n          },\n          {\n            \"start\": 182,\n            \"end\": 186,\n            \"probability\": 0.9978,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Trump\"\n          },\n          {\n            \"start\": 242,\n            \"end\": 254,\n            \"probability\": 0.5928,\n            \"type\": \"Organization\",\n            \"normalized_text\": \"Supreme Court\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"start\": 260,\n            \"end\": 283,\n            \"url\": \"https://t.co/Elnsnvqr4u\",\n            \"expanded_url\": \"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\",\n            \"display_url\": \"motherjones.com/2020-elections…\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1321079020184809474/XbL5QJjE?format=jpg&name=orig\",\n                \"width\": 1200,\n                \"height\": 630\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1321079020184809474/XbL5QJjE?format=jpg&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"status\": 200,\n            \"title\": \"Brett Kavanaugh just laid out his plan to help Trump steal the election\",\n            \"description\": \"A Bush v. Gore 2.0 crisis just became much more likely.\",\n            \"unwound_url\": \"https://www.motherjones.com/2020-elections/2020/10/brett-kavanaugh-lays-out-a-plan-to-help-trump-steal-the-election/\"\n          }\n        ]\n      },\n      \"id\": \"1321087179939057665\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"88\",\n            \"name\": \"Political Body\",\n            \"description\": \"A section of a government, like The Supreme Court\"\n          },\n          \"entity\": {\n            \"id\": \"867872043672326144\",\n            \"name\": \"Supreme Court of the United States\",\n            \"description\": \"Conversation about the Supreme Court and justices\"\n          }\n        }\n      ],\n      \"conversation_id\": \"1320872258085310466\"\n    },\n    {\n      \"attachments\": {\n        \"media_keys\": [\"3_1320898952183619584\"]\n      },\n      \"public_metrics\": {\n        \"retweet_count\": 552,\n        \"reply_count\": 60,\n        \"like_count\": 2177,\n        \"quote_count\": 41\n      },\n      \"text\": \"Kagan dissent in Wisconsin case: \\\"the Court’s decision will disenfranchise large numbers of responsible voters in the midst of hazardous pandemic conditions\\\" https://t.co/Q4pEXmDN96\",\n      \"lang\": \"en\",\n      \"in_reply_to_user_id\": \"15952856\",\n      \"source\": \"Twitter Web App\",\n      \"created_at\": \"2020-10-27T01:24:23.000Z\",\n      \"possibly_sensitive\": false,\n      \"author_id\": \"15952856\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320883524954660867\"\n        }\n      ],\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"start\": 17,\n            \"end\": 25,\n            \"probability\": 0.9873,\n            \"type\": \"Place\",\n            \"normalized_text\": \"Wisconsin\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"start\": 158,\n            \"end\": 181,\n            \"url\": \"https://t.co/Q4pEXmDN96\",\n            \"expanded_url\": \"https://twitter.com/AriBerman/status/1320899108606017539/photo/1\",\n            \"display_url\": \"pic.twitter.com/Q4pEXmDN96\"\n          }\n        ]\n      },\n      \"id\": \"1320899108606017539\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"123\",\n            \"name\": \"Ongoing News Story\",\n            \"description\": \"Ongoing News Stories like 'Brexit'\"\n          },\n          \"entity\": {\n            \"id\": \"1220701888179359745\",\n            \"name\": \"COVID-19\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"867887467902283776\",\n            \"name\": \"Elena Kagan\",\n            \"description\": \"US Supreme Court Justice Elena Kagan\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"867887467902283776\",\n            \"name\": \"Elena Kagan\",\n            \"description\": \"US Supreme Court Justice Elena Kagan\"\n          }\n        }\n      ],\n      \"conversation_id\": \"1320872258085310466\"\n    },\n    {\n      \"attachments\": {\n        \"media_keys\": [\"3_1320882150552571905\"]\n      },\n      \"public_metrics\": {\n        \"retweet_count\": 696,\n        \"reply_count\": 108,\n        \"like_count\": 1875,\n        \"quote_count\": 152\n      },\n      \"text\": \"Insane rhetoric from Kavanaugh decrying \\\"chaos &amp; suspicions of impropriety that can ensue if thousands of absentee ballots flow in after election day and potentially flip the results of an election\\\"\\n\\nThis comes on some night Trump tweets winner of election must be announced Nov 3 https://t.co/Y9iZcBGOGy\",\n      \"lang\": \"en\",\n      \"in_reply_to_user_id\": \"15952856\",\n      \"source\": \"Twitter Web App\",\n      \"created_at\": \"2020-10-27T00:22:27.000Z\",\n      \"possibly_sensitive\": false,\n      \"author_id\": \"15952856\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320877150770241541\"\n        }\n      ],\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"start\": 21,\n            \"end\": 29,\n            \"probability\": 0.6753,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Kavanaugh\"\n          },\n          {\n            \"start\": 225,\n            \"end\": 229,\n            \"probability\": 0.9984,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Trump\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"start\": 285,\n            \"end\": 308,\n            \"url\": \"https://t.co/Y9iZcBGOGy\",\n            \"expanded_url\": \"https://twitter.com/AriBerman/status/1320883524954660867/photo/1\",\n            \"display_url\": \"pic.twitter.com/Y9iZcBGOGy\"\n          }\n        ]\n      },\n      \"id\": \"1320883524954660867\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"799022225751871488\",\n            \"name\": \"Donald Trump\",\n            \"description\": \"US President Donald Trump\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"1016678775298260992\",\n            \"name\": \"Brett Kavanaugh\",\n            \"description\": \"US Supreme Court nominee Brett Kavanaugh\"\n          }\n        }\n      ],\n      \"conversation_id\": \"1320872258085310466\"\n    },\n    {\n      \"attachments\": {\n        \"media_keys\": [\"3_1320876915889262592\"]\n      },\n      \"public_metrics\": {\n        \"retweet_count\": 2255,\n        \"reply_count\": 226,\n        \"like_count\": 4100,\n        \"quote_count\": 414\n      },\n      \"text\": \"Kavanaugh cites Bush v. Gore to justify making it harder to vote in Wisconsin\\n\\nKavanaugh, Roberts &amp; Barrett all served on George W. Bush legal team in Florida 2000 recount &amp; worked to make sure only GOP votes counted https://t.co/NhNessbwoB\",\n      \"lang\": \"en\",\n      \"in_reply_to_user_id\": \"15952856\",\n      \"source\": \"Twitter Web App\",\n      \"created_at\": \"2020-10-26T23:57:08.000Z\",\n      \"possibly_sensitive\": false,\n      \"author_id\": \"15952856\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"replied_to\",\n          \"id\": \"1320872258085310466\"\n        }\n      ],\n      \"entities\": {\n        \"annotations\": [\n          {\n            \"start\": 0,\n            \"end\": 8,\n            \"probability\": 0.9211,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Kavanaugh\"\n          },\n          {\n            \"start\": 16,\n            \"end\": 19,\n            \"probability\": 0.9427,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Bush\"\n          },\n          {\n            \"start\": 24,\n            \"end\": 27,\n            \"probability\": 0.7073,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Gore\"\n          },\n          {\n            \"start\": 68,\n            \"end\": 76,\n            \"probability\": 0.6797,\n            \"type\": \"Organization\",\n            \"normalized_text\": \"Wisconsin\"\n          },\n          {\n            \"start\": 79,\n            \"end\": 87,\n            \"probability\": 0.7773,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Kavanaugh\"\n          },\n          {\n            \"start\": 90,\n            \"end\": 96,\n            \"probability\": 0.9675,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Roberts\"\n          },\n          {\n            \"start\": 100,\n            \"end\": 106,\n            \"probability\": 0.9509,\n            \"type\": \"Person\",\n            \"normalized_text\": \"Barrett\"\n          },\n          {\n            \"start\": 122,\n            \"end\": 135,\n            \"probability\": 0.9268,\n            \"type\": \"Person\",\n            \"normalized_text\": \"George W. Bush\"\n          },\n          {\n            \"start\": 151,\n            \"end\": 157,\n            \"probability\": 0.9004,\n            \"type\": \"Place\",\n            \"normalized_text\": \"Florida\"\n          },\n          {\n            \"start\": 199,\n            \"end\": 201,\n            \"probability\": 0.8738,\n            \"type\": \"Organization\",\n            \"normalized_text\": \"GOP\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"start\": 225,\n            \"end\": 248,\n            \"url\": \"https://t.co/NhNessbwoB\",\n            \"expanded_url\": \"https://twitter.com/AriBerman/status/1320877150770241541/photo/1\",\n            \"display_url\": \"pic.twitter.com/NhNessbwoB\"\n          }\n        ]\n      },\n      \"id\": \"1320877150770241541\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"10\",\n            \"name\": \"Person\",\n            \"description\": \"Named people in the world like Nelson Mandela\"\n          },\n          \"entity\": {\n            \"id\": \"921071164469911552\",\n            \"name\": \"George W. Bush\",\n            \"description\": \"Former US President George W. Bush\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"35\",\n            \"name\": \"Politician\",\n            \"description\": \"Politicians in the world, like Joe Biden\"\n          },\n          \"entity\": {\n            \"id\": \"921071164469911552\",\n            \"name\": \"George W. Bush\",\n            \"description\": \"Former US President George W. Bush\"\n          }\n        }\n      ],\n      \"conversation_id\": \"1320872258085310466\"\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"url\": \"\",\n        \"id\": \"15952856\",\n        \"username\": \"AriBerman\",\n        \"name\": \"Ari Berman\",\n        \"protected\": false,\n        \"public_metrics\": {\n          \"followers_count\": 159733,\n          \"following_count\": 2782,\n          \"tweet_count\": 26572,\n          \"listed_count\": 2705\n        },\n        \"entities\": {\n          \"description\": {\n            \"urls\": [\n              {\n                \"start\": 77,\n                \"end\": 100,\n                \"url\": \"https://t.co/q5tNcCdQjy\",\n                \"expanded_url\": \"http://amzn.to/2asHld2\",\n                \"display_url\": \"amzn.to/2asHld2\"\n              }\n            ],\n            \"mentions\": [\n              {\n                \"start\": 109,\n                \"end\": 121,\n                \"username\": \"motherjones\"\n              }\n            ]\n          }\n        },\n        \"description\": \"Author: Give Us the Ballot: The Modern Struggle for Voting Rights in America https://t.co/q5tNcCdQjy Writer: @motherjones Speaking: annette@speakersforall.com\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1286763565412753409/KkQAmz2N_normal.jpg\",\n        \"verified\": true,\n        \"pinned_tweet_id\": \"1159848529038364672\",\n        \"location\": \"New York\",\n        \"created_at\": \"2008-08-23T01:46:02.000Z\"\n      }\n    ]\n  },\n  \"meta\": {\n    \"newest_id\": \"1321087179939057665\",\n    \"oldest_id\": \"1320877150770241541\",\n    \"result_count\": 4\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/squirrel-pumpkin.json",
    "content": "{\n  \"data\": [\n    {\n      \"author_id\": \"228955392\",\n      \"created_at\": \"2020-11-04T02:48:10.000Z\",\n      \"entities\": {\n        \"urls\": [\n          {\n            \"start\": 78,\n            \"end\": 101,\n            \"url\": \"https://t.co/yqUFJ4vnNP\",\n            \"expanded_url\": \"https://twitter.com/ohheykiri/status/1323819296267665409/photo/1\",\n            \"display_url\": \"pic.twitter.com/yqUFJ4vnNP\"\n          },\n          {\n            \"start\": 78,\n            \"end\": 101,\n            \"url\": \"https://t.co/yqUFJ4vnNP\",\n            \"expanded_url\": \"https://twitter.com/ohheykiri/status/1323819296267665409/photo/1\",\n            \"display_url\": \"pic.twitter.com/yqUFJ4vnNP\"\n          },\n          {\n            \"start\": 78,\n            \"end\": 101,\n            \"url\": \"https://t.co/yqUFJ4vnNP\",\n            \"expanded_url\": \"https://twitter.com/ohheykiri/status/1323819296267665409/photo/1\",\n            \"display_url\": \"pic.twitter.com/yqUFJ4vnNP\"\n          },\n          {\n            \"start\": 78,\n            \"end\": 101,\n            \"url\": \"https://t.co/yqUFJ4vnNP\",\n            \"expanded_url\": \"https://twitter.com/ohheykiri/status/1323819296267665409/photo/1\",\n            \"display_url\": \"pic.twitter.com/yqUFJ4vnNP\"\n          }\n        ]\n      },\n      \"source\": \"Twitter for iPhone\",\n      \"lang\": \"en\",\n      \"public_metrics\": {\n        \"retweet_count\": 44,\n        \"reply_count\": 6,\n        \"like_count\": 207,\n        \"quote_count\": 2\n      },\n      \"attachments\": {\n        \"media_keys\": [\n          \"3_1323819284926255104\",\n          \"3_1323819285119225857\",\n          \"3_1323819285555384325\",\n          \"3_1323819287425986562\"\n        ]\n      },\n      \"text\": \"I watched a squirrel eat a pumpkin for like an hour today and it was magical. https://t.co/yqUFJ4vnNP\",\n      \"possibly_sensitive\": false,\n      \"id\": \"1323819296267665409\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"66\",\n            \"name\": \"Interests and Hobbies Category\",\n            \"description\": \"A grouping of interests and hobbies entities, like Novelty Food or Destinations\"\n          },\n          \"entity\": {\n            \"id\": \"824777229892661248\",\n            \"name\": \"Generic Food\",\n            \"description\": \"Generic Food\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"67\",\n            \"name\": \"Interests and Hobbies\",\n            \"description\": \"Interests, opinions, and behaviors of individuals, groups, or cultures; like Speciality Cooking or Theme Parks\"\n          },\n          \"entity\": {\n            \"id\": \"1037762759415365633\",\n            \"name\": \"Pumpkin\",\n            \"description\": \"Pumpkin\"\n          }\n        }\n      ],\n      \"conversation_id\": \"1323819296267665409\"\n    }\n  ],\n  \"includes\": {\n    \"media\": [\n      {\n        \"media_key\": \"3_1323819284926255104\",\n        \"height\": 1536,\n        \"url\": \"https://pbs.twimg.com/media/El8moDDXUAAVhWX.jpg\",\n        \"width\": 2048,\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"3_1323819285119225857\",\n        \"height\": 1536,\n        \"url\": \"https://pbs.twimg.com/media/El8moDxX0AEi7q-.jpg\",\n        \"width\": 2048,\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"3_1323819285555384325\",\n        \"height\": 2048,\n        \"url\": \"https://pbs.twimg.com/media/El8moFZXEAUI77Z.jpg\",\n        \"width\": 1536,\n        \"type\": \"photo\"\n      },\n      {\n        \"media_key\": \"3_1323819287425986562\",\n        \"height\": 1536,\n        \"url\": \"https://pbs.twimg.com/media/El8moMXWMAIARks.jpg\",\n        \"width\": 2048,\n        \"type\": \"photo\"\n      }\n    ],\n    \"users\": [\n      {\n        \"description\": \"Often funny, always verbosely sincere non-binary woman & jacket lesbian / She/They / Creative / “Easy to be gay about” / DMs Open / INFP / 4w5 @ohsokirious\",\n        \"id\": \"228955392\",\n        \"verified\": false,\n        \"username\": \"ohheykiri\",\n        \"public_metrics\": {\n          \"followers_count\": 9135,\n          \"following_count\": 739,\n          \"tweet_count\": 14890,\n          \"listed_count\": 43\n        },\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1320072536714084352/jwlzDKhx_normal.jpg\",\n        \"name\": \"Kiri Anne Ryan Stewart \\uD83C\\uDF36\\uD83E\\uDD52\",\n        \"location\": \"Grand Rapids, MI\",\n        \"url\": \"https://t.co/umkTzdgMVM\",\n        \"entities\": {\n          \"url\": {\n            \"urls\": [\n              {\n                \"start\": 0,\n                \"end\": 23,\n                \"url\": \"https://t.co/umkTzdgMVM\",\n                \"expanded_url\": \"http://bit.ly/2KWHgiC\",\n                \"display_url\": \"bit.ly/2KWHgiC\"\n              }\n            ]\n          },\n          \"description\": {\n            \"mentions\": [\n              {\n                \"start\": 143,\n                \"end\": 155,\n                \"username\": \"ohsokirious\"\n              }\n            ]\n          }\n        },\n        \"created_at\": \"2010-12-21T04:11:13.000Z\",\n        \"protected\": false\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/mock/twitter-responses/username-hashtag.json",
    "content": "{\n  \"data\": [\n    {\n      \"text\": \"Random tweet for @twindle #KimiNoNawa \\n\\nhttps://t.co/nMpySj1y61 https://t.co/kZejtqOYUn\",\n      \"public_metrics\": {\n        \"retweet_count\": 0,\n        \"reply_count\": 0,\n        \"like_count\": 2,\n        \"quote_count\": 0\n      },\n      \"conversation_id\": \"1324638941983567872\",\n      \"referenced_tweets\": [\n        {\n          \"type\": \"quoted\",\n          \"id\": \"1323137099906777088\"\n        }\n      ],\n      \"source\": \"Twitter Web App\",\n      \"lang\": \"en\",\n      \"entities\": {\n        \"hashtags\": [\n          {\n            \"start\": 26,\n            \"end\": 37,\n            \"tag\": \"KimiNoNawa\"\n          }\n        ],\n        \"urls\": [\n          {\n            \"start\": 40,\n            \"end\": 63,\n            \"url\": \"https://t.co/nMpySj1y61\",\n            \"expanded_url\": \"https://www.youtube.com/watch?v=_f8AIppMxXw\",\n            \"display_url\": \"youtube.com/watch?v=_f8AIp…\",\n            \"images\": [\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1324638946156900357/XI9fZR7n?format=jpg&name=orig\",\n                \"width\": 1280,\n                \"height\": 720\n              },\n              {\n                \"url\": \"https://pbs.twimg.com/news_img/1324638946156900357/XI9fZR7n?format=jpg&name=150x150\",\n                \"width\": 150,\n                \"height\": 150\n              }\n            ],\n            \"status\": 200,\n            \"title\": \"Pirates of the Caribbean: At World's End - What Shall We Die For? Extended\",\n            \"description\": \"Music by: Hans Zimmer Track: What Shall We Die For From the Film: Pirates of the Caribbean - At World's End Pirates of the Caribbean: At World's End - What S...\",\n            \"unwound_url\": \"https://www.youtube.com/watch?v=_f8AIppMxXw\"\n          },\n          {\n            \"start\": 64,\n            \"end\": 87,\n            \"url\": \"https://t.co/kZejtqOYUn\",\n            \"expanded_url\": \"https://twitter.com/puruvjdev/status/1323137099906777088\",\n            \"display_url\": \"twitter.com/puruvjdev/stat…\"\n          }\n        ],\n        \"mentions\": [\n          {\n            \"start\": 17,\n            \"end\": 25,\n            \"username\": \"twindle\"\n          }\n        ]\n      },\n      \"id\": \"1324638941983567872\",\n      \"possibly_sensitive\": false,\n      \"created_at\": \"2020-11-06T09:05:09.000Z\",\n      \"author_id\": \"3436392039\",\n      \"context_annotations\": [\n        {\n          \"domain\": {\n            \"id\": \"3\",\n            \"name\": \"TV Shows\",\n            \"description\": \"Television shows from around the world\"\n          },\n          \"entity\": {\n            \"id\": \"10002926010\",\n            \"name\": \"Avatar: The Last Airbender\"\n          }\n        },\n        {\n          \"domain\": {\n            \"id\": \"3\",\n            \"name\": \"TV Shows\",\n            \"description\": \"Television shows from around the world\"\n          },\n          \"entity\": {\n            \"id\": \"10024119551\",\n            \"name\": \"The Legend of Korra\"\n          }\n        }\n      ]\n    }\n  ],\n  \"includes\": {\n    \"users\": [\n      {\n        \"description\": \"18. Web dev for 5 years. Loves orchestral music.\\nLove Star Wars, Avengers, Harry Potter.\\n\\nPart of @twindleco\\n\\nWhat's the deal with this \\uD83D\\uDC46 #cat? \\uD83E\\uDD14\",\n        \"profile_image_url\": \"https://pbs.twimg.com/profile_images/1316284643675578369/Ym8iAim8_normal.jpg\",\n        \"name\": \"{/* puruvj.dev */}\",\n        \"location\": \"Hogwarts, Narnia, Middle Earth\",\n        \"url\": \"https://t.co/gdeY0K6smm\",\n        \"entities\": {\n          \"url\": {\n            \"urls\": [\n              {\n                \"start\": 0,\n                \"end\": 23,\n                \"url\": \"https://t.co/gdeY0K6smm\",\n                \"expanded_url\": \"https://puruvj.dev\",\n                \"display_url\": \"puruvj.dev\"\n              }\n            ]\n          },\n          \"description\": {\n            \"hashtags\": [\n              {\n                \"start\": 138,\n                \"end\": 142,\n                \"tag\": \"cat\"\n              }\n            ],\n            \"mentions\": [\n              {\n                \"start\": 98,\n                \"end\": 108,\n                \"username\": \"twindleco\"\n              }\n            ]\n          }\n        },\n        \"username\": \"puruvjdev\",\n        \"public_metrics\": {\n          \"followers_count\": 297,\n          \"following_count\": 403,\n          \"tweet_count\": 3620,\n          \"listed_count\": 5\n        },\n        \"created_at\": \"2015-08-23T07:46:19.000Z\",\n        \"id\": \"3436392039\",\n        \"protected\": false,\n        \"verified\": false,\n        \"pinned_tweet_id\": \"1308682456434868225\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "twindle-cli/src/twitter/scraping/index.js",
    "content": "const puppeteer = require(\"puppeteer\");\nconst { waitFor } = require(\"../../utils/helpers\");\n\n/**\n * Get tweets(even older than 7 days) using puppeteer\n * @param {string} tweetID\n */\nconst getTweetIDs = async (tweetID) => {\n  const pageURL = `https://twitter.com/anyone/status/${tweetID}`;\n\n  // Modify this variable to control the size of viewport\n  const factor = 0.2;\n  const height = Math.floor(2000 / factor);\n  const width = Math.floor(1700 / factor);\n\n  const browser = await puppeteer.launch({\n    headless: true,\n    defaultViewport: {\n      width,\n      height,\n    },\n    args: [`--force-device-scale-factor=${factor}`, `--window-size=${width},${height}`],\n  });\n\n  const page = await browser.newPage();\n\n  await page.goto(pageURL, {\n    waitUntil: \"networkidle2\",\n    timeout: 5 * 60 * 1000,\n  });\n\n  await waitFor(4000);\n\n  /** @type {string[]} */\n  const tweetIDs = await page.evaluate(async () => {\n    const ids = [];\n\n    /**\n     * Wait for `ms` amount of milliseconds\n     * @param {number} ms\n     */\n    const waitFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n    // Find the first Show thread button and click it\n    const showRepliesButton = [...document.querySelectorAll('div[dir=\"auto\"]')]\n      .filter((node) => node.children[0] && node.children[0].tagName === \"SPAN\")\n      .find((node) => node.children[0].innerHTML === \"Show replies\");\n\n    if (showRepliesButton) {\n      showRepliesButton.click();\n\n      await waitFor(2000);\n    }\n\n    const timeNodes = Array.from(document.querySelectorAll(\"time\"));\n\n    for (let timeNode of timeNodes) {\n      /** @type {HTMLAnchorElement | HTMLSpanElement} */\n      const timeContainerAnchor = timeNode.parentElement;\n\n      if (timeContainerAnchor.tagName === \"SPAN\") continue;\n\n      const id = timeContainerAnchor.href.split(\"/\").reverse()[0];\n\n      ids.push(id);\n    }\n\n    return ids;\n  });\n\n  await browser.close();\n\n  return [tweetID, ...tweetIDs];\n};\n\nmodule.exports = { getTweetIDs };\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/helpers.js",
    "content": "// @ts-check\n// this file can be split into multiple files within a helpers folder\n\nconst twemoji = require(\"twemoji\");\nconst { formatTimestamp } = require(\"../utils/date\");\n\n/** @param {string} tweet_url */\nconst extractScreenName = (tweet_url) => tweet_url.replace(/^https?:\\/\\//, \"\").split(\"/\")[1];\n\n/** @param {TwitterConversationResponse} responseJSON */\nconst getTweetArray = (responseJSON) => {\n  return ((responseJSON && responseJSON.data) || []).map((data) => ({\n    ...data,\n    includes: responseJSON.includes,\n  }));\n};\n\n/** @param {TwitterConversationResponse} responseJSON */\nconst getUserObject = (responseJSON) =>\n  (responseJSON && responseJSON.includes && responseJSON.includes.users).filter(\n    (user) => user.id === responseJSON.data[0].author_id\n  )[0];\n\n/** @param {TwitterConversationResponse} responseJSON */\nconst getTweetObject = (responseJSON) => ({\n  ...responseJSON.data[0],\n  includes: responseJSON.includes,\n});\n\n/**\n * @param {TwitterConversationData} tweet_object\n * @returns {import(\"../../types/twitter\").CustomTweetData}\n */\nconst createCustomTweet = (tweet_object) => {\n  /** @type {import(\"../../types/twitter\").CustomTweetData} */\n  const tweet = {\n    id: tweet_object.id,\n    tweet: twemoji.parse(fixLineBreaks(tweet_object.text), {\n      folder: \"svg\",\n      ext: \".svg\",\n    }),\n  };\n\n  if (tweet_object.created_at) tweet.created_at = formatTimestamp(tweet_object.created_at);\n\n  if (tweet_object.customMedia) {\n    tweet.customMedia = tweet_object.customMedia;\n  }\n\n  if (tweet_object.linkWithImage) {\n    tweet.linkWithImage = tweet_object.linkWithImage;\n  }\n\n  if (tweet_object.embeddedTweet) {\n    tweet.embeddedTweet = tweet_object.embeddedTweet;\n  }\n\n  return tweet;\n};\n\n/**\n * Replaces `\\n` with `<br />`\n * @param {string} tweet\n */\nfunction fixLineBreaks(tweet) {\n  return tweet.replace(/\\n/g, \"<br />\");\n}\n\nmodule.exports = {\n  extractScreenName,\n  getTweetArray,\n  getUserObject,\n  createCustomTweet,\n  fixLineBreaks,\n  getTweetObject,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/helpers.test.js",
    "content": "const { extractScreenName, getTweetArray } = require(\"./helpers\");\n\ndescribe(\"extractScreenName()\", () => {\n  test(\"should extract screenName correctly\", () => {\n    expect(extractScreenName(\"https://twitter.com/realDonaldTrump\")).toBe(\"realDonaldTrump\");\n  });\n\n  test(\"should not be able to extract screen name\", () => {\n    expect(extractScreenName(\"realDonaldTrump\")).toBeUndefined();\n  });\n});\n\ndescribe(\"#getTweetArray()\", () => {\n  test(\"should pass if nothing is passed\", () => {\n    expect(getTweetArray()).toEqual([]);\n    expect(getTweetArray({ data: [] })).toEqual([]);\n  });\n\n  test(\"should pass with bare minimum data\", () => {\n    const response = {\n      data: [],\n      includes: {\n        media: [],\n        users: [],\n      },\n    };\n\n    expect(getTweetArray(response)).toEqual([]);\n  });\n\n  test(\"should pass with bare minimum data #2\", () => {\n    const response = {\n      data: [\n        {\n          conversation_id: \"1323545207510630400\",\n        },\n      ],\n      includes: {\n        media: [],\n        users: [],\n      },\n    };\n\n    expect(getTweetArray(response)).toEqual([\n      {\n        conversation_id: \"1323545207510630400\",\n        includes: {\n          media: [],\n          users: [],\n        },\n      },\n    ]);\n  });\n\n  test(\"should  have includes\", () => {\n    const response = {\n      data: [\n        {\n          conversation_id: \"1323545207510630400\",\n        },\n      ],\n    };\n\n    expect(getTweetArray(response)).toEqual([\n      {\n        conversation_id: \"1323545207510630400\",\n        includes: undefined,\n      },\n    ]);\n  });\n});\n\n// ['twitter.com', 'username', 'sdvhjcvd', '/']\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/rich-rendering.js",
    "content": "// @ts-check\nconst fetch = require(\"node-fetch\").default;\nconst twemoji = require(\"twemoji\");\nconst { getTweetById } = require(\"../api/twitter-endpoints/tweets\");\nconst { formatTimestamp } = require(\"../utils/date\");\nconst { getTweetObject } = require(\"./helpers\");\n\n/**\n * @typedef {import(\"../../types/twitter\").Mention} TMention\n * @typedef {import(\"../../types/twitter\").Hashtag} THashtag\n */\n\n/**\n * Render the images, videos and GIFs\n * @param {TwitterConversationData} tweetObj\n */\nfunction renderMedia(tweetObj) {\n  /** @type {import(\"../../types/twitter\").CustomMedia} */\n  const mediaObj = {\n    photo: [],\n    video: [],\n    animated_gif: [],\n  };\n\n  /** @type {string} */\n  let tweetText = tweetObj.text;\n\n  /** @type {string[]} */\n  const mediaKeys = tweetObj.attachments && tweetObj.attachments.media_keys;\n\n  if (!mediaKeys) return tweetObj;\n\n  const expandedMediaIncludes = tweetObj.includes.media;\n\n  for (let mediaKey of mediaKeys) {\n    // Search for it in the expandedMedia\n    const mediaInfo = expandedMediaIncludes.find(({ media_key }) => media_key === mediaKey);\n\n    const { width, height, url, preview_image_url } = mediaInfo;\n\n    // Now let's delete the url matching this media\n\n    // Let's get the entitites\n    const urls = tweetObj.entities.urls;\n\n    const urlObjOfImageIndex = urls\n      .filter((url) => url.expanded_url != undefined)\n      .findIndex(\n        ({ expanded_url }) => expanded_url.includes(\"/photo/\") || expanded_url.includes(\"/video/\")\n      );\n\n    const urlObjOfImage = urls[urlObjOfImageIndex];\n\n    // Add to our list\n    mediaObj[mediaInfo.type].push({\n      width,\n      height,\n      preview_img_url: preview_image_url || url,\n      link: urlObjOfImage === undefined ? \"\" : urlObjOfImage.expanded_url,\n    });\n\n    // Get the actual short URL (t.co/[STUFF])\n    const shortURL = urlObjOfImage === undefined ? \"\" : urlObjOfImage.url;\n\n    // Replace in the tweet text\n    tweetText = tweetText.replace(shortURL, \"\");\n\n    delete tweetObj.entities.urls[urlObjOfImageIndex];\n  }\n\n  // Modify the object\n  tweetObj.text = tweetText;\n\n  tweetObj.customMedia = mediaObj;\n\n  // Return it\n  return tweetObj;\n}\n\n/**\n * Renders the outsider links(i.e links that are not embedded tweets)\n * @param {TwitterConversationData} tweetObj\n * @param {boolean} embedded\n */\nasync function renderOutsiderLinks(tweetObj, embedded) {\n  /** @type {any[]} */\n\n  if (!tweetObj.entities) return tweetObj;\n\n  let urlObjs = tweetObj.entities.urls;\n\n  if (!urlObjs) return tweetObj;\n\n  /**\n   * The final link whose image should be rendered\n   * @type {Partial<import(\"../../types/twitter\").LinkWithImage>}\n   */\n  let linkWithImage = {};\n\n  let linkWithImageChosen = false;\n\n  // Tweet content\n  /** @type {string} */\n  const tweetText = tweetObj.text;\n\n  // Filter images and retweets\n  // urlObjs = urlObjs.filter((urlObj) => !urlObj.expanded_url.includes(\"status\"));\n\n  for (let urlObj of urlObjs) {\n    if (!urlObj) continue;\n\n    const isACard = \"images\" in urlObj;\n    const hasCustomMedia = \"customMedia\" in tweetObj && !!tweetObj.customMedia;\n\n    const isStatusLink = urlObj.expanded_url.includes(\"/status/\");\n\n    if (isACard && !linkWithImageChosen && !hasCustomMedia && !embedded) {\n      // console.log(urlObj)\n      linkWithImage = {\n        expanded_url: urlObj.expanded_url,\n        images: urlObj.images,\n        title: urlObj.title,\n        description: urlObj.description,\n        domain: new URL(urlObj.unwound_url).hostname,\n      };\n\n      /**\n       * @type {{\n       *   url: string;\n       *   width: number;\n       *   height: number;\n       *  }[]}\n       */\n      const newList = [];\n\n      for (let img of linkWithImage.images) {\n        const req = await fetch(img.url);\n\n        if (req.status !== 404) {\n          newList.push(img);\n        }\n      }\n\n      linkWithImage.images = newList;\n\n      if (!linkWithImage.images.length) {\n        // Nothing here\n        linkWithImage = {};\n        continue;\n      }\n\n      // Check if it is at the end or not\n      if (tweetObj.text.trim().substring(urlObj.start, urlObj.end + 1) === urlObj.url.trim()) {\n        // console.log(1);\n        // Remove from the markup\n        tweetObj.text = tweetText.replace(urlObj.url, \"\");\n      }\n\n      linkWithImageChosen = true;\n    }\n\n    // Replace all short links with their shown links\n    if (!isStatusLink) {\n      tweetObj.text = tweetObj.text.replace(\n        urlObj.url,\n        `<a href=\"${urlObj.expanded_url}\">${urlObj.expanded_url}</a>`\n      );\n    }\n  }\n\n  if (Object.entries(linkWithImage).length) tweetObj.linkWithImage = linkWithImage;\n\n  return tweetObj;\n}\n\n/**\n * This function takes in the text, rather than a tweet object, and returns that string.\n * For portability across tweets and user descriptions\n * @param {Object} param\n * @param {string} param.text\n * @param {TMention[]} param.mentions\n * @param {THashtag[]} param.hashtags\n * @returns {string}\n */\nfunction renderMentionsHashtags({ text = \"\", mentions = [], hashtags = [] }) {\n  // Make the checks\n  if (!mentions.length && !hashtags.length) return text;\n\n  if (mentions.length) {\n    // There are mentions\n    for (let mention of mentions) {\n      const { username } = mention;\n\n      // Replace\n      text = text.replace(\n        `@${username}`,\n        `<a href=\"https://twitter.com/${username}\">@${username}</a>`\n      );\n    }\n  }\n\n  if (hashtags.length) {\n    // There are hashtags\n    for (let hashtag of hashtags) {\n      const { tag } = hashtag;\n\n      // Replace\n      text = text.replace(`#${tag}`, `<a href=\"https://twitter.com/hashtag/${tag}\">#${tag}</a>`);\n    }\n  }\n\n  return text;\n}\n\n/**\n * @param {import(\"../../types/twitter\").User} user\n * Fix user description from multiple tweets combined obj. DO NOT COMPOSE IN THE RENDERRICHTWEETS FUNCTION\n */\nfunction fixUserDescription(user) {\n  // console.log(tweets.common.user.entities);\n  if (!user.entities) return user;\n\n  // Fix spaces\n  user.description = twemoji.parse(user.description.replace(/\\n/g, \"<br />\"), {\n    folder: \"svg\",\n    ext: \".svg\",\n  });\n\n  const entitiesDescription = user.entities.description;\n\n  user.description = renderMentionsHashtags({\n    text: user.description,\n    hashtags: (entitiesDescription && entitiesDescription.hashtags) || [],\n    mentions: (entitiesDescription && entitiesDescription.mentions) || [],\n  });\n\n  const descriptionURLs = entitiesDescription && entitiesDescription.urls;\n\n  if (!descriptionURLs) return user;\n\n  for (let descriptionURLObj of descriptionURLs) {\n    user.description = user.description.replace(\n      descriptionURLObj.url,\n      `<a href=\"${descriptionURLObj.expanded_url}\" class=\"description-link\" rel=\"noopener noreferrer\">${descriptionURLObj.display_url}</a>`\n    );\n  }\n\n  return user;\n}\n\n/**\n * returns data to make the output look like a tweet\n * @param {TwitterConversationData} tweetObj\n * @param {boolean} embedded\n */\nasync function _renderRichTweets(tweetObj, embedded = false) {\n  // NOTE Keep the order of the functions intact. Any wrong order can break the whole process\n\n  tweetObj = renderMedia(tweetObj);\n  tweetObj = await renderOutsiderLinks(tweetObj, embedded);\n\n  if (tweetObj.entities) {\n    tweetObj.text = renderMentionsHashtags({\n      text: tweetObj.text,\n      mentions: tweetObj.entities.mentions || [],\n      hashtags: tweetObj.entities.hashtags || [],\n    });\n  }\n\n  return tweetObj;\n}\n\n/** @param {TwitterConversationData} tweetObj */\nfunction sanitizeForHandlebars(tweetObj) {\n  // Remove unnecessary data for the handlebars\n\n  if (tweetObj.embeddedTweet) {\n    delete tweetObj.linkWithImage;\n    return tweetObj;\n  }\n\n  // Combine conditions\n  if (tweetObj.embeddedTweet && tweetObj.customMedia) {\n    tweetObj.embeddedTweet.embeddedTweetCardSize = \"large\";\n  }\n\n  return tweetObj;\n}\n\n/**\n *\n * @param {TwitterConversationData} tweetObj\n * @param {string} token\n */\nasync function _renderEmbeddedTweets(tweetObj, token) {\n  // Now work on the embedded tweets\n  // Check\n  if (!tweetObj.referenced_tweets) return tweetObj;\n\n  if (tweetObj.referenced_tweets[0].type === \"replied_to\") return tweetObj;\n\n  const tweetID = tweetObj.referenced_tweets[0].id;\n\n  // Now do the thing\n  const { data } = await getTweetById(tweetID, token);\n\n  // @ts-ignore\n  if (data.errors) return tweetObj;\n\n  // Now render stuff\n  const tweet = getTweetObject(data);\n\n  const richEmbeddedTweet = await _renderRichTweets(tweet, true);\n\n  richEmbeddedTweet.text = twemoji.parse(richEmbeddedTweet.text, {\n    folder: \"svg\",\n    ext: \".svg\",\n  });\n\n  richEmbeddedTweet.embeddedTweetUser = tweet.includes.users.find(\n    (user) => user.id === richEmbeddedTweet.author_id\n  );\n\n  richEmbeddedTweet.embeddedTweetUser.username = `@${richEmbeddedTweet.embeddedTweetUser.username}`;\n\n  richEmbeddedTweet.created_at = formatTimestamp(richEmbeddedTweet.created_at);\n\n  tweetObj.embeddedTweet = richEmbeddedTweet;\n\n  // Delete the irrelevant URLS\n\n  const url = tweetObj.entities.urls.find((url) =>\n    url.expanded_url.replace(\"?s=20\", \"\").includes(tweetID)\n  ).url;\n\n  tweetObj.text = tweetObj.text.replace(url, \"\");\n\n  // Replace rest of status URLS with their displayed version\n  const urls = tweetObj.entities.urls\n    .filter((url) => url.expanded_url.includes(\"/status/\"))\n    .filter((url) => !url.expanded_url.includes(tweetID));\n\n  for (let { url, expanded_url } of urls) {\n    // Replace with the display url\n    tweetObj.text = tweetObj.text.replace(url, expanded_url);\n  }\n\n  return tweetObj;\n}\n\n/**\n *\n * @param {TwitterConversationData} tweetObj\n * @param {string} token\n */\nasync function renderRichTweets(tweetObj, token, isUserTimeline) {\n  tweetObj = await _renderRichTweets(tweetObj);\n  tweetObj = await _renderEmbeddedTweets(tweetObj, token);\n  tweetObj = sanitizeForHandlebars(tweetObj);\n\n  if (!isUserTimeline) delete tweetObj.created_at;\n\n  return tweetObj;\n}\n\nmodule.exports = { renderRichTweets, fixUserDescription, renderMentionsHashtags };\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/rich-rendering.test.js",
    "content": "const { renderMentionsHashtags } = require(\"./rich-rendering\");\nconst { fixUserDescription } = require(\"./rich-rendering\");\n\n//1\ndescribe(\"renderMentionHashtag function working\", () => {\n  //these are just mock data\n  const { mentions, hashtags } = { mentions: [{ username: \"abc\" }], hashtags: [{ tag: \"abc\" }] };\n  const { text } = { text: \"@abc #abc this is cool\" };\n  test(\"replace mention and hashtag in text by its url \", () => {\n    const response = renderMentionsHashtags({ text, mentions, hashtags });\n    expect(response).toBe(\n      '<a href=\"https://twitter.com/abc\">@abc</a> <a href=\"https://twitter.com/hashtag/abc\">#abc</a> this is cool'\n    );\n  });\n\n  test(\"replace mention by mention url\", () => {\n    const response = renderMentionsHashtags({ text, mentions });\n    expect(response).toBe('<a href=\"https://twitter.com/abc\">@abc</a> #abc this is cool');\n  });\n\n  test(\"replace tag by tag url\", () => {\n    const response = renderMentionsHashtags({ text, hashtags });\n    expect(response).toBe('@abc <a href=\"https://twitter.com/hashtag/abc\">#abc</a> this is cool');\n  });\n\n  test(\"expect only text no replcement\", () => {\n    const response = renderMentionsHashtags({ text });\n    expect(response).toBe(\"@abc #abc this is cool\");\n  });\n});\n\n//2\n\ndescribe(\"fixuserDescription function working\", () => {\n  test(\"expecting a transformed description\", () => {\n    //mockuser data\n    const mockUser = {\n      description: \"I'm loving it! : https://t.co/7Ct2VXMx64\",\n      entities: {\n        description: {\n          urls: [\n            {\n              url: \"https://t.co/7Ct2VXMx64\",\n              expanded_url: \"https://ThreadReaderApp.com\",\n              display_url: \"ThreadReaderApp.com\",\n            },\n          ],\n        },\n      },\n    };\n    let response = fixUserDescription(mockUser);\n    expect(response.description).toEqual(\n      `I'm loving it! : <a href=\"https://ThreadReaderApp.com\" class=\"description-link\" rel=\"noopener noreferrer\">ThreadReaderApp.com</a>`\n    );\n  });\n\n  test(\"twimoji working\", () => {\n    const mockUser = {\n      description: \"I'm loving it! 😀\",\n      entities: {},\n    };\n    const response = fixUserDescription(mockUser);\n    expect(response.description).toContain(\"emoji\");\n  });\n});\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/search-endpoint.js",
    "content": "// @ts-check\n\nconst { createCustomTweet, getTweetArray } = require(\"./helpers\");\n\nconst { renderRichTweets, fixUserDescription } = require(\"./rich-rendering\");\n\n/**\n * @param {TwitterConversationResponse} responseJSON\n * @param {string} token\n */\nasync function processSearchResponse(responseJSON, token) {\n  /** @type {import(\"../../types/twitter\").CustomTweetData[]} */\n  const tweets = [];\n\n  let directReplies = getTweetArray(responseJSON).filter(\n    (tweet) =>\n      tweet.referenced_tweets.filter(\n        (ref) => ref.type == \"replied_to\" && ref.id == tweet.conversation_id\n      ).length > 0\n  );\n\n  while (directReplies.length > 0) {\n    let reply_id = directReplies[directReplies.length - 1].id;\n\n    tweets.push(\n      createCustomTweet(\n        await renderRichTweets(directReplies[directReplies.length - 1], token, false)\n      )\n    );\n\n    directReplies = getTweetArray(responseJSON).filter(\n      (tweet) =>\n        tweet.referenced_tweets.filter((ref) => ref.type == \"replied_to\" && ref.id == reply_id)\n          .length > 0\n    );\n  }\n\n  return tweets;\n}\n\n/**\n *\n * @param {TwitterConversationResponse} responseJSON\n * @param {string} token\n */\nasync function processReplies(responseJSON, token) {\n  let indirectReplies = getTweetArray(responseJSON).filter(\n    (tweet) => tweet.in_reply_to_user_id && tweet.in_reply_to_user_id != tweet.author_id\n  );\n\n  /** @type {Reply[]} */\n  let replies = [];\n\n  for (let indirectReply of indirectReplies) {\n    /** @type {import(\"../../types/twitter\").Answer} */\n    let replyAnswer = createCustomTweet(await renderRichTweets(indirectReply, token, false));\n\n    /** @type {Reply} */\n    // @ts-ignore\n    let reply = {\n      id: indirectReply.referenced_tweets.filter((ref) => ref.type === \"replied_to\")[0].id,\n      answer: replyAnswer,\n    };\n    replies.push(reply);\n  }\n  return replies;\n}\n\n/**\n *\n * @param {TwitterConversationResponse} responseJSON\n * @param {Reply[]} replies\n * @param {import(\"../../types/twitter\").CustomTweets} finalTweetsData\n * @param {string} token\n */\nasync function updateReplies(responseJSON, replies, finalTweetsData, token) {\n  for (let reply of replies) {\n    let replyResponseData = responseJSON.data.filter((tweet) => tweet.id === reply.id)[0];\n\n    let tweetOnThreadId = replyResponseData.referenced_tweets.filter(\n      (ref) => ref.type === \"replied_to\"\n    )[0].id;\n\n    let replyUser = responseJSON.includes.users.filter(\n      (user) => replyResponseData.author_id === user.id\n    )[0];\n\n    /** @type {Reply} */\n    let replyData;\n\n    // @ts-ignore\n    replyData = createCustomTweet(await renderRichTweets(replyResponseData, token, false));\n\n    replyData.includes = responseJSON.includes;\n\n    replyUser.username = `@${replyUser.username}`;\n    replyUser.profile_image_url = replyUser.profile_image_url.replace(\"_normal.\", \".\");\n\n    replyData.user = replyUser;\n    replyData.answer = reply.answer;\n\n    replyData.tweet = replyData.tweet\n      .split(\" \")\n      .filter(\n        (text, i, arr) =>\n          !(\n            (text.startsWith(\"<a\") &&\n              arr[i + 1] &&\n              arr[i + 1].includes(\"@\") &&\n              arr[i + 1].includes(\"href\")) ||\n            (text.includes(\"@\") && text.includes(\"href=\"))\n          )\n      )\n      .join(\" \");\n\n    replyData.answer.tweet = replyData.answer.tweet\n      .split(\" \")\n      .filter(\n        (text, i, arr) =>\n          !(\n            (text.startsWith(\"<a\") &&\n              arr[i + 1] &&\n              arr[i + 1].includes(\"@\") &&\n              arr[i + 1].includes(\"href\")) ||\n            (text.includes(\"@\") && text.includes(\"href=\"))\n          )\n      )\n      .join(\" \");\n\n    let finalTweet = finalTweetsData.data.filter((tweet) => tweet.id === tweetOnThreadId)[0];\n\n    if (finalTweet) {\n      if (!finalTweet.replies) finalTweet.replies = [];\n      finalTweet.replies.push(replyData);\n    }\n  }\n\n  return finalTweetsData;\n}\n\nmodule.exports = { processSearchResponse, processReplies, updateReplies };\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/tweet-endpoint.js",
    "content": "const { getTweetObject, getUserObject, createCustomTweet } = require(\"./helpers\");\n\nconst { renderRichTweets, fixUserDescription } = require(\"./rich-rendering\");\n\n// const { doTweetsSearch } = require(\"./tweets-search-request\");\nconst { formatTimestamp } = require(\"../utils/date\");\n\n/**\n *\n * @param {Partial<TwitterConversationResponse>} responseJSON\n * @param {string} token\n */\nasync function processTweetLookup(responseJSON, token) {\n  let tweet = await renderRichTweets(getTweetObject(responseJSON), token, false);\n  let user = getUserObject(responseJSON);\n\n  /** @type {CustomTweetsObject} */\n  let resp = {\n    data: [],\n    common: {},\n  };\n\n  resp.common.created_at = formatTimestamp(getTweetObject(responseJSON).created_at);\n  resp.common.user = { ...user };\n\n  resp.common.user.username = `@${resp.common.user.username}`;\n\n  resp.common.user = fixUserDescription(resp.common.user);\n\n  resp.common.user.profile_image_url = resp.common.user.profile_image_url.replace(\"_normal.\", \".\");\n\n  resp.data.push(createCustomTweet(tweet, user));\n\n  // await doTweetsSearch(tweet.conversation_id, user.username);\n\n  resp.common.count = resp.data.length;\n  return { resp, tweet, user };\n}\n\nmodule.exports = {\n  processTweetLookup,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/tweets-array-Endpoint.test.js",
    "content": "const { processReplies } = require(\"./tweets-array-endpoint\");\nconst { processTweetsArray } = require(\"./tweets-array-endpoint\");\n\ndescribe(\"testing functionality of processeplies() functiion \", () => {\n  test(\"this will test if the function return array of replies\", async () => {\n    //this is mock data\n    const data = [\n      //first tweet\n      {\n        conversation_id: \"123\",\n        entities: {},\n        author_id: \"000\",\n        id: \"123\",\n      },\n      {\n        //indirect reply fulfils all conditions\n        conversation_id: \"123\",\n        entities: {},\n        author_id: \"000\",\n        in_reply_to_user_id: \"111\",\n        source: \"ThreadReaderApp\",\n        id: \"456\",\n\n        referenced_tweets: [\n          {\n            type: \"replied_to\",\n            id: \"789\",\n          },\n        ],\n      },\n    ];\n\n    const response = await processReplies({ data });\n    expect(response).toMatchObject([\n      {\n        id: \"789\",\n        answer: {\n          id: \"456\",\n          tweet: \"\",\n        },\n      },\n    ]);\n  });\n});\n\n//2\n\ndescribe(\"testing functionality of processTweetsArray() function\", () => {\n  test(\"function working and processing Tweets\", async () => {\n    const response = {\n      data: [\n        //first tweet\n        {\n          created_at: \"2018-10-23T23:30:56.000Z\",\n          text: \"lorem ipsum\",\n          entities: {\n            description: {\n              urls: [\n                {\n                  url: \"https://t.co/7Ct2VXMx64\",\n                  expanded_url: \"https://ThreadReaderApp.com\",\n                  display_url: \"ThreadReaderApp.com\",\n                },\n              ],\n            },\n          },\n          id: \"123\",\n          conversation_id: \"123\",\n          author_id: \"895814938995957760\",\n        },\n        //this is reply or another tweet\n        {\n          created_at: \"2018-10-23T23:30:59.000Z\",\n          author_id: \"895814938995957760\",\n          conversation_id: \"123\",\n\n          entities: {\n            description: {\n              urls: [\n                {\n                  url: \"https://t.co/7Ct2VXMx64\",\n                  expanded_url: \"https://ThreadReaderApp.com\",\n                  display_url: \"ThreadReaderApp.com\",\n                },\n              ],\n            },\n          },\n\n          id: \"1054877880033861632\",\n          in_reply_to_user_id: \"895814938995957760\",\n          text: \"One or two minutes later.\",\n          referenced_tweets: [\n            {\n              type: \"replied_to\",\n              id: \"1054877877462659072\",\n            },\n          ],\n        },\n      ],\n      includes: {\n        media: [],\n        users: [\n          {\n            created_at: \"2017-08-11T01:11:39.000Z\",\n            id: \"895814938995957760\",\n            pinned_tweet_id: \"934543319962546176\",\n            profile_image_url:\n              \"https://pbs.twimg.com/profile_images/936421015067824134/g_PfzHXA_normal.jpg\",\n            description: \"I'm a 🤖 to help you read threads more easily. 😀\",\n            entities: {},\n          },\n        ],\n      },\n    };\n\n    const result = await processTweetsArray(response);\n    expect(result).toMatchObject({\n      data: [{ id: \"123\", tweet: \"lorem ipsum\" }],\n      common: { created_at: \"Oct 23, 2018\", user: {}, count: 1 },\n    });\n  });\n});\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/tweets-array-endpoint.js",
    "content": "const { createCustomTweet } = require(\"./helpers\");\n\nconst { renderRichTweets, fixUserDescription } = require(\"./rich-rendering\");\n\nconst { formatTimestamp } = require(\"../utils/date\");\n\n/**\n * @param {TwitterConversationResponse} responseJSON\n */\nasync function processTweetsArray(responseJSON, token) {\n  const tweets = (responseJSON.data || []).map((resData) => ({\n    ...resData,\n    includes: responseJSON.includes,\n  }));\n\n  const firstTweet = tweets.filter((tweet) => tweet.id === tweet.conversation_id)[0];\n  const created_at = firstTweet.created_at;\n  const userObject = responseJSON.includes.users.filter(\n    (user) => user.id === firstTweet.author_id\n  )[0];\n\n  let tweet = await renderRichTweets(firstTweet, token, false);\n  let user = userObject;\n\n  /** @type {CustomTweetsObject} */\n  let resp = {\n    data: [],\n    common: {},\n  };\n\n  resp.common.created_at = formatTimestamp(created_at);\n  resp.common.user = { ...user };\n\n  resp.common.user = fixUserDescription(resp.common.user);\n\n  resp.common.user.profile_image_url = resp.common.user.profile_image_url.replace(\"_normal.\", \".\");\n\n  resp.data.push(createCustomTweet(tweet, user));\n\n  let directReplies = tweets\n    .filter((tweet) => tweet.author_id === resp.common.user.id && tweet.referenced_tweets)\n    .filter(\n      (tweet) =>\n        tweet.referenced_tweets.filter(\n          (ref) => ref.type == \"replied_to\" && ref.id == tweet.conversation_id\n        ).length > 0\n    );\n\n  while (directReplies.length > 0) {\n    let reply_id = directReplies[directReplies.length - 1].id;\n\n    resp.data.push(\n      createCustomTweet(\n        await renderRichTweets(directReplies[directReplies.length - 1], token, false)\n      )\n    );\n    directReplies = tweets\n      .filter((tweet) => tweet.author_id === resp.common.user.id && tweet.referenced_tweets)\n      .filter(\n        (tweet) =>\n          tweet.referenced_tweets.filter((ref) => ref.type == \"replied_to\" && ref.id == reply_id)\n            .length > 0\n      );\n  }\n\n  resp.common.count = resp.data.length;\n  return resp;\n}\n\nasync function processReplies(responseJSON, token) {\n  const firstTweet = responseJSON.data.filter((tweet) => tweet.id === tweet.conversation_id)[0];\n  let indirectReplies = responseJSON.data.filter(\n    (tweet) =>\n      tweet.author_id === firstTweet.author_id &&\n      tweet.in_reply_to_user_id &&\n      tweet.in_reply_to_user_id != tweet.author_id\n  );\n  let replies = [];\n  for (let indirectReply of indirectReplies) {\n    let replyAnswer = createCustomTweet(await renderRichTweets(indirectReply, token, false));\n    let reply = {\n      id: indirectReply.referenced_tweets.filter((ref) => ref.type === \"replied_to\")[0].id,\n      answer: replyAnswer,\n    };\n    replies.push(reply);\n  }\n  return replies;\n}\n\nasync function updateReplies(responseJSON, replies, finalTweetsData, token) {\n  for (let tweet of finalTweetsData.data) {\n    tweet.replies = [];\n  }\n  for (let reply of replies) {\n    let replyData = responseJSON.data.filter((tweet) => tweet.id === reply.id)[0];\n    let tweetOnThreadId = replyData.referenced_tweets.filter((ref) => ref.type === \"replied_to\")[0]\n      .id;\n\n    let replyUser = responseJSON.includes.users.filter(\n      (user) => replyData.author_id === user.id\n    )[0];\n    replyData = createCustomTweet(await renderRichTweets(replyData, token, false));\n    replyUser.username = `@${replyUser.username}`;\n    replyUser.profile_image_url = replyUser.profile_image_url.replace(\"_normal.\", \".\");\n    replyUser = fixUserDescription(replyUser);\n    replyData.user = replyUser;\n    replyData.answer = reply.answer;\n    let finalTweet = finalTweetsData.data.filter((tweet) => tweet.id === tweetOnThreadId)[0];\n    if (finalTweet) {\n      finalTweet.replies.push(replyData);\n    }\n  }\n  return finalTweetsData;\n}\n\nmodule.exports = {\n  processTweetsArray,\n  processReplies,\n  updateReplies,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/user-timeline-Endpoint.test.js",
    "content": "const { processUserTweets } = require(\"./user-timeline-endpoint\");\n\ndescribe(\"testing functionality of user timeline\", () => {\n  const responseJSON = {\n    //1\n    //mock data\n    data: [\n      {\n        conversation_id: \"123\",\n        id: \"123\",\n        text: \"lorem ipsum \",\n        attachments: {},\n        author_id: \"25472458\",\n        created_at: \"2020-12-28T10:50:45.000Z\",\n      },\n    ],\n    includes: {\n      media: [],\n      users: [\n        {\n          description: \"Rotterdam School of Management\",\n          username: \"abc\",\n          url: \"http://t.co/6Uiy3qfoiW\",\n          id: \"25472458\",\n          created_at: \"2009-03-20T06:38:54.000Z\",\n          profile_image_url:\n            \"https://pbs.twimg.com/profile_images/1034856145167503360/fhRoau55_normal.jpg\",\n          name: \"RSM\",\n        },\n      ],\n    },\n  };\n\n  const userName = \"abc\";\n\n  test(\"this should process user Tweet\", async () => {\n    const response = await processUserTweets(userName, responseJSON, \"\");\n    expect(response).toMatchObject({\n      common: {\n        user: {\n          id: \"25472458\",\n          username: \"@abc\",\n          profile_image_url:\n            \"https://pbs.twimg.com/profile_images/1034856145167503360/fhRoau55.jpg\",\n        },\n        count: 1,\n      },\n      data: [\n        {\n          id: \"123\",\n          tweet: \"lorem ipsum \",\n        },\n      ],\n    });\n  });\n\n  //2\n  test(\"this make sure its user timeline\", async () => {\n    const response = await processUserTweets(userName, responseJSON, \"\");\n    expect(response).toHaveProperty(\"common.created_at\");\n  });\n});\n"
  },
  {
    "path": "twindle-cli/src/twitter/transformations/user-timeline-endpoint.js",
    "content": "const { createCustomTweet } = require(\"./helpers\");\n\nconst { renderRichTweets, fixUserDescription } = require(\"./rich-rendering\");\n\nconst { formatTimestamp } = require(\"../utils/date\");\n\n/**\n * @param {screenName} string\n * @param {TwitterConversationResponse} responseJSON\n * @param {string} token\n */\nasync function processUserTweets(screenName, responseJSON, token) {\n  let tweets = (responseJSON.data || []).map((resData) => ({\n    ...resData,\n    includes: responseJSON.includes,\n  }));\n\n  const userObject = responseJSON.includes.users.filter((user) => user.username === screenName)[0];\n\n  let user = userObject;\n\n  /** @type {CustomTweetsObject} */\n  let resp = {\n    data: [],\n    common: {},\n  };\n\n  resp.common.created_at = formatTimestamp(new Date() + \"\");\n  resp.common.user = { ...user };\n\n  resp.common.user = fixUserDescription(resp.common.user);\n\n  resp.common.user.profile_image_url = resp.common.user.profile_image_url.replace(\"_normal.\", \".\");\n  resp.common.user.username = \"@\" + resp.common.user.username;\n\n  let conversations = [];\n  tweets = tweets.filter(\n    (t) => !t.in_reply_to_user_id || (t.in_reply_to_user_id && t.in_reply_to_user_id === user.id)\n  );\n  for (let tweet of tweets) {\n    if (!conversations.includes(tweet.conversation_id)) {\n      conversations.push(tweet.conversation_id);\n      let threadTweets = tweets.filter((t) => tweet.conversation_id === t.conversation_id);\n      for (i = threadTweets.length - 1; i >= 0; i--) {\n        resp.data.push(createCustomTweet(await renderRichTweets(threadTweets[i], token, true)));\n      }\n    }\n  }\n\n  resp.common.count = resp.data.length;\n  return resp;\n}\n\nmodule.exports = {\n  processUserTweets,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/twitter.js",
    "content": "// @ts-check\nconst { getConversationById, getTweetById } = require(\"./api\");\n\nconst Scraping = require(\"./scraping\");\n\n// const { firstTweet, finalProcessedTweet } = require(\"./test/data\");\nconst TweetEndpointValidation = require(\"./validations/tweet-endpoint\");\n\nconst TweetEndpointTransformation = require(\"./transformations/tweet-endpoint\");\nconst TweetArrayEndpointTransformation = require(\"./transformations/tweets-array-endpoint\");\nconst SearchEndpointTransformation = require(\"./transformations/search-endpoint\");\nconst UserTimelineEndpointTransformation = require(\"./transformations/user-timeline-endpoint\");\n\nconst { ValidationErrors } = require(\"./error\");\nconst { getUserTweets } = require(\"./api/twitter-endpoints/user_timeline\");\n\nconst spinner = require(\"../spinner\");\n\n/**\n * @param {Object} param\n * @param {number} param.initial\n * @param {number} param.final\n */\nfunction counter({ initial, final }) {\n  let current = initial - 1;\n\n  function increment() {\n    current++;\n\n    spinner.text = `Fetching ${current} of ${final} thread${final !== 1 ? \"s\" : \"\"}      `;\n  }\n\n  return { increment };\n}\n\n/**\n * @param {string[]} ids\n * @param {string} token\n */\nconst getTweetsFromArray = async (ids, includeReplies, token) => {\n  let responseJSON = await getTweetById(ids.join(\",\"), token);\n\n  if (responseJSON.status === \"error\") {\n    throw new Error(\"something wrong\");\n  }\n\n  // do processing\n  let finalTweetsData = await TweetArrayEndpointTransformation.processTweetsArray(\n    responseJSON.data,\n    token\n  );\n\n  if (includeReplies) {\n    let replies = await TweetArrayEndpointTransformation.processReplies(responseJSON.data, token);\n    let replyIds = replies.map((r) => r.id);\n\n    if (replyIds.length > 0) {\n      responseJSON = await getTweetById(replyIds.join(\",\"), token);\n\n      if (responseJSON.data && !responseJSON.data.errors) {\n        finalTweetsData = await TweetArrayEndpointTransformation.updateReplies(\n          responseJSON.data,\n          replies,\n          finalTweetsData,\n          token\n        );\n      }\n    }\n  }\n  return [finalTweetsData];\n};\n\n/**\n * @param {string} screenName\n * @param {string} token\n */\nasync function getTweetsFromUser(screenName, token) {\n  let responseJSON = await getUserTweets(screenName, token);\n\n  if (responseJSON.status === \"error\") {\n    throw new Error(\"something wrong\");\n  }\n  // do processing\n  let finalTweetsData = await UserTimelineEndpointTransformation.processUserTweets(\n    screenName,\n    responseJSON.data,\n    token\n  );\n  return [finalTweetsData];\n}\n\n/**\n * @param {string} ids\n * @param {string} token\n */\nconst getTweetsFromThreads = async (ids, includeReplies, token) => {\n  const { increment } = counter({ initial: 1, final: ids.split(\",\").length });\n\n  let { data: responseJSON, status } = await getTweetById(ids, token);\n\n  if (status === \"error\") {\n    throw new Error(\"something wrong\");\n  }\n\n  //console.log(responseJSON.data.includes);\n  /** @type {TwitterConversationData[]}  */\n  const tweets = (responseJSON.data || []).map((resData) => ({\n    ...resData,\n    includes: responseJSON.includes,\n  }));\n\n  /** @type {import(\"../types/twitter\").CustomTweets[]} */\n  const tweetThreads = [];\n\n  /** @type {string[]} */\n  const usersNames = [];\n\n  for (let loopTweet of tweets) {\n    increment();\n\n    /** @type {import(\"../types/twitter\").CustomTweets} */\n    let finalTweetsData = {\n      common: {\n        id: \"\",\n        created_at: \"\",\n        count: 0,\n        user: {\n          id: \"\",\n          username: \"\",\n          name: \"\",\n          profile_image_url: \"\",\n          description: \"\",\n        },\n      },\n      data: [],\n    };\n\n    // do validation\n    // @Mira-Alf processResponse checks into the `errors` field to check if the tweet exists or not. What\n    // do we do to do that check now?\n    const validation = TweetEndpointValidation.processResponse({ data: [loopTweet] });\n\n    if (validation.status === \"error\") {\n      if (validation.error instanceof ValidationErrors.TweetNotFirstOfThreadError) {\n        // const id = getConversationId({ data: [loopTweet] });\n        loopTweet = (await getTweetById(loopTweet.conversation_id, token)).data[0];\n      } else if (validation.error instanceof ValidationErrors.TweetOlderThan7DaysError) {\n        if (process.env.SCRAPE_TWEETS === \"true\") {\n          const tweetIDs = await Scraping.getTweetIDs(loopTweet.id);\n          // @ts-ignore\n          tweetThreads.push(...(await getTweetsFromArray(tweetIDs, includeReplies, token)));\n          continue;\n        }\n        throw validation.error;\n      } else throw validation.error;\n    }\n\n    // Do processing\n    const {\n      resp: transformedFirstTweet,\n      tweet,\n      user,\n    } = await TweetEndpointTransformation.processTweetLookup(\n      { data: [loopTweet], includes: loopTweet.includes },\n      token\n    );\n\n    finalTweetsData = { ...finalTweetsData, ...transformedFirstTweet };\n\n    //get second api\n    const conversationTweetsData = await getConversationById(\n      tweet.conversation_id,\n      user.username,\n      token\n    );\n\n    let transformedSecondTweets = [];\n\n    if (conversationTweetsData.data.meta.result_count !== 0) {\n      transformedSecondTweets = await SearchEndpointTransformation.processSearchResponse(\n        conversationTweetsData.data,\n        token\n      );\n    }\n\n    finalTweetsData = {\n      ...finalTweetsData,\n      data: [...finalTweetsData.data, ...transformedSecondTweets],\n    };\n\n    finalTweetsData.common.count = finalTweetsData.data.length;\n\n    if (!usersNames.includes(finalTweetsData.common.user.name)) {\n      usersNames.push(finalTweetsData.common.user.name);\n    }\n\n    if (includeReplies) {\n      /** @type {Reply[]} */\n      let replies = await SearchEndpointTransformation.processReplies(\n        conversationTweetsData.data,\n        token\n      );\n\n      let replyIds = replies.map(({ id }) => id);\n      if (replyIds.length > 0) {\n        const repliesResponse = await getTweetById(replyIds.join(\",\"), token);\n\n        // console.log(responseJSON);\n\n        if (repliesResponse.data && !repliesResponse.data.errors) {\n          finalTweetsData = await SearchEndpointTransformation.updateReplies(\n            repliesResponse.data,\n            replies,\n            finalTweetsData,\n            token\n          );\n        }\n      }\n    }\n\n    // console.log(finalTweetsData);\n\n    tweetThreads.push(finalTweetsData);\n  }\n\n  // Remove duplicates generating due to puppeteer scraping\n  return tweetThreads;\n};\n\nmodule.exports = {\n  getTweetsFromArray,\n  getTweetsFromUser,\n  getTweetsFromThreads,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/utils/date.js",
    "content": "const { format } = require(\"date-fns\");\n\n/**\n * Formats date into the format of `Nov 12, 2020`\n * @param {string} timestamp\n */\nconst formatTimestamp = (timestamp) => {\n  if (!timestamp) throw new Error(\"no-timestamp-passed\");\n\n  return format(new Date(timestamp), \"MMM d, yyyy\");\n};\n\nmodule.exports = {\n  formatTimestamp,\n};\n"
  },
  {
    "path": "twindle-cli/src/twitter/utils/date.test.js",
    "content": "const { formatTimestamp } = require(\"./date\");\n\ntest(\"#format() should correctly format the date\", () => {\n  const aDate = new Date(\"2020-01-01T00:00:00\") + \"\";\n\n  const transformedDate = formatTimestamp(aDate);\n  const expectedOutput = \"Jan 1, 2020\";\n\n  expect(transformedDate).toBe(expectedOutput);\n});\n\ntest(\"should fail on wrong date to wrong formatted date\", () => {\n  const aDate = new Date(\"2020-11-12T00:00:00\") + \"\";\n\n  const transformedDate = formatTimestamp(aDate);\n  const expectedOutput = \"Jan 1, 2020\";\n\n  expect(transformedDate).not.toBe(expectedOutput);\n});\n\ntest(\"should throw error on no date passed\", () => {\n  /**\n   * Observations:\n   * 1. to call `toThrow`, expect should get a function only, not a value\n   * 2. toThrow can have the error message, or an error instance as the input\n   */\n  expect(() => formatTimestamp()).toThrow(\"no-timestamp-passed\");\n\n  expect(formatTimestamp).toThrow(\"no-timestamp-passed\");\n\n  // This doesn't work, as `formatTimeStamp()` is a value, not a function.\n  // Values can't throw errors, only functions can\n  // expect(formatTimestamp()).toThrow(\"no-timestamp-passed\");\n\n  expect(formatTimestamp).toThrow(new Error(\"no-timestamp-passed\"));\n});\n"
  },
  {
    "path": "twindle-cli/src/twitter/validations/tweet-endpoint.js",
    "content": "//  options for the tweet endpoint\nconst { differenceInDays } = require(\"date-fns\");\nconst { ValidationErrors, ApiErrors } = require(\"../error\");\n\n/**\n *\n * @param {TwitterConversationData} tweet\n */\nconst isFirstTweetOfThread = (tweet) => {\n  return tweet.id === tweet.conversation_id;\n};\n\n/** @param {TwitterConversationData} tweet */\nconst tweetOlderThanSevenDays = (tweet) => {\n  const currentTime = new Date();\n  const tweetCreatedAt = new Date(tweet.created_at);\n\n  return differenceInDays(currentTime, tweetCreatedAt) >= 7;\n};\n\n/** @param {TwitterConversationResponse} responseJSON */\nconst tweetDeleted = (responseJSON) => {\n  if (responseJSON.errors === undefined) return false;\n  return true;\n};\n\n/** @param {TwitterConversationResponse} responseJSON */\nconst getTweetObject = (responseJSON) => {\n  return {\n    ...responseJSON.data[0],\n    includes: responseJSON.includes,\n  };\n};\n\n/**\n * Process the data received from Twitter API\n * @param {Partial<TwitterConversationResponse>} response\n */\nfunction processResponse(response) {\n  if (tweetDeleted(response)) {\n    return {\n      status: \"error\",\n      error: new ApiErrors.TweetDoesNotExist(),\n    };\n  }\n\n  let tweet = getTweetObject(response);\n\n  if (tweetOlderThanSevenDays(tweet)) {\n    return {\n      status: \"error\",\n      error: new ValidationErrors.TweetOlderThan7DaysError(),\n    };\n  }\n\n  if (!isFirstTweetOfThread(tweet)) {\n    // This ain't the first tweet of the thread. Find out the first of this thread\n    // await doTweetLookup(tweet.conversation_id);\n    return {\n      status: \"error\",\n      error: new ValidationErrors.TweetNotFirstOfThreadError(),\n    };\n  }\n\n  return {\n    status: \"ok\",\n  };\n}\n\nmodule.exports = {\n  processResponse,\n};\n"
  },
  {
    "path": "twindle-cli/src/types/twitter.ts",
    "content": "export interface Mention {\n  start: number;\n  end: number;\n  username: string;\n}\n\nexport interface Hashtag {\n  start: number;\n  end: number;\n  tag: string;\n}\n\nexport interface EntityUrl {\n  start: number;\n  end: number;\n  /** format: `https://t.co/[REST]` */\n  url: string;\n  expanded_url: string;\n  /** The possibly truncated URL */\n  display_url: string;\n  status: number;\n  title: string;\n  description: string;\n  unwound_url: string;\n  images?: {\n    url: string;\n    height: number;\n    width: number;\n  }[];\n}\n\nexport interface Attachments {\n  poll_id?: string[];\n  media_keys?: string[];\n}\n\nexport interface User {\n  username: string;\n  description: string;\n  profile_image_url: string;\n  verified: boolean;\n  location: string;\n  created_at: string;\n  name: string;\n  protected: boolean;\n  id: string;\n  url?: string;\n  public_metrics: {\n    followers_count: number;\n    following_count: number;\n    tweet_count: number;\n    listed_count: number;\n  };\n  entities?: {\n    url?: {\n      urls: EntityUrl[];\n    };\n    description?: {\n      urls?: EntityUrl[];\n      mentions?: Mention[];\n      hashtags?: Hashtag[];\n    };\n  };\n}\n\nexport interface ConversationResponseData {\n  conversation_id: string;\n  id: string;\n  text: string;\n  author_id: string;\n  created_at: string;\n  in_reply_to_user_id: string;\n  public_metrics: {\n    retweet_count: number;\n    reply_count: number;\n    like_count: number;\n    quote_count: number;\n  };\n  entities?: {\n    mentions?: Mention[];\n    hashtags?: Hashtag[];\n    urls?: EntityUrl[];\n  };\n  referenced_tweets?: {\n    type: \"retweeted\" | \"quoted\" | \"replied_to\";\n    id: string;\n  }[];\n  attachments?: Attachments;\n  /** Not returned by twitter API. Added by the code */\n  includes?: ConversationIncludes;\n\n  customMedia?: CustomMedia;\n\n  linkWithImage?: Partial<LinkWithImage>;\n\n  embeddedTweet?: ConversationResponseData;\n  embeddedTweetUser?: User;\n  embeddedTweetCardSize: \"large\" | \"small\";\n}\n\nexport interface IncludesMedia {\n  height: number;\n  width: number;\n  type: \"photo\" | \"video\" | \"animated_gif\";\n  url: string;\n  preview_image_url: string;\n  media_key: string;\n}\n\nexport interface ConversationIncludes {\n  media?: IncludesMedia[];\n  users: User[];\n}\n\n/**\n * Types from response after cleanup\n */\nexport interface ConversationResponse {\n  data: ConversationResponseData[];\n  includes: ConversationIncludes;\n  meta: {\n    newest_id: string;\n    oldest_id: string;\n    result_count: number;\n  };\n  errors?: any;\n}\n\nexport type CustomMedia = {\n  [media in \"photo\" | \"video\" | \"animated_gif\"]: {\n    height: number;\n    width: number;\n    preview_img_url: string;\n    /** Format: pic.twitter.com/*. No HTTPS in the link */\n    link: string;\n  }[];\n};\n\nexport interface LinkWithImage {\n  expanded_url: string;\n  images: {\n    url: string;\n    width: number;\n    height: number;\n  }[];\n  title: string;\n  description: string;\n  domain: string;\n}\n\nexport interface Answer extends Pick<CustomTweetData, \"id\" | \"tweet\"> {}\n\nexport interface Reply extends Pick<CustomTweetData, \"id\" | \"tweet\"> {\n  answer: Answer;\n  user?: User;\n  includes?: ConversationIncludes;\n}\n\nexport interface CustomTweetData {\n  id: string;\n  created_at?: string;\n  tweet: string;\n  customMedia?: CustomMedia;\n\n  linkWithImage?: Partial<LinkWithImage>;\n\n  embeddedTweet?: ConversationResponseData;\n  embeddedTweetUser?: User;\n\n  replies?: Reply[];\n}\n\nexport interface CommonData {\n  id: string;\n  count: number;\n  created_at?: string;\n  user?: Partial<User>;\n}\n\nexport interface CustomTweets {\n  common: CommonData;\n  data: CustomTweetData[];\n}\n"
  },
  {
    "path": "twindle-cli/src/types.js",
    "content": "/**\n * @typedef {import(\"./types/twitter\").ConversationResponse} TwitterConversationResponse\n * @typedef {import(\"./types/twitter\").ConversationResponseData} TwitterConversationData\n * @typedef {{threads: import(\"./types/twitter\").CustomTweets[]}} CustomTweetsObject\n * @typedef {import(\"./types/twitter\").CustomTweetData} CustomTweetData\n * @typedef {import(\"./types/twitter\").CustomMedia} CustomMedia\n * @typedef {import(\"./types/twitter\").LinkWithImage} LinkWithImage\n * @typedef {import(\"./types/twitter\").ConversationResponseData} EmbeddedTweetData\n * @typedef {import(\"./types/twitter\").Reply} Reply\n * @typedef {import(\"./types/twitter\").CommonData} CommonData\n */\n"
  },
  {
    "path": "twindle-cli/src/utils/date.js",
    "content": "const { format } = require(\"date-fns\");\n\n/**\n * Formats date into the format of `Nov 12, 2020`\n * @param {string} timestamp\n */\nconst formatTimestamp = (timestamp) => format(new Date(timestamp), \"MMM d, yyyy\");\n\nmodule.exports = {\n  formatTimestamp,\n};\n"
  },
  {
    "path": "twindle-cli/src/utils/env.js",
    "content": "const fs = require(\"fs\");\n\nconst homeDirectory = (process.env.APPDATA || \n    (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : \n        process.env.HOME + \"/.local/share\")) + '/twindle.json';\nlet jsonConfig = undefined;\nif(fs.existsSync(homeDirectory)) {\n    jsonConfig = JSON.parse(fs.readFileSync(homeDirectory));\n}\n\nfunction getBearerToken() {\n    if(jsonConfig === undefined) {\n        return process.env.TWITTER_AUTH_TOKEN;\n    } else return jsonConfig.token;\n}\n\n\nfunction getTwindleLibraryPath() {\n    if(jsonConfig === undefined) {\n        return process.env.TWINDLE_LIBRARY_PATH;\n    } else return jsonConfig.twindleLibrary;\n}\n\nfunction getEmailConfig() {\n    if(jsonConfig === undefined) { \n        const emailConfig = {};\n        emailConfig.kindleEmail = process.env.KINDLE_EMAIL;\n        emailConfig.senderEmail = process.env.EMAIL;\n        emailConfig.password = process.env.PASS;\n        emailConfig.host = process.env.HOST;\n        emailConfig.port = process.env.PORT;\n        return emailConfig;\n    } else return jsonConfig;\n}\n\nmodule.exports = { getBearerToken, getTwindleLibraryPath, getEmailConfig };"
  },
  {
    "path": "twindle-cli/src/utils/helpers.js",
    "content": "/**\n * Wait for `ms` amount of milliseconds\n * @param {number} ms\n */\nconst waitFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Validate email\n * @param {string} email\n */\nfunction isValidEmail(email) {\n  const re = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/i;\n  return re.test(email);\n}\n\nconst kleur = require(\"kleur\");\nconst formatLogColors = {\n  epub: kleur.green,\n  pdf: kleur.red,\n  mobi: kleur.yellow,\n};\n\nmodule.exports = { waitFor, isValidEmail, formatLogColors };\n"
  },
  {
    "path": "twindle-cli/src/utils/helpers.test.js",
    "content": "const { isValidEmail } = require(\"./helpers\");\n\ntest(\"Should match email format #1\", () => {\n  expect(isValidEmail(\"valid-email@email.com\")).toBe(true);\n  expect(isValidEmail(\"valid+x@gmail.com\")).toBe(true);\n  expect(isValidEmail(\"v@g.com\")).toBe(true);\n});\n\ntest(\"Should be false if email is empty\", () => {\n  expect(isValidEmail(\"valid-email@.com\")).toBe(false);\n  expect(isValidEmail(\"\")).toBe(false);\n  expect(isValidEmail(null)).toBe(false);\n});\n"
  },
  {
    "path": "twindle-cli/src/utils/image.js",
    "content": "const fetch = require(\"node-fetch\");\n\n/**\n * Encode images from given url to base64\n * @param {string} url\n * @returns {Promise<string>} base64 encoded image string\n */\nasync function encodeImage(url) {\n  const data = await fetch(url).then((r) => r.arrayBuffer());\n  const b = Buffer.from(data);\n\n  const format = url.split(\".\")[1];\n  const encoded = `data:image/${format};base64,` + b.toString(\"base64\");\n\n  return encoded;\n}\n\nmodule.exports = { encodeImage };\n"
  },
  {
    "path": "twindle-cli/src/utils/image.test.js",
    "content": "const { encodeImage } = require(\"./image\");\n\ndescribe(\"using mock url to test encoding function\", () => {\n  test(\"correct response\", async () => {\n    //this is to test that function is responding\n    const response = await encodeImage(\"https://google.com\");\n    expect(response).toMatch(/^(data:image)/);\n  });\n\n  test(\"undefined response when no url passed\", async () => {\n    await expect(() => encodeImage()).rejects.toThrow(Error);\n  });\n});\n"
  },
  {
    "path": "twindle-cli/src/utils/library.js",
    "content": "const fs = require(\"fs\");\nconst path = require(\"path\");\nconst { green, cyan } = require(\"kleur\");\nconst { getTwindleLibraryPath } = require(\"./env\");\nconst libraryPath = getTwindleLibraryPath();\n\nconst getLibraryPath = (...extraPaths) => {\n  const libPath = libraryPath\n    ? libraryPath\n    : path.join(process.cwd(), \"TwindleLibrary\");\n\n  // if extra paths given, combine them with the library path\n  if (extraPaths) {\n    return path.join(libPath, ...extraPaths);\n  }\n  return libPath;\n};\nfunction createLibraryIfNotExists() {\n  const exists = fs.existsSync(getLibraryPath());\n  if (!exists) {\n    console.log(`\nNo ${cyan(\"TwindleLibrary\")}📚 found, creating a new TwindleLibrary to\n${green(getLibraryPath())}\n\nIf you wish to set your own TwindleLibrary, set the TWINDLE_LIBRARY_PATH enviroment variable to your prefered location\n`);\n    fs.mkdirSync(getLibraryPath());\n  }\n}\n\nmodule.exports = { createLibraryIfNotExists, getLibraryPath };\n"
  },
  {
    "path": "twindle-cli/src/utils/path.js",
    "content": "const path = require(\"path\");\nconst { getLibraryPath } = require(\"./library\");\n\nconst getOutputFilePath = (outputFilename, outputFormat) => {\n  const n = outputFilename.split(\".\");\n  const includesFormat = n.length > 1 && n[1].includes(outputFormat);\n  let outputFilePath = getLibraryPath(outputFilename);\n\n  if (!includesFormat) outputFilePath += \".\" + outputFormat;\n  console.log(outputFilePath);\n  return outputFilePath;\n};\n\nconst getFilenameFromPath = (filename) => filename.split(path.sep).reverse()[0];\n\nmodule.exports = { getFilenameFromPath, getOutputFilePath };\n"
  },
  {
    "path": "twindle-cli/src/utils/path.test.js",
    "content": "const { getOutputFilePath } = require(\"./path\");\n\ndescribe(\"testing for correct output file path\", () => {\n  test(\"is path valid\", () => {\n    const response = getOutputFilePath(\"path/of/file.pdf\", \"pdf\");\n    expect(response).toMatch(/.+\\.pdf$/);\n  });\n\n  test(\"no path and format provided\", () => {\n    expect(() => getOutputFilePath()).toThrow(Error);\n  });\n});\n"
  },
  {
    "path": "twindle-cli/src/utils/send-email.js",
    "content": "const nodemailer = require(\"nodemailer\");\nconst { UserError } = require(\"./../helpers/error\");\nconst { getEmailConfig } = require(\"./env\");\n\nconst config = getEmailConfig();\nconst emailConfig = {\n  host: config.host,\n  port: config.port,\n  secure: true,\n  auth: {\n    user: config.senderEmail,\n    pass: config.password,\n  },\n};\n\nasync function sendMail({ subject, emailTo, attachments }) {\n  try {\n    // create reusable transporter object using the default SMTP transport\n    const transporter = nodemailer.createTransport(emailConfig);\n\n    // send mail with defined transport object\n    const info = await transporter.sendMail({\n      from: emailConfig.auth.user, // sender address\n      to: emailTo, // list of receivers\n      subject, // Subject line\n      text: \"Here is your twindle thread\",\n      attachments: [\n        ...attachments,\n        // {\n        // \tfilename: \"twindle.epub\",\n        // \tcontent: Buffer.from(file, \"base64\"),\n        // \tcontentType: \"application/epub+zip\",\n        // },\n      ],\n    });\n    console.devLog(\"Email sent: %s\", info.messageId);\n  } catch (e) {\n    console.log(e);\n    throw new UserError(\n      \"sending-email-error\",\n      \"Sending Email failed. Please check Mail server credentials\"\n    );\n  }\n}\n\nmodule.exports = { sendMail };\n"
  },
  {
    "path": "twindle-cli/src/utils/send-to-kindle.js",
    "content": "const fs = require(\"fs\");\nconst { sendMail } = require(\"./send-email\");\nconst { getFilenameFromPath } = require(\"../utils/path\");\nconst { blue, red } = require(\"kleur\");\n\nasync function sendToKindle(kindleEmail, filePath) {\n  const filename = getFilenameFromPath(filePath);\n  await sendMail({\n    emailTo: [kindleEmail],\n    subject: \"From Twindle \" + new Date().toLocaleDateString(),\n    attachments: [\n      {\n        filename,\n        path: filePath,\n      },\n    ],\n  });\n  console.log(\n    `Your file ${red(filename)} has been sent to Kindle email address ${blue(kindleEmail)}`\n  );\n}\n\nmodule.exports = { sendToKindle };\n"
  }
]