{"componentChunkName":"component---src-templates-blog-post-template-js","path":"/2017/05/23/twitter-bot-playground/","result":{"data":{"mdx":{"id":"66e6994d-da73-5c1e-8aae-06db8b54f4d8","frontmatter":{"title":"Twitter bot playground","date":"2017 May 23rd","cover":{"id":"0dd33083-a9ec-5ff7-b762-9cc139e69ffe","publicURL":"/static/cover-29551c2c172142b4901a7052fb11597d.jpg"}},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"date\": \"2017-05-23T00:00:00.000Z\",\n  \"title\": \"Twitter bot playground\",\n  \"tags\": [\"information\", \"guide\"],\n  \"published\": true,\n  \"cover\": \"./cover.jpg\"\n};\n\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\n\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"How to build and deploy a multifunctional Twitter bot!\"), mdx(\"p\", null, \"This is a reference for me and anyone else that\\u2019s interested in\\nTwitter bots in JavaScript.\"), mdx(\"p\", null, \"All of the examples here use the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"npm\"), \" package \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/package/twit\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"twit\"), \".\"), mdx(\"p\", null, \"We\\u2019ll go through setting up a simple bot so each of these examples can\\nbe run with it.\"), mdx(\"p\", null, \"I\\u2019m going to assume that you have \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nodejs\"), \" installed along with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm\"), \"\\nand that you are comfortable with the terminal.\"), mdx(\"p\", null, \"If you are not familiar node or do not have your environment set up to\\nuse it take a look at the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/spences10/twitter-bot-bootstrap#twitter-bot-bootstrap\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"README.md\"), \" on\\nmy \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/spences10/twitter-bot-bootstrap\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Twitter bot bootstrap\"), \" repo which details\\ngetting a Twitter application set up and a development environment\\nwith c9.\"), mdx(\"p\", null, \"A great resource is \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/amandeepmittal\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Aman Mittal\\u2019s\"), \" \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/amandeepmittal/awesome-twitter-bots\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Awesome\\nTwitter bots\"), \" repo which has resources and bot\\nexamples.\"), mdx(\"p\", null, \"A lot of this information is already out there I\\u2019m hoping this is all\\nthe information someone will need to get started with their own\\nTwitter bot. I\\u2019m doing this for my own learning and hopefully other\\npeople will get something out of this as well.\"), mdx(\"h2\", {\n    \"id\": \"set-up-the-bot\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#set-up-the-bot\",\n    \"aria-label\": \"set up the bot permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Set up the bot\"), mdx(\"p\", null, \"Before touching the terminal or writing any code we\\u2019ll need to create\\na \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://apps.twitter.com/app/new\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Twitter app\"), \" to get our API keys, we\\u2019ll need them all:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"Consumer Key (API Key)\\nConsumer Secret (API Secret)\\nAccess Token\\nAccess Token Secret\\n\")), mdx(\"p\", null, \"Keep the keys somewhere safe so you can use them again when you need\\nthem, we\\u2019re going to be using them in the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/package/dotenv\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \".env\"), \" file we\\u2019re\\ngoing to create.\"), mdx(\"p\", null, \"We\\u2019re using \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/package/dotenv\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"dotenv\"), \" so that if at some point in the future we\\nwant to add our bot to GitHub the Twitter API keys are not added to\\nGitHub for all to see.\"), mdx(\"p\", null, \"Starting from scratch, create a new folder via the terminal and\\ninitialise the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" via \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yarn\"), \" we\\u2019ll need \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"twit\"), \"\\nand \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"dotenv\"), \" for all these examples.\"), mdx(\"p\", null, \"I\\u2019ll be using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yarn\"), \" for all these examples, you can use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm\"), \" if you\\nprefer.\"), mdx(\"p\", null, \"Terminal commands:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"mkdir tweebot-play\\ncd tweebot-play\\nyarn init -y\\nyarn add twit dotenv\\ntouch .env .gitignore index.js\\n\")), mdx(\"p\", null, \"If you take a look at the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" that was created it should\\nlook something like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-json\"\n  }), \"{\\n  \\\"name\\\": \\\"tweebot-play\\\",\\n  \\\"version\\\": \\\"1.0.0\\\",\\n  \\\"main\\\": \\\"index.js\\\",\\n  \\\"author\\\": \\\"Scott Spence <spences10apps@gmail.com> (https://spences10.github.io/)\\\",\\n  \\\"license\\\": \\\"MIT\\\",\\n  \\\"dependencies\\\": {\\n    \\\"dotenv\\\": \\\"^4.0.0\\\",\\n    \\\"twit\\\": \\\"^2.2.5\\\"\\n  }\\n}\\n\")), mdx(\"p\", null, \"Add an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm\"), \" script to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" to kick off the bot when\\nwe\\u2019re testing and looking for output:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-json\"\n  }), \"  \\\"scripts\\\": {\\n    \\\"start\\\": \\\"node index.js\\\"\\n  },\\n\")), mdx(\"p\", null, \"It should look something like this now:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-json\"\n  }), \"{\\n  \\\"name\\\": \\\"tweebot-play\\\",\\n  \\\"version\\\": \\\"1.0.0\\\",\\n  \\\"main\\\": \\\"index.js\\\",\\n  \\\"scripts\\\": {\\n    \\\"start\\\": \\\"node index.js\\\"\\n  },\\n  \\\"author\\\": \\\"Scott Spence <spences10apps@gmail.com> (https://spences10.github.io/)\\\",\\n  \\\"license\\\": \\\"MIT\\\",\\n  \\\"dependencies\\\": {\\n    \\\"dotenv\\\": \\\"^4.0.0\\\",\\n    \\\"twit\\\": \\\"^2.2.5\\\"\\n  }\\n}\\n\")), mdx(\"p\", null, \"Now we can add the following pointer to the bot in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.js\"), \", like\\nso:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"require('./src/bot')\\n\")), mdx(\"p\", null, \"So when we use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yarn start\"), \" to run the bot it calls the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"index.js\"), \"\\nfile which runs the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" file from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder we\\u2019re going to\\ncreate.\"), mdx(\"p\", null, \"Now we add our API keys to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".env\"), \" file, it should look something\\nlike this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"CONSUMER_KEY=AmMSbxxxxxxxxxxNh4BcdMhxg\\nCONSUMER_SECRET=eQUfMrHbtlxxxxxxxxxxkFNNj1H107xxxxxxxxxx6CZH0fjymV\\nACCESS_TOKEN=7xxxxx492-uEcacdl7HJxxxxxxxxxxecKpi90bFhdsGG2N7iII\\nACCESS_TOKEN_SECRET=77vGPTt20xxxxxxxxxxxZAU8wxxxxxxxxxx0PhOo43cGO\\n\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".gitignore\"), \" file we need to add \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".env\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"node_modules\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"# Dependency directories\\nnode_modules\\n\\n# env files\\n.env\\n\")), mdx(\"p\", null, \"Then init git:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"git init\\n\")), mdx(\"p\", null, \"Ok, now we can start to configure the bot, we\\u2019ll need a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder a\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" file and a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"config.js\"), \" file.\"), mdx(\"p\", null, \"Terminal:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"mkdir src\\ncd src\\ntouch config.js bot.js\\n\")), mdx(\"p\", null, \"Then we can set up the bot config, open the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"config.js\"), \" file and add\\nthe following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"require('dotenv').config()\\n\\nmodule.exports = {\\n  consumer_key: process.env.CONSUMER_KEY,\\n  consumer_secret: process.env.CONSUMER_SECRET,\\n  access_token: process.env.ACCESS_TOKEN,\\n  access_token_secret: process.env.ACCESS_TOKEN_SECRET,\\n}\\n\")), mdx(\"p\", null, \"Ok, that\\u2019s the bot config done now we can set up the bot, each of the\\nexamples detailed here will have the same three lines of code:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst config = require('./config')\\n\\nconst bot = new Twit(config)\\n\")), mdx(\"p\", null, \"Ok, that\\u2019s it out bot is ready to go, do a test with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"yarn start\"), \" from\\nthe terminal, we should get this for output:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"yarn start\\nyarn start v0.23.4\\n$ node index.js\\nDone in 0.64s.\\n\")), mdx(\"p\", null, \"Bot is now configured and ready to go!\\uD83D\\uDE80\"), mdx(\"h2\", {\n    \"id\": \"post-statuses\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#post-statuses\",\n    \"aria-label\": \"post statuses permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Post Statuses\"), mdx(\"p\", null, \"Firstly post statuses, with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/update'...\"), \" bot will post\\na hello world! status.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'statuses/update',\\n  {\\n    status: 'hello world!',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} tweeted!`)\\n    }\\n  }\\n)\\n\")), mdx(\"h2\", {\n    \"id\": \"work-with-users\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#work-with-users\",\n    \"aria-label\": \"work with users permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Work with users\"), mdx(\"p\", null, \"To get a list of followers ids use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".get('followers/ids'...\"), \" and\\ninclude the account that you want the followers of, in this example\\nwe\\u2019re using \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/DroidScott\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"@DroidScott\")), \", you can use any account you\\nlike. We can then log them out to the console in this example.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'followers/ids',\\n  {\\n    screen_name: 'DroidScott',\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"You can specify with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"count\"), \" parameter how many results you get up\\nto 100 at a time.\"), mdx(\"p\", null, \"Or to get a detailed list you can use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".get('followers/list'...\")), mdx(\"p\", null, \"Here we print off a list of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"user.screen_name\"), \"\\u2019s up to 200 per call.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'followers/list',\\n  {\\n    screen_name: 'DroidScott',\\n    count: 200,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      data.users.forEach(user => {\\n        console.log(user.screen_name)\\n      })\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To follow back a follower we can use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('friendships/create'...\"), \"\\nhere the bot is following back the user \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MarcGuberti\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"A bot should only follow users that follow the bot.\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'friendships/create',\\n  {\\n    screen_name: 'MarcGuberti',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"Like with followers you can get a list of accounts that your bot is\\nfollowing back.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'friends/ids',\\n  {\\n    screen_name: 'DroidScott',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"And also a detailed list.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'friends/list',\\n  {\\n    screen_name: 'DroidScott',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"Get friendship status, this is useful for following new followers,\\nthis will give us the relation of a specific user. So you can run\\nthrough your followers list and follow back any users that do not have\\nthe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"following\"), \" connection.\"), mdx(\"p\", null, \"Let\\u2019s take a look at the relation between our bot and\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://twitter.com/spences10\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"@spences10\"))), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'friendships/lookup',\\n  {\\n    screen_name: 'spences10',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"If the user follows the bot, then relationship will be:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"[ { name: 'Scott Spence \\uD83C\\uDF2F\\uD83D\\uDE34\\uD83D\\uDCBB\\u267B',\\n    screen_name: 'spences10',\\n    id: 4897735439,\\n    id_str: '4897735439',\\n    connections: [ 'followed_by' ] } ]\\n\")), mdx(\"p\", null, \"If the user and the bot are following each other, the relationship\\nwill be:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"[ { name: 'Scott Spence \\uD83C\\uDF2F\\uD83D\\uDE34\\uD83D\\uDCBB\\u267B',\\n    screen_name: 'spences10',\\n    id: 4897735439,\\n    id_str: '4897735439',\\n    connections: [ 'following', 'followed_by' ] } ]\\n\")), mdx(\"p\", null, \"And if there is no relationship then:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"[ { name: 'Scott Spence \\uD83C\\uDF2F\\uD83D\\uDE34\\uD83D\\uDCBB\\u267B',\\n    screen_name: 'spences10',\\n    id: 4897735439,\\n    id_str: '4897735439',\\n    connections: [ 'none' ] } ]\\n\")), mdx(\"p\", null, \"Direct Message a user with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.post('direct_messages/new'...\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"A bot should only DM a user that is following the bot account\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'direct_messages/new',\\n  {\\n    screen_name: 'spences10',\\n    text: 'Hello from bot!',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"h2\", {\n    \"id\": \"interact-with-tweets\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#interact-with-tweets\",\n    \"aria-label\": \"interact with tweets permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Interact with tweets\"), mdx(\"p\", null, \"To get a list of tweets in the bots time line use\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".get(statuses/home_timeline'...\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'statuses/home_timeline',\\n  {\\n    count: 1,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To be more granular you can pull out specific information on each\\ntweet.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'statuses/home_timeline',\\n  {\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      data.forEach(t => {\\n        console.log(t.text)\\n        console.log(t.user.screen_name)\\n        console.log(t.id_str)\\n        console.log('\\\\n')\\n      })\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To retweet use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/retweet/:id'...\"), \" and pass in a tweet\\nid to retweet.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'statuses/retweet/:id',\\n  {\\n    id: '860828247944253440',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} retweet success!`)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To unretweet just use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/unretweet/:id'...\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'statuses/unretweet/:id',\\n  {\\n    id: '860828247944253440',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} unretweet success!`)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To like a tweet use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('favorites/create'...\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'favorites/create',\\n  {\\n    id: '860897020726435840',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} tweet liked!`)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To unlike a post use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('favorites/destroy'...\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'favorites/destroy',\\n  {\\n    id: '860897020726435840',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} tweet unliked!`)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"To reply to a tweet is much the same a posting a tweet but you need to\\ninclude the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"in_reply_to_status_id\"), \" parameter, but that\\u2019s not enough\\nas you will also need to put in the screen name of the person you are\\nreplying to.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'statuses/update',\\n  {\\n    status: '@spences10 I reply to you yes!',\\n    in_reply_to_status_id: '860900406381211649',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} tweeted!`)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"Finally if you want to delete a tweet use\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/destroy/:id'...\"), \" passing the tweet id you want to\\ndelete.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.post(\\n  'statuses/destroy/:id',\\n  {\\n    id: '860900437993676801',\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(`${data.text} tweet deleted!`)\\n    }\\n  }\\n)\\n\")), mdx(\"h2\", {\n    \"id\": \"use-twitter-search\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#use-twitter-search\",\n    \"aria-label\": \"use twitter search permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Use Twitter search\"), mdx(\"p\", null, \"To use search use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".get('search/tweets',...\"), \" there are quite a few\\nsearch parameters for search.\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: ''\"), \" the Q is for query so to search for mango use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: 'mango'\"), \" we\\ncan also limit the results returned with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"count: n\"), \" so let\\u2019s limit it\\nthe count to in the example:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'search/tweets',\\n  {\\n    q: 'mango',\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log(data.statuses)\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"Like we did with the timeline we will pull out specific items from the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"data.statuses\"), \" returned, like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'search/tweets',\\n  {\\n    q: 'mango',\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      data.statuses.forEach(s => {\\n        console.log(s.text)\\n        console.log(s.user.screen_name)\\n        console.log('\\\\n')\\n      })\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"The search API returns for relevance and not completeness, if you want\\nto search for an exact phrase you\\u2019ll need to wrap the query in quotes\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\"purple pancakes\\\"\"), \" if you want to search for one of two words then\\nuse \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"OR\"), \" like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'tabs OR spaces'\"), \" if you want to search for both use\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"AND\"), \" like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'tabs AND spaces'\"), \".\"), mdx(\"p\", null, \"If you want to search for a tweet without another word use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-\"), \" like\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"donald -trump\"), \" you can use it multiple times as well, like\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"donald -trump -duck\")), mdx(\"p\", null, \"You can search for tweets with emoticons, like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: 'sad :('\"), \" try it!\"), mdx(\"p\", null, \"Of course look for hashtags \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: '#towie'\"), \". Look for tweets to a user\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: 'to:@stephenfry'\"), \" or from a user \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q: 'from:@stephenfry'\")), mdx(\"p\", null, \"You can filter out indecent tweets with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"filter:safe\"), \" parameter\\nyou can also use it to filter for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"media\"), \" tweets which will return\\ntweets containing video. You can specify for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"images\"), \" to view tweets\\nwith images and you can specify \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"links\"), \" for tweets with links.\"), mdx(\"p\", null, \"If you want tweets from a certain website you can specify with the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"url\"), \" parameter like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"url:asda\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'search/tweets',\\n  {\\n    q:\\n      'from:@dan_abramov url:facebook filter:images since:2017-01-01',\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      data.statuses.forEach(s => {\\n        console.log(s.text)\\n        console.log(s.user.screen_name)\\n        console.log('\\\\n')\\n      })\\n    }\\n  }\\n)\\n\")), mdx(\"p\", null, \"Last few now, there\\u2019s the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"result_type\"), \" parameter that will return\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"recent\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"popular\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"mixed\"), \" results.\"), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"geocode\"), \" parameter that take the format latitude longitude then\\nradius in miles \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'51.5033640,-0.1276250,1mi'\"), \" example:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"bot.get(\\n  'search/tweets',\\n  {\\n    q: 'bacon',\\n    geocode: '51.5033640,-0.1276250,1mi',\\n    count: 5,\\n  },\\n  (err, data, response) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      data.statuses.forEach(s => {\\n        console.log(s.text)\\n        console.log(s.user.screen_name)\\n        console.log('\\\\n')\\n      })\\n    }\\n  }\\n)\\n\")), mdx(\"h2\", {\n    \"id\": \"use-twitter-stream-api\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#use-twitter-stream-api\",\n    \"aria-label\": \"use twitter stream api permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Use Twitter Stream API\"), mdx(\"p\", null, \"There are two ways to use the Stream API first there\\u2019s\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".stream('statuses/sample')\"), \" example:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const stream = bot.stream('statuses/sample')\\n\\nstream.on('tweet', t => {\\n  console.log(`${t.text}\\\\n`)\\n})\\n\")), mdx(\"p\", null, \"This will give you a random sampling of tweets.\"), mdx(\"p\", null, \"For more specific information use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".stream('statuses/filter')...\"), \" then\\npass some parameters, use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"track:\"), \" to specify a search string:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"var stream = bot.stream('statuses/filter', {\\n  track: 'bot',\\n})\\n\\nstream.on('tweet', function(t) {\\n  console.log(t.text + '\\\\n')\\n})\\n\")), mdx(\"p\", null, \"You can also use multiple words in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"track\"), \" parameter, tis will get\\nyou results with either \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"twitter\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot\"), \" in them.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const stream = bot.stream('statuses/filter', {\\n  track: 'twitter, bot',\\n})\\n\\nstream.on('tweet', t => {\\n  console.log(`${t.text}\\\\n`)\\n})\\n\")), mdx(\"p\", null, \"If you want both words then remove the comma \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \",\"), \" you can think of\\nspaces as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"AND\"), \" and commas as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"OR\")), mdx(\"p\", null, \"You can also use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"follow:\"), \" parameter which lets you input the ids\\nof specific users, example:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const stream = bot.stream('statuses/filter', {\\n  follow: '4897735439',\\n})\\n\\nstream.on('tweet', t => {\\n  console.log(`${t.text}\\\\n`)\\n})\\n\")), mdx(\"h2\", {\n    \"id\": \"tweet-media-files\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#tweet-media-files\",\n    \"aria-label\": \"tweet media files permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Tweet media files\"), mdx(\"p\", null, \"This \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://egghead.io/lessons/node-js-tweet-media-files-with-twit-js\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"egghead.io\"), \" video is a great resource for\\nthis section thanks to \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://egghead.io/instructors/hannah-davis\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Hannah Davis\"), \" for the awesome\\ncontent!\"), mdx(\"p\", null, \"This will be a request to get the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.nasa.gov/multimedia/imagegallery/iotd.html\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"NASA image of the day\"), \"\\nand tweet it.\"), mdx(\"p\", null, \"For this we will need references to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"request\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs\"), \" for working\\nwith the file system.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst request = require('request')\\nconst fs = require('fs')\\nconst config = require('./config')\\n\\nconst bot = new Twit(config)\\n\")), mdx(\"p\", null, \"First up get the photo from the NASA api, for this we will need to\\ncreate a parameter object inside our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto\"), \" function that will be\\npassed to the node HTTP client \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"request\"), \" for the image:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function getPhoto() {\\n  const parameters = {\\n    url: 'https://api.nasa.gov/planetary/apod',\\n    qs: {\\n      api_key: process.env.NASA_KEY,\\n    },\\n    encoding: 'binary',\\n  }\\n}\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"parameters\"), \" specify an \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"api_key\"), \" for this you can \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://api.nasa.gov/index.html#apply-for-an-api-key\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"apply for an\\nAPI key\"), \" or you can use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DEMO_KEY\"), \" this API key can be\\nused for initially exploring APIs prior to signing up, but it has much\\nlower rate limits, so you\\u2019re encouraged to signup for your own API\\nkey.\"), mdx(\"p\", null, \"In the example you can see that I have configured my key with the rest\\nof my \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".env\"), \" variables.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"CONSUMER_KEY=AmMSbxxxxxxxxxxNh4BcdMhxg\\nCONSUMER_SECRET=eQUfMrHbtlxxxxxxxxxxkFNNj1H107xxxxxxxxxx6CZH0fjymV\\nACCESS_TOKEN=7xxxxx492-uEcacdl7HJxxxxxxxxxxecKpi90bFhdsGG2N7iII\\nACCESS_TOKEN_SECRET=77vGPTt20xxxxxxxxxxxZAU8wxxxxxxxxxx0PhOo43cGO\\n\\nNASA_KEY=DEMO_KEY\\n\")), mdx(\"p\", null, \"Now to use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"request\"), \" to get the image:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function getPhoto() {\\n  const parameters = {\\n    url: 'https://api.nasa.gov/planetary/apod',\\n    qs: {\\n      api_key: process.env.NASA_KEY,\\n    },\\n    encoding: 'binary',\\n  }\\n  request.get(parameters, (err, respone, body) => {\\n    body = JSON.parse(body)\\n    saveFile(body, 'nasa.jpg')\\n  })\\n}\\n\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"request\"), \" we pass in our parameters and parse the body as JOSN\\nso we can save it with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"saveFile\"), \" function which we\\u2019ll go over\\nnow:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function saveFile(body, fileName) {\\n  const file = fs.createWriteStream(fileName)\\n  request(body)\\n    .pipe(file)\\n    .on('close', err => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log('Media saved!')\\n        console.log(body)\\n      }\\n    })\\n}\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"request(body).pipe(file).on('close'...\"), \" is what saves the file from\\nthe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"file\"), \" variable which has the name passed to it \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nasa.jpg\"), \" from\\nthe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto\"), \" function.\"), mdx(\"p\", null, \"Calling \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto()\"), \" should now save the NASA image of the day to the\\nroot of your project.\"), mdx(\"p\", null, \"Now we can share it on Twitter \\uD83D\\uDE0E\"), mdx(\"p\", null, \"Two parts to this, first save the file.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function saveFile(body, fileName) {\\n  const file = fs.createWriteStream(fileName)\\n  request(body)\\n    .pipe(file)\\n    .on('close', err => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log('Media saved!')\\n        const descriptionText = body.title\\n        uploadMedia(descriptionText, fileName)\\n      }\\n    })\\n}\\n\")), mdx(\"p\", null, \"Then \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"uploadMedia\"), \" to upload media to Twitter before we can post it,\\nthis had me stumped for a bit as I have my files in a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder, if\\nyou have your bot files nested in folders then you will need to do the\\nsame if you are struggling with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"file does not exist\"), \" errors:\"), mdx(\"p\", null, \"Add a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"require\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"path\"), \" then use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"join\"), \" with the relevant relative\\nfile path.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const path = require('path')\\n//...\\nconst filePath = path.join(__dirname, '../' + fileName)\\n\")), mdx(\"p\", null, \"Complete function here:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function uploadMedia(descriptionText, fileName) {\\n  console.log(`uploadMedia: file PATH ${fileName}`)\\n  bot.postMediaChunked(\\n    {\\n      file_path: fileName,\\n    },\\n    (err, data, respone) => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log(data)\\n        const params = {\\n          status: descriptionText,\\n          media_ids: data.media_id_string,\\n        }\\n        postStatus(params)\\n      }\\n    }\\n  )\\n}\\n\")), mdx(\"p\", null, \"Then with the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"params\"), \" we created in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"uploadMedia\"), \" we can post with a\\nstraightforward \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/update'...\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function postStatus(params) {\\n  bot.post('statuses/update', params, (err, data, respone) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log('Status posted!')\\n    }\\n  })\\n}\\n\")), mdx(\"p\", null, \"Call the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto()\"), \" function top post to Twitter\\u2026 super straight\\nforward, right \\uD83D\\uDE00 no, I know it wasn\\u2019t. Here\\u2019s the complete module:\"), mdx(\"details\", null, mdx(\"summary\", null, \"Click to expand\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst request = require('request')\\nconst fs = require('fs')\\nconst config = require('./config')\\nconst path = require('path')\\n\\nconst bot = new Twit(config)\\n\\nfunction getPhoto() {\\n  const parameters = {\\n    url: 'https://api.nasa.gov/planetary/apod',\\n    qs: {\\n      api_key: process.env.NASA_KEY,\\n    },\\n    encoding: 'binary',\\n  }\\n  request.get(parameters, (err, respone, body) => {\\n    body = JSON.parse(body)\\n    saveFile(body, 'nasa.jpg')\\n  })\\n}\\n\\nfunction saveFile(body, fileName) {\\n  const file = fs.createWriteStream(fileName)\\n  request(body)\\n    .pipe(file)\\n    .on('close', err => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log('Media saved!')\\n        const descriptionText = body.title\\n        uploadMedia(descriptionText, fileName)\\n      }\\n    })\\n}\\n\\nfunction uploadMedia(descriptionText, fileName) {\\n  const filePath = path.join(__dirname, `../${fileName}`)\\n  console.log(`file PATH ${filePath}`)\\n  bot.postMediaChunked(\\n    {\\n      file_path: filePath,\\n    },\\n    (err, data, respone) => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log(data)\\n        const params = {\\n          status: descriptionText,\\n          media_ids: data.media_id_string,\\n        }\\n        postStatus(params)\\n      }\\n    }\\n  )\\n}\\n\\nfunction postStatus(params) {\\n  bot.post('statuses/update', params, (err, data, respone) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log('Status posted!')\\n    }\\n  })\\n}\\n\\ngetPhoto()\\n\"))), mdx(\"h2\", {\n    \"id\": \"make-a-markov-bot\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#make-a-markov-bot\",\n    \"aria-label\": \"make a markov bot permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Make a Markov bot\"), mdx(\"p\", null, \"This is pretty neat, again from the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://egghead.io/lessons/node-js-make-a-bot-that-sounds-like-you-with-rita-js?series=create-your-own-twitter-bots\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"egghead.io\"), \"\\nseries it uses \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/package/rita\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"rita\"), \" natural language toolkit. It also uses\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"csv-parse\"), \" as we\\u2019re going to be reading out our Twitter archive to\\nmake the bot sound like us tweeting.\"), mdx(\"p\", null, \"First of all, to set up the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://support.twitter.com/articles/20170160\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"Twitter archive\"), \", you\\u2019ll\\nneed to request your data from the Twitter settings page. You\\u2019ll be\\nemailed a link to download your archive, then when you have downloaded\\nthe archive extract out the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweets.csv\"), \" file, we\\u2019ll then put that in\\nit\\u2019s own folder, so from the root of your project:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"cd src\\nmkdir twitter-archive\\n\")), mdx(\"p\", null, \"We\\u2019ll move our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweets.csv\"), \" there to be accessed by the bot we\\u2019re\\ngoing to go over now.\"), mdx(\"p\", null, \"Use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"fs\"), \" to set up a read stream\\u2026\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const filePath = path.join(__dirname, './twitter-archive/tweets.csv')\\n\\nconst tweetData = fs\\n  .createReadStream(filePath)\\n  .pipe(\\n    csvparse({\\n      delimiter: ',',\\n    })\\n  )\\n  .on('data', row => {\\n    console.log(row[5])\\n  })\\n\")), mdx(\"p\", null, \"When you run this from the console you should get the output from your\\nTwitter archive.\"), mdx(\"p\", null, \"Now clear out things like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"RT\"), \" to help with the natural\\nlanguage processing we\\u2019ll set up two functions \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cleanText\"), \" and\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"hasNoStopWords\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cleanText\"), \" will tokenize the text delimiting it on space \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"' '\"), \" filter\\nout the stop words then \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".join(' ')\"), \" back together with a space and\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".trim()\"), \" any whitespace that may be at the start of the text.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function cleanText(text) {\\n  return rita.RiTa.tokenize(text, ' ')\\n    .filter(hasNoStopWords)\\n    .join(' ')\\n    .trim()\\n}\\n\")), mdx(\"p\", null, \"The tokenized text can then be fed into the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"hasNoStopWords\"), \" function\\nto be sanitized for use in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweetData\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function hasNoStopWords(token) {\\n  const stopwords = ['@', 'http', 'RT']\\n  return stopwords.every(sw => !token.includes(sw))\\n}\\n\")), mdx(\"p\", null, \"Now that we have the data cleaned we can tweet it, so replace\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"console.log(row[5])\"), \" with\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"inputText = inputText + ' ' + cleanText(row[5])\"), \" then we can use\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"rita.RiMarkov(3)\"), \" the 3 being the number of words to take into\\nconsideration. Then use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"markov.generateSentences(1)\"), \" with 1 being the\\nnumber of sentences being generated. We\\u2019ll also use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".toString()\"), \" and\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".substring(0, 140)\"), \" to truncate the result down to 140 characters.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const tweetData =\\n  fs.createReadStream(filePath)\\n  .pipe(csvparse({\\n    delimiter: ','\\n  }))\\n  .on('data', function (row) {\\n    inputText = `${inputText} ${cleanText(row[5])}`\\n  })\\n  .on('end', function(){\\n    const markov = new rita.RiMarkov(3)\\n    markov.loadText(inputText)\\n    const sentence = markov.generateSentences(1)\\n      .toString()\\n      .substring(0, 140)\\n  }\\n\")), mdx(\"p\", null, \"Now we can tweet this with the bot using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/update'...\"), \"\\npassing in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sentence\"), \" variable as the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"status\"), \" logging out when\\nthere is a tweet.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const tweetData =\\n  fs.createReadStream(filePath)\\n    .pipe(csvparse({\\n      delimiter: ','\\n    }))\\n    .on('data', row => {\\n      inputText = `${inputText} ${cleanText(row[5])}`\\n    })\\n    .on('end', () => {\\n      const markov = new rita.RiMarkov(3)\\n      markov.loadText(inputText)\\n      const sentence = markov.generateSentences(1)\\n        .toString()\\n        .substring(0, 140)\\n      bot.post('statuses/update', {\\n        status: sentence\\n      }, (err, data, response) => {\\n        if (err) {\\n          console.log(err)\\n        } else {\\n          console.log('Markov status tweeted!', sentence)\\n        }\\n      })\\n    })\\n}\\n\")), mdx(\"p\", null, \"If you want your sentences to be closer to the input text you can\\nincrease the words to consider in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"rita.RiMarkov(6)\"), \" and if you want\\nto make it gibberish then lower the number.\"), mdx(\"p\", null, \"Here\\u2019s the completed module:\"), mdx(\"details\", null, mdx(\"summary\", null, \"Click to expand\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst fs = require('fs')\\nconst csvparse = require('csv-parse')\\nconst rita = require('rita')\\nconst config = require('./config')\\nconst path = require('path')\\n\\nlet inputText = ''\\n\\nconst bot = new Twit(config)\\n\\nconst filePath = path.join(__dirname, '../twitter-archive/tweets.csv')\\n\\nconst tweetData =\\n  fs.createReadStream(filePath)\\n    .pipe(csvparse({\\n      delimiter: ','\\n    }))\\n    .on('data', row => {\\n      inputText = `${inputText} ${cleanText(row[5])}`\\n    })\\n    .on('end', () => {\\n      const markov = new rita.RiMarkov(10)\\n      markov.loadText(inputText)\\n      const sentence = markov.generateSentences(1)\\n        .toString()\\n        .substring(0, 140)\\n      bot.post('statuses/update', {\\n        status: sentence\\n      }, (err, data, response) => {\\n        if (err) {\\n          console.log(err)\\n        } else {\\n          console.log('Markov status tweeted!', sentence)\\n        }\\n      })\\n    })\\n}\\n\\nfunction hasNoStopWords(token) {\\n  const stopwords = ['@', 'http', 'RT']\\n  return stopwords.every(sw => !token.includes(sw))\\n}\\n\\nfunction cleanText(text) {\\n  return rita.RiTa.tokenize(text, ' ')\\n    .filter(hasNoStopWords)\\n    .join(' ')\\n    .trim()\\n}\\n\"))), mdx(\"h2\", {\n    \"id\": \"retrieve-and-tweet-data-from-google-sheets\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#retrieve-and-tweet-data-from-google-sheets\",\n    \"aria-label\": \"retrieve and tweet data from google sheets permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Retrieve and Tweet data from Google sheets\"), mdx(\"p\", null, \"If you want to tweet a list of links you can use\\n\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.npmjs.com/package/tabletop\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"tabletop\")), \" to work though the list, in this example\\nagain from \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://egghead.io/lessons/node-js-retrieve-and-tweet-information-from-google-spreadsheets\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"egghead.io\"), \" we\\u2019ll go through a list of\\nlinks.\"), mdx(\"p\", null, \"So, set up the bot and require \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tabletop\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst config = require('./config')\\nconst Tabletop = require('tabletop')\\n\\nconst bot = new Twit(config)\\n\")), mdx(\"p\", null, \"On your \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"sheets.google.com\"\n  }), mdx(\"inlineCode\", {\n    parentName: \"a\"\n  }, \"Google spreadsheet\")), \" you\\u2019ll need to have a\\nheader defined and then add your links, we\\u2019ll use the following for an\\nexample:\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", _extends({\n    parentName: \"tr\"\n  }, {\n    \"align\": null\n  }), \"links\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", _extends({\n    parentName: \"tr\"\n  }, {\n    \"align\": null\n  }), mdx(\"a\", _extends({\n    parentName: \"td\"\n  }, {\n    \"href\": \"https://www.freecodecamp.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"https://www.freecodecamp.com\"))), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", _extends({\n    parentName: \"tr\"\n  }, {\n    \"align\": null\n  }), mdx(\"a\", _extends({\n    parentName: \"td\"\n  }, {\n    \"href\": \"https://github.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"https://github.com\"))), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", _extends({\n    parentName: \"tr\"\n  }, {\n    \"align\": null\n  }), mdx(\"a\", _extends({\n    parentName: \"td\"\n  }, {\n    \"href\": \"https://www.reddit.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"https://www.reddit.com\"))), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", _extends({\n    parentName: \"tr\"\n  }, {\n    \"align\": null\n  }), mdx(\"a\", _extends({\n    parentName: \"td\"\n  }, {\n    \"href\": \"https://twitter.com\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"https://twitter.com\"))))), mdx(\"p\", null, \"Now from Google sheets we can select \\u2018File\\u2019>\\u2018Publish to the web\\u2019 and\\ncopy the link that is generated we can use that in table top.\"), mdx(\"p\", null, \"Now init Table top with three parameters, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"key:\"), \" which is the\\nspreadsheet URL, a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"callback:\"), \" function to get the data and\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"simpleSheet:\"), \" which is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"true\"), \" if you only have one sheet, like in our\\nexample here:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const spreadsheetUrl =\\n  'https://docs.google.com/spreadsheets/d/1842GC9JS9qDWHc-9leZoEn9Q_-jcPUcuDvIqd_MMPZQ/pubhtml'\\n\\nTabletop.init({\\n  key: spreadsheetUrl,\\n  callback(data, tabletop) {\\n    console.log(data)\\n  },\\n  simpleSheet: true,\\n})\\n\")), mdx(\"p\", null, \"Running the bot now should give output like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"$ node index.js\\n[ { 'links': 'https://www.freecodecamp.com' },\\n  { 'links': 'https://github.com' },\\n  { 'links': 'https://www.reddit.com' },\\n  { 'links': 'https://twitter.com' } ]\\n\")), mdx(\"p\", null, \"So now we can tweet them using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".post('statuses/update',...\"), \" with a\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"forEach\"), \" on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"data\"), \" that is returned in the callback:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"Tabletop.init({\\n  key: spreadsheetUrl,\\n  callback(data, tabletop) {\\n    data.forEach(d => {\\n      const status = `${d.links} a link from a Google spreadsheet`\\n      bot.post(\\n        'statuses/update',\\n        {\\n          status,\\n        },\\n        (err, response, data) => {\\n          if (err) {\\n            console.log(err)\\n          } else {\\n            console.log('Post success!')\\n          }\\n        }\\n      )\\n    })\\n  },\\n  simpleSheet: true,\\n})\\n\")), mdx(\"p\", null, \"Note that \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"${d.links}\"), \" is the header name we use in the Google\\nspreadsheet, I tried using skeleton and camel case and both returned\\nerrors so I went with a single name header on the spreadsheet.\"), mdx(\"p\", null, \"The completed code here:\"), mdx(\"details\", null, mdx(\"summary\", null, \"Click to expand\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst config = require('./config')\\nconst Tabletop = require('tabletop')\\n\\nconst bot = new Twit(config)\\n\\nconst spreadsheetUrl =\\n  'https://docs.google.com/spreadsheets/d/1842GC9JS9qDWHc-9leZoEn9Q_-jcPUcuDvIqd_MMPZQ/pubhtml'\\n\\nTabletop.init({\\n  key: spreadsheetUrl,\\n  callback(data, tabletop) {\\n    data.forEach(d => {\\n      const status = `${d.links} a link from a Google spreadsheet`\\n      console.log(status)\\n      bot.post(\\n        'statuses/update',\\n        {\\n          status,\\n        },\\n        (err, response, data) => {\\n          if (err) {\\n            console.log(err)\\n          } else {\\n            console.log('Post success!')\\n          }\\n        }\\n      )\\n    })\\n  },\\n  simpleSheet: true,\\n})\\n\"))), mdx(\"h2\", {\n    \"id\": \"putting-it-all-together\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#putting-it-all-together\",\n    \"aria-label\": \"putting it all together permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Putting it all together\"), mdx(\"p\", null, \"Ok, so those examples were good n\\u2019 all but we haven\\u2019t really got a bot\\nout of this have we? I mean you run it from the terminal and that\\u2019s it\\ndone, we want to be able to kick off the bot and leave it to do its\\nthing.\"), mdx(\"p\", null, \"One way I have found to do this is to use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setInterval\"), \" which will\\nkick off events from the main \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" module, so let\\u2019s try this:\"), mdx(\"p\", null, \"Take the example we did to tweet a picture and add it to it\\u2019s own\\nmodule, so from the root directory of our project:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"cd src\\ntouch picture-bot.js\\n\")), mdx(\"p\", null, \"Take the example code from that and paste it into the new module, then\\nwe\\u2019re going to make the following changes, to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const getPhoto = () => {\\n  const parameters = {\\n    url: 'https://api.nasa.gov/planetary/apod',\\n    qs: {\\n      api_key: process.env.NASA_KEY,\\n    },\\n    encoding: 'binary',\\n  }\\n  request.get(parameters, (err, respone, body) => {\\n    body = JSON.parse(body)\\n    saveFile(body, 'nasa.jpg')\\n  })\\n}\\n\")), mdx(\"p\", null, \"Then at the bottom of the module add:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"module.exports = getPhoto\\n\")), mdx(\"p\", null, \"So now we can call the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto\"), \" function from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"picture-bot.js\"), \"\\nmodule in our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" module, our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" module should look\\nsomething like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const picture = require('./picture-bot')\\n\\npicture()\\n\")), mdx(\"p\", null, \"That\\u2019s it, two lines of code, try running that from the terminal now:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"yarn start\\n\")), mdx(\"p\", null, \"We should get some output like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"yarn start v0.23.4\\n$ node index.js\\nMedia saved!\\nfile PATH C:\\\\Users\\\\path\\\\to\\\\project\\\\tweebot-play\\\\nasa.jpg\\n{ media_id: 863020197799764000,\\n  media_id_string: '863020197799763968',\\n  size: 371664,\\n  expires_after_secs: 86400,\\n  image: { image_type: 'image/jpeg', w: 954, h: 944 } }\\nStatus posted!\\nDone in 9.89s.\\n\")), mdx(\"p\", null, \"Ok, so thats the picture of the day done, but it has run once and\\ncompleted we need to put it on an interval with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setInterval\"), \" which we\\nneed to pass two options to, the function it\\u2019s going to call and the\\ntimeout value.\"), mdx(\"p\", null, \"The picture updates every 24 hours so that will be how many\\nmilliseconds in 24 hours \", \"[8.64e+7]\", \" I don\\u2019t even \\uD83E\\uDD37\\u200D\"), mdx(\"p\", null, \"I work it out like this, 1000 \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \" 60 = 1 minute, so 1000 \"), \" 60 \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \" 60 \"), \" 24\\nso for now let\\u2019s add that directly into the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setInterval\"), \" function:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const picture = require('./picture-bot')\\n\\npicture()\\nsetInterval(picture, 1000 * 60 * 60 * 24)\\n\")), mdx(\"p\", null, \"Cool, that\\u2019s a bot that will post the NASA image of the day every 24\\nhours!\"), mdx(\"p\", null, \"Let\\u2019s keep going, now let\\u2019s add some randomness in with the Markov\\nbot, like we did in the picture of the day example, let\\u2019s create a new\\nmodule for the Markov bot and add all the code in there from the\\nprevious example, so from the terminal:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"cd src\\ntouch markov-bot.js\\n\")), mdx(\"p\", null, \"Then copy pasta the markov bot example into the new module, then we\\u2019re\\ngoing to make the following changes:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const tweetData = () => {\\n  fs.createReadStream(filePath)\\n    .pipe(\\n      csvparse({\\n        delimiter: ',',\\n      })\\n    )\\n    .on('data', row => {\\n      inputText = `${inputText} ${cleanText(row[5])}`\\n    })\\n    .on('end', () => {\\n      const markov = new rita.RiMarkov(10)\\n      markov\\n        .loadText(inputText)\\n        .toString()\\n        .substring(0, 140)\\n      const sentence = markov.generateSentences(1)\\n      bot.post(\\n        'statuses/update',\\n        {\\n          status: sentence,\\n        },\\n        (err, data, response) => {\\n          if (err) {\\n            console.log(err)\\n          } else {\\n            console.log('Markov status tweeted!', sentence)\\n          }\\n        }\\n      )\\n    })\\n}\\n\")), mdx(\"p\", null, \"Then at the bottom of the module add:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"module.exports = tweetData\\n\")), mdx(\"p\", null, \"Ok, same again as with the picture bot example we\\u2019re going to add the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweetData\"), \" export from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"markov-bot.js\"), \" to our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"bot.js\"), \" module, which\\nshould now look something like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const picture = require('./picture-bot')\\nconst markov = require('./markov-bot')\\n\\npicture()\\nsetInterval(picture, 1000 * 60 * 60 * 24)\\n\\nmarkov()\\n\")), mdx(\"p\", null, \"Let\\u2019s make the Markov bot tweet at random intervals between 5 minutes\\nand 3 hours\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const picture = require('./picture-bot')\\nconst markov = require('./markov-bot')\\n\\npicture()\\nsetInterval(picture, 1000 * 60 * 60 * 24)\\n\\nconst markovInterval = (Math.floor(Math.random() * 180) + 1) * 1000\\nmarkov()\\nsetInterval(markov, markovInterval)\\n\")), mdx(\"p\", null, \"Allrighty! Picture bot, Markov bot, done \\uD83D\\uDC4D\"), mdx(\"p\", null, \"Do the same with the link bot? Ok, same as before, you get the idea\\nnow, right?\"), mdx(\"p\", null, \"Create a new file in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"src\"), \" folder for link bot:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"touch link-bot.js\\n\")), mdx(\"p\", null, \"Copy pasta the code from the link bot example into the new module,\\nlike this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const link = () => {\\n  Tabletop.init({\\n    key: spreadsheetUrl,\\n    callback(data, tabletop) {\\n      data.forEach(d => {\\n        const status = `${d.links} a link from a Google spreadsheet`\\n        console.log(status)\\n        bot.post(\\n          'statuses/update',\\n          {\\n            status,\\n          },\\n          (err, response, data) => {\\n            if (err) {\\n              console.log(err)\\n            } else {\\n              console.log('Post success!')\\n            }\\n          }\\n        )\\n      })\\n    },\\n    simpleSheet: true,\\n  })\\n}\\n\\nmodule.exports = link\\n\")), mdx(\"p\", null, \"Then we can call it from the bot, so it should look something like\\nthis:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const picture = require('./picture-bot')\\nconst markov = require('./markov-bot')\\nconst link = require('./link-bot')\\n\\npicture()\\nsetInterval(picture, 1000 * 60 * 60 * 24)\\n\\nconst markovInterval = (Math.floor(Math.random() * 180) + 1) * 1000\\nmarkov()\\nsetInterval(markov, markovInterval)\\n\\nlink()\\nsetInterval(link, 1000 * 60 * 60 * 24)\\n\")), mdx(\"p\", null, \"Ok? Cool \\uD83D\\uDC4D\\uD83D\\uDE0E\"), mdx(\"p\", null, \"We can now leave the bot running to do its thing!!\"), mdx(\"h2\", {\n    \"id\": \"deploy-to-now\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#deploy-to-now\",\n    \"aria-label\": \"deploy to now permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Deploy to now\"), mdx(\"p\", null, \"Right, we have a bot that does a few things but it\\u2019s on our\\ndevelopment environment, so it can\\u2019t stay there forever, well it could\\nbut it\\u2019d be pretty impcratcical. Let\\u2019s put our bot on a server\\nsomewhere to do it\\u2019s thing.\"), mdx(\"p\", null, \"To do this we\\u2019re going to be using \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/now\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"now\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" allows for simple\\ndeployments from the CLI if you\\u2019re not fimailiar with now then take a\\nquick look at the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/now\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"documentation\"), \" in these examples we\\u2019re going\\nto be using the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now-cli\"), \".\"), mdx(\"p\", null, \"There\\u2019s a few things we need to do in order to get our bot ready to go\\non \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/now\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"now\"), \", let\\u2019s list them quickly and then go into detail.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Signup and install now-cli\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Add now settings + .npmignore file\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Add .env variables as secrets\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Add npm deploy script\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Re jig picture-bot.js\")), mdx(\"p\", null, \"Ready? Let\\u2019s do this! \\uD83D\\uDCAA\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Signup and install now-cli\")), mdx(\"p\", null, \"Fist up let\\u2019s signup for \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/login\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"zeit\"), \" \\u25B2 create an account and\\nauthenticate, then we can install the CLI.\"), mdx(\"p\", null, \"Install \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" globally on our machine so you can use it everywhere, to\\ninstall the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now-cli\"), \" from the terminal enter:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"npm install -g now\\n\")), mdx(\"p\", null, \"Once it\\u2019s completed login with:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"now --login\\n\")), mdx(\"p\", null, \"The first time you run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \", it\\u2019ll ask for your email address in\\norder to identify you. Go to the email account to supplied when\\nsigining up an click on the email sent to you from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \", and you\\u2019ll\\nbe logged in automatically.\"), mdx(\"p\", null, \"If you need to switch the account or re-authenticate, run the same\\ncommand again.\"), mdx(\"p\", null, \"You can always check out the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/docs/getting-started/installing-now#cli-with-npm\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"now-cli\"), \"\\ndocumentation for more information along with the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://zeit.co/docs/getting-started/your-first-deployments#deploying-node\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"your first\\ndeployment\"), \" guide.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Add now settings\")), mdx(\"p\", null, \"Ok, so that\\u2019s signup and install sorted, we can now configure the bot\\nfor deploying to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \". First up let\\u2019s add the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" settings to our\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" file, I\\u2019ve put it in between my \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm\"), \" scripts and the\\nauthor name in my \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-json\"\n  }), \"\\\"scripts\\\": {\\n    \\\"start\\\": \\\"node index.js\\\"\\n  },\\n  \\\"now\\\": {\\n    \\\"alias\\\": \\\"my-awesome-alias\\\",\\n    \\\"files\\\": [\\n      \\\"src\\\",\\n      \\\"index.js\\\"\\n    ]\\n  },\\n  \\\"author\\\": \\\"Scott Spence\\\",\\n\")), mdx(\"p\", null, \"This was a source of major confusion for me so I\\u2019m hoping I can save\\nyou the pain I went through trying to configure this, all the relevant\\ndocumentation is there you just need to put it all together \\uD83D\\uDE0E\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"If you find anything in here that doesn\\u2019t make sense or is just\\noutright wrong then please \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/spences10/twitter-bot-playground/issues/new\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"log an issue\"), \" or create a\\npull request \\uD83D\\uDC4D\")), mdx(\"p\", null, \"The now settings \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"alias\"), \" is to give your deployment a shothand name\\nover the auto generated URL that \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" creates, the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"files\"), \" section\\ncovers what we want to include in the depoloyment to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" we\\u2019ll go\\nover the file structure shortly. Basically what is included in the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"files\"), \" array is all that get pused up to the now servers.\"), mdx(\"p\", null, \"All good so for?\"), mdx(\"p\", null, \"Ok, now we need to add a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".npmignore\"), \" file in the root of the project\\nand add the following line to it:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"!tweets.csv\\n\")), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweets.csv\"), \" needs to go up to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" server to be used by the\\nbot, but we previously included it in our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".gitignore\"), \" which is what\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" uses to build your project when it\\u2019s being loaded to the server.\\nSo this means that the file isn\\u2019t going to get loaded unless we add\\nthe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".npmignore\"), \" to not ignore the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tweets.csv\"), \" \\uD83D\\uDE05\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Add .env variables as secrets\")), mdx(\"p\", null, \"Ok, our super duper secret Twitter keys will need to be stored as\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"secrets\"), \" in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" this is a pretty neat feature where you can define\\nanything as a secret and reference it as an alias with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \".\"), mdx(\"p\", null, \"Let\\u2019s start, so the syntax is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now secrets add my-secret \\\"my value\\\"\"), \"\\nso for our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".env\"), \" keys add them all in giving them a descriptive \", \"[but\\nshort!]\", \" name.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"You will not need to wrap your \\u201Cmy value\\u201D in quotes but the\\ndocumentation does say \\u201Cwhen in doubt, wrap your value in quotes\\u201D\")), mdx(\"p\", null, \"Ok, so from the terminal \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now secrets ls\"), \" should list out your\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"secrets\"), \" you just created:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"$ now secrets ls\\n> 5 secrets found under spences10 [1s]\\n\\n                            id  name                   created\\n  sec_xxxxxxxxxxZpLDxxxxxxxxxx  ds-twit-key            23h ago\\n  sec_xxxxxxxxxxTE5Kxxxxxxxxxx  ds-twit-secret         23h ago\\n  sec_xxxxxxxxxxNorlxxxxxxxxxx  ds-twit-access         23h ago\\n  sec_xxxxxxxxxxMe1Cxxxxxxxxxx  ds-twit-access-secret  23h ago\\n  sec_xxxxxxxxxxMJ2jxxxxxxxxxx  nasa-key               23h ago\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Add npm deploy script\")), mdx(\"p\", null, \"Now we have our secrets defined we can create a deployment script to\\ndeploy to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \", so in our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"package.json\"), \" let\\u2019s add an additional\\nscript:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-json\"\n  }), \"  \\\"main\\\": \\\"index.js\\\",\\n  \\\"scripts\\\": {\\n    \\\"start\\\": \\\"node index.js\\\",\\n    \\\"deploy\\\": \\\"now -e CONSUMER_KEY=@ds-twit-key\\n        -e CONSUMER_SECRET=@ds-twit-secret -e ACCESS_TOKEN=@ds-twit-access\\n        -e ACCESS_TOKEN_SECRET=@ds-twit-access-secret -e NASA_KEY=@nasa-key\\\"\\n  },\\n  \\\"now\\\": {\\n\")), mdx(\"p\", null, \"Let\\u2019s go over what we have added there, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"deploy\"), \" will run the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \"\\ncommand and pass it all our environment \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-e\"), \" variables and the\\nassociated \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"secret\"), \" value, if we break it down into separate lines it\\nwill be a bit clearer:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"now\\n-e CONSUMER_KEY=@ds-twit-key\\n-e CONSUMER_SECRET=@ds-twit-secret\\n-e ACCESS_TOKEN=@ds-twit-access\\n-e ACCESS_TOKEN_SECRET=@ds-twit-access-secret\\n-e NASA_KEY=@nasa-key\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Re jig picture-bot.js\")), mdx(\"p\", null, \"Ok, because \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" deployments are \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://blog.codeship.com/immutable-deployments/\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"immutable\"), \" it\\nmeans that there\\u2019s no write access to the disk where we want to save\\nour NASA photo of the day, so to get around that we need to use the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/tmp\"), \" file location.\"), mdx(\"p\", null, \"Shout out to \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://github.com/timneutkens\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"@Tim\"), \" from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"zeit\"), \" for helping me out with this!\"), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"picture-bot.js\"), \" module add the following two lines to the top\\nof the module:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const os = require('os')\\nconst tmpDir = os.tmpdir()\\n\")), mdx(\"p\", null, \"Those two lines give us the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"temp\"), \" directory of the operating system,\\nso if like me you\\u2019re on Windows it will work as well as if you are on\\nanother stsyem like a linux based system, which is what \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" is. In\\nour \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"saveFile\"), \" function we\\u2019re going to use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tmpDir\"), \" to save our file.\"), mdx(\"p\", null, \"We\\u2019ve taken out the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nasa.jpg\"), \" from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"getPhoto\"), \" function as we can\\ndefine that information in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"saveFile\"), \" function, the NASA potd is\\nnot just a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"'jpeg\"), \" some items posted there are videos as well. We we\\ncan define the type with a \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator\",\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }), \"ternary function\"), \" off of the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"body\"), \" being passed in, this will send a tweet with a link to the\\nvideo:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"function saveFile(body) {\\n  const fileName =\\n    body.media_type === 'image/jpeg' ? 'nasa.jpg' : 'nasa.mp4'\\n  const filePath = path.join(tmpDir + `/${fileName}`)\\n\\n  console.log(`saveFile: file PATH ${filePath}`)\\n  if (fileName === 'nasa.mp4') {\\n    // tweet the link\\n    const params = {\\n      status: 'NASA video link: ' + body.url,\\n    }\\n    postStatus(params)\\n    return\\n  }\\n  const file = fs.createWriteStream(filePath)\\n\\n  request(body)\\n    .pipe(file)\\n    .on('close', err => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log('Media saved!')\\n        const descriptionText = body.title\\n        uploadMedia(descriptionText, filePath)\\n      }\\n    })\\n}\\n\")), mdx(\"p\", null, \"The completed code here:\"), mdx(\"details\", null, mdx(\"summary\", null, \"Click to expand\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-js\"\n  }), \"const Twit = require('twit')\\nconst request = require('request')\\nconst fs = require('fs')\\nconst config = require('./config')\\nconst path = require('path')\\n\\nconst bot = new Twit(config)\\n\\nconst os = require('os')\\nconst tmpDir = os.tmpdir()\\n\\nconst getPhoto = () => {\\n  const parameters = {\\n    url: 'https://api.nasa.gov/planetary/apod',\\n    qs: {\\n      api_key: process.env.NASA_KEY,\\n    },\\n    encoding: 'binary',\\n  }\\n  request.get(parameters, (err, respone, body) => {\\n    body = JSON.parse(body)\\n    saveFile(body)\\n  })\\n}\\n\\nfunction saveFile(body) {\\n  const fileName =\\n    body.media_type === 'image/jpeg' ? 'nasa.jpg' : 'nasa.mp4'\\n  const filePath = path.join(tmpDir + `/${fileName}`)\\n\\n  console.log(`saveFile: file PATH ${filePath}`)\\n  if (fileName === 'nasa.mp4') {\\n    // tweet the link\\n    const params = {\\n      status: 'NASA video link: ' + body.url,\\n    }\\n    postStatus(params)\\n    return\\n  }\\n  const file = fs.createWriteStream(filePath)\\n\\n  request(body)\\n    .pipe(file)\\n    .on('close', err => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log('Media saved!')\\n        const descriptionText = body.title\\n        uploadMedia(descriptionText, filePath)\\n      }\\n    })\\n}\\n\\nfunction uploadMedia(descriptionText, fileName) {\\n  console.log(`uploadMedia: file PATH ${fileName}`)\\n  bot.postMediaChunked(\\n    {\\n      file_path: fileName,\\n    },\\n    (err, data, respone) => {\\n      if (err) {\\n        console.log(err)\\n      } else {\\n        console.log(data)\\n        const params = {\\n          status: descriptionText,\\n          media_ids: data.media_id_string,\\n        }\\n        postStatus(params)\\n      }\\n    }\\n  )\\n}\\n\\nfunction postStatus(params) {\\n  bot.post('statuses/update', params, (err, data, respone) => {\\n    if (err) {\\n      console.log(err)\\n    } else {\\n      console.log('Status posted!')\\n    }\\n  })\\n}\\n\\nmodule.exports = getPhoto\\n\"))), mdx(\"p\", null, \"Ok, thats it! We\\u2019re ready to deploy to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \"!\\uD83D\\uDE80\"), mdx(\"p\", null, \"So from the terminal we call our deployment script we defined earlier:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"yarn deploy\\n\")), mdx(\"p\", null, \"You will get some output:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-bash\"\n  }), \"\\u03BB yarn deploy\\nyarn deploy v0.24.4\\n$ now -e CONSUMER_KEY=@ds-twit-key -e CONSUMER_SECRET=@ds-twit-secret\\n    -e ACCESS_TOKEN=@ds-twit-access  -e ACCESS_TOKEN_SECRET=@ds-twit-access-secret\\n    -e NASA_KEY=@nasa-key\\n> Deploying ~\\\\gitrepos\\\\tweebot-play under spences10\\n> Using Node.js 7.10.0 (default)\\n> Ready! https://twee-bot-play-rapjuiuddx.now.sh (copied to clipboard) [5s]\\n> Upload [====================] 100% 0.0s\\n> Sync complete (1.54kB) [2s]\\n> Initializing\\u2026\\n> Building\\n> \\u25B2 npm install\\n> \\u29D7 Installing:\\n>  \\u2023 csv-parse@^1.2.0\\n>  \\u2023 dotenv@^4.0.0\\n>  \\u2023 rita@^1.1.63\\n>  \\u2023 tabletop@^1.5.2\\n>  \\u2023 twit@^2.2.5\\n> \\u2713 Installed 106 modules [3s]\\n> \\u25B2 npm start\\n> > tweet-bot-playground@1.0.0 start /home/nowuser/src\\n> > node index.js\\n> saveFile: file PATH /tmp/nasa.jpg\\n> Media saved!\\n> uploadMedia: file PATH /tmp/nasa.jpg\\n\")), mdx(\"p\", null, \"Woot! You have your bot deployed! \\uD83D\\uDE4C\"), mdx(\"p\", null, \"If you click on the link produced you will be able to inspect the bot\\nas it is on \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"now\"), \" there\\u2019s also a handy logs section on the page where\\nyou can check for output. \\uD83D\\uDC4D\"), mdx(\"h2\", {\n    \"id\": \"contributing\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#contributing\",\n    \"aria-label\": \"contributing permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"Contributing\"), mdx(\"p\", null, \"Please fork this repository and contribute back using pull requests.\"), mdx(\"p\", null, \"Any contributions, large or small, major features, bug fixes and\\nintegration tests are welcomed and appreciated but will be thoroughly\\nreviewed and discussed.\"), mdx(\"h2\", {\n    \"id\": \"license\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", _extends({\n    parentName: \"h2\"\n  }, {\n    \"href\": \"#license\",\n    \"aria-label\": \"license permalink\",\n    \"className\": \"anchor-toc before\"\n  }), mdx(\"svg\", _extends({\n    parentName: \"a\"\n  }, {\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }), mdx(\"path\", _extends({\n    parentName: \"svg\"\n  }, {\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), \"License\"), mdx(\"p\", null, \"MIT License\"), mdx(\"p\", null, \"Copyright (c) 2017, Scott Spence. All rights reserved.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"How to build and deploy a multifunctional Twitter bot! This is a reference for me and anyone else that's interested in\nTwitter bots in…","tableOfContents":{"items":[{"url":"#set-up-the-bot","title":"Set up the bot"},{"url":"#post-statuses","title":"Post Statuses"},{"url":"#work-with-users","title":"Work with users"},{"url":"#interact-with-tweets","title":"Interact with tweets"},{"url":"#use-twitter-search","title":"Use Twitter search"},{"url":"#use-twitter-stream-api","title":"Use Twitter Stream API"},{"url":"#tweet-media-files","title":"Tweet media files"},{"url":"#make-a-markov-bot","title":"Make a Markov bot"},{"url":"#retrieve-and-tweet-data-from-google-sheets","title":"Retrieve and Tweet data from Google sheets"},{"url":"#putting-it-all-together","title":"Putting it all together"},{"url":"#deploy-to-now","title":"Deploy to now"},{"url":"#contributing","title":"Contributing"},{"url":"#license","title":"License"}]},"timeToRead":13,"fields":{"slug":"/2017/05/23/twitter-bot-playground/","editLink":"https://github.com/spences10/thelocalhost.blog/edit/authoring/posts/2017/05/23/twitter-bot-playground/index.md"}}},"pageContext":{"slug":"/2017/05/23/twitter-bot-playground/","previous":{"id":"df945a37-67da-5f62-b293-cb2edde1005b","excerpt":"Just some rationalisation of what I'm currently doing. Recently I have decided that I'm going to improve my current skill\nset. To do this I'm studying with freeCodeCamp for a full stack\ndeveloper certification, I'm also trying to learn as much as I…","frontmatter":{"title":"Code life balance","date":"2017-03-15T00:00:00.000Z"},"fields":{"slug":"/2017/03/15/code-life-balance/"}},"next":{"id":"b465bae8-287e-5195-9502-3b292471ae8c","excerpt":"No doubt you have heard of Git or GitHub for source control, but\n what is source control? “Revision control (also known as version control, source control or\n(source) code management (SCM)) is the management of multiple\nrevisions of the same unit of…","frontmatter":{"title":"Just starting out with Git and GitHub? It gets easier, honest!","date":"2017-05-31T00:00:00.000Z"},"fields":{"slug":"/2017/05/31/gets-better-with-git/"}}}}}