├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── index.js ├── lib ├── signin.js ├── signout.js ├── signup.js ├── star-repo.js ├── unstar-repo.js └── verify-email.js ├── package.json ├── readme.md ├── test └── index.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "standard" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # builds 7 | build 8 | dist 9 | 10 | # misc 11 | .DS_Store 12 | .env 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | .cache 18 | 19 | lerna-debug.log* 20 | lerna-error.log* 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - 9 5 | - 8 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const faker = require('faker') 4 | const getRepositoryUrl = require('get-repository-url') 5 | const ow = require('ow') 6 | const puppeteer = require('puppeteer-extra') 7 | 8 | const signup = require('./lib/signup') 9 | const signin = require('./lib/signin') 10 | const signout = require('./lib/signout') 11 | const starRepo = require('./lib/star-repo') 12 | const unstarRepo = require('./lib/unstar-repo') 13 | const verifyEmail = require('./lib/verify-email') 14 | 15 | puppeteer.use(require('puppeteer-extra-plugin-stealth')()) 16 | 17 | /** 18 | * [GitHub](https://github.com) automation driven by headless chrome. 19 | * 20 | * @param {Object} [opts={ }] - Options 21 | * @param {Object} [opts.browser] - Puppeteer browser instance to use 22 | * @param {Object} [opts.puppeteer] - Puppeteer [launch options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions) 23 | */ 24 | class PuppeteerGitHub { 25 | constructor (opts = { }) { 26 | this._opts = opts 27 | this._user = null 28 | } 29 | 30 | /** 31 | * Whether or not this instance is authenticated with GitHub. 32 | * 33 | * @member {boolean} 34 | */ 35 | get isAuthenticated () { return !!this._user } 36 | 37 | /** 38 | * Authenticated user if authenticated with GitHub. 39 | * 40 | * @member {Object} 41 | */ 42 | get user () { return this._user } 43 | 44 | /** 45 | * Puppeteer Browser instance to use. 46 | * 47 | * @return {Promise} 48 | */ 49 | async browser () { 50 | if (!this._browser) { 51 | this._browser = this._opts.browser || await puppeteer.launch(this._opts.puppeteer) 52 | } 53 | 54 | return this._browser 55 | } 56 | 57 | /** 58 | * Automates the creation of a new GitHub account. 59 | * 60 | * @param {object} user - User details for new account 61 | * @param {string} user.email - Email (required) 62 | * @param {string} [user.username] - Username 63 | * @param {string} [user.password] - Password 64 | * @param {object} [opts={ }] - Options 65 | * @param {boolean} [opts.verify] - Whether or not to verify email 66 | * @param {string} [opts.emailPassword] - Email password for verification 67 | * @return {Promise} 68 | */ 69 | async signup (user, opts = { }) { 70 | if (this.isAuthenticated) throw new Error('"signup" requires no authentication') 71 | ow(user, ow.object.plain.nonEmpty.label('user')) 72 | ow(user.email, ow.string.nonEmpty.label('user.email')) 73 | 74 | user.username = user.username || user.email.split('@')[0] 75 | user.password = user.password || faker.internet.password() 76 | 77 | user.username = user.username 78 | .trim() 79 | .toLowerCase() 80 | .replace(/[^\d\w-]/g, '-') 81 | .replace(/_/g, '-') 82 | .replace(/^-/g, '') 83 | .replace(/-$/g, '') 84 | .replace(/--/g, '-') 85 | 86 | ow(user.username, ow.string.nonEmpty.label('user.username')) 87 | ow(user.password, ow.string.nonEmpty.label('user.password')) 88 | 89 | const browser = await this.browser() 90 | await signup(browser, user, opts) 91 | 92 | this._user = user 93 | 94 | if (opts.verify) { 95 | await this.verifyEmail(opts) 96 | } 97 | } 98 | 99 | /** 100 | * Signs into an existing GitHub account. 101 | * 102 | * Note: either username or email is required. 103 | * 104 | * @param {Object} user - User details for new account 105 | * @param {string} [user.username] - Username 106 | * @param {string} [user.email] - Email 107 | * @param {string} user.password - Password 108 | * @param {Object} [opts={ }] - Options 109 | * @return {Promise} 110 | */ 111 | async signin (user, opts = { }) { 112 | if (this.isAuthenticated) throw new Error('"signin" requires no authentication') 113 | 114 | ow(user, ow.object.plain.nonEmpty.label('user')) 115 | ow(user.password, ow.string.nonEmpty.label('user.password')) 116 | 117 | if (user.email) { 118 | ow(user.email, ow.string.nonEmpty.label('user.email')) 119 | } else { 120 | ow(user.username, ow.string.nonEmpty.label('user.username')) 121 | } 122 | 123 | const browser = await this.browser() 124 | await signin(browser, user, opts) 125 | 126 | this._user = user 127 | } 128 | 129 | /** 130 | * Signs out of the currently authenticated GitHub account. 131 | * @return {Promise} 132 | */ 133 | async signout () { 134 | if (!this.isAuthenticated) throw new Error('"signout" requires authentication') 135 | const browser = await this.browser() 136 | 137 | await signout(browser, this._user) 138 | this._user = null 139 | } 140 | 141 | /** 142 | * Verifies the authenticated GitHub account's email via `puppeteer-email`. 143 | * 144 | * @param {Object} opts - Options 145 | * @param {string} opts.emailPassword - Email password for verification 146 | * @param {string} [opts.email] - Email verification (defaults to user's GitHub email) 147 | * @return {Promise} 148 | */ 149 | async verifyEmail (opts) { 150 | ow(opts, ow.object.plain.nonEmpty.label('opts')) 151 | ow(opts.emailPassword, ow.string.nonEmpty.label('opts.emailPassword')) 152 | 153 | const browser = await this.browser() 154 | await verifyEmail(browser, { 155 | email: opts.email || this.user.email, 156 | password: opts.emailPassword 157 | }, opts) 158 | } 159 | 160 | /** 161 | * Stars an npm package's github repository. 162 | * 163 | * @param {string} pkgName - NPM package name 164 | * @return {Promise} 165 | * 166 | * @example 167 | * const gh = new PuppeteerGitHub() 168 | * await gh.signin(...) 169 | * await gh.starPackage('react') 170 | * await gh.close() 171 | */ 172 | async starPackage (pkgName) { 173 | ow(pkgName, ow.string.nonEmpty.label('pkgName')) 174 | const url = await getRepositoryUrl(pkgName) 175 | return this.starRepo(url) 176 | } 177 | 178 | /** 179 | * Unstars an npm package's github repository. 180 | * 181 | * @param {string} pkgName - NPM package name 182 | * @return {Promise} 183 | */ 184 | async unstarPackage (pkgName) { 185 | ow(pkgName, ow.string.nonEmpty.label('pkgName')) 186 | const url = await getRepositoryUrl(pkgName) 187 | return this.unstarRepo(url) 188 | } 189 | 190 | /** 191 | * Stars a github repository. 192 | * 193 | * @param {string} repo - GitHub repository identifier 194 | * @return {Promise} 195 | * 196 | * @example 197 | * const gh = new PuppeteerGitHub() 198 | * await gh.signin(...) 199 | * await gh.starRepo('avajs/ava') 200 | * await gh.starRepo('https://github.com/facebook/react') 201 | * await gh.close() 202 | */ 203 | async starRepo (repo) { 204 | ow(repo, ow.string.nonEmpty.label('repo')) 205 | const browser = await this.browser() 206 | return starRepo(browser, repo) 207 | } 208 | 209 | /** 210 | * Unstars a github repository. 211 | * 212 | * @param {string} repo - GitHub repository identifier 213 | * @return {Promise} 214 | */ 215 | async unstarRepo (repo) { 216 | ow(repo, ow.string.nonEmpty.label('repo')) 217 | const browser = await this.browser() 218 | return unstarRepo(browser, repo) 219 | } 220 | 221 | /** 222 | * Closes the underlying browser instance, effectively ending this session. 223 | * 224 | * @return {Promise} 225 | */ 226 | async close () { 227 | const browser = await this.browser() 228 | await browser.close() 229 | 230 | this._browser = null 231 | this._user = null 232 | } 233 | } 234 | 235 | module.exports = PuppeteerGitHub 236 | -------------------------------------------------------------------------------- /lib/signin.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const PRIMARY_BUTTON = 'input[type=submit].btn-primary' 4 | 5 | module.exports = async (browser, user, opts) => { 6 | const page = await browser.newPage() 7 | await page.goto('https://github.com/login') 8 | 9 | await page.waitFor('input[name=login]', { visible: true }) 10 | await page.type('input[name=login]', user.username || user.email) 11 | await page.type('input[type=password]', user.password) 12 | await Promise.all([ 13 | page.waitForNavigation(), 14 | page.click(PRIMARY_BUTTON) 15 | ]) 16 | 17 | // => https://github.com/ 18 | await page.close() 19 | } 20 | -------------------------------------------------------------------------------- /lib/signout.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = async (browser, user, opts) => { 4 | const page = await browser.newPage() 5 | await page.goto('https://github.com/') 6 | 7 | await page.waitFor('.HeaderNavlink.name') 8 | page.click('.HeaderNavlink.name') 9 | 10 | await page.waitFor('.logout-form button[type=submit]', { visible: true }) 11 | await Promise.all([ 12 | page.waitForNavigation(), 13 | page.click('.logout-form button[type=submit]') 14 | ]) 15 | 16 | // => https://github.com/ 17 | 18 | await page.close() 19 | } 20 | -------------------------------------------------------------------------------- /lib/signup.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const PRIMARY_BUTTON = 'button[type=submit].btn-primary' 4 | 5 | module.exports = async (browser, user, opts) => { 6 | const page = await browser.newPage() 7 | await page.goto('https://github.com/') 8 | 9 | // username / email / password 10 | // --------------------------- 11 | 12 | // await page.waitForNavigation({ timeout: 0 }) 13 | await page.waitFor('input[id="user[login]"]', { visible: true }) 14 | await page.type('input[id="user[login]"]', user.username) 15 | await page.type('input[id="user[email]"]', user.email) 16 | await page.type('input[type=password]', user.password) 17 | await Promise.all([ 18 | page.waitForNavigation(), 19 | page.click(PRIMARY_BUTTON) 20 | ]) 21 | 22 | // => https://github.com/join/plan 23 | 24 | await page.waitFor(PRIMARY_BUTTON) 25 | await Promise.all([ 26 | page.waitForNavigation(), 27 | page.click(PRIMARY_BUTTON) 28 | ]) 29 | 30 | // => https://github.com/join/customize 31 | 32 | await page.waitFor('a.alternate-action', { visible: true }) 33 | await Promise.all([ 34 | page.waitForNavigation(), 35 | page.click('a.alternate-action') 36 | ]) 37 | 38 | // => https://github.com/dashboard 39 | 40 | // next step is to verify email from 'noreply@github.com' 41 | await page.close() 42 | } 43 | -------------------------------------------------------------------------------- /lib/star-repo.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const delay = require('delay') 4 | const parseGithubUrl = require('parse-github-url') 5 | 6 | module.exports = async (browser, repo, opts) => { 7 | const parsed = parseGithubUrl(repo) 8 | const url = `https://github.com/${parsed.repository}` 9 | 10 | const page = await browser.newPage() 11 | await page.goto(url) 12 | 13 | const isStarred = await page.$eval('.starred button', $el => $el.offsetHeight > 0) 14 | 15 | if (!isStarred) { 16 | await page.click('.unstarred button') 17 | await delay(500) 18 | } 19 | 20 | await page.close() 21 | } 22 | -------------------------------------------------------------------------------- /lib/unstar-repo.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const delay = require('delay') 4 | const parseGithubUrl = require('parse-github-url') 5 | 6 | module.exports = async (browser, repo, opts) => { 7 | const parsed = parseGithubUrl(repo) 8 | const url = `https://github.com/${parsed.repository}` 9 | 10 | const page = await browser.newPage() 11 | await page.goto(url) 12 | 13 | const isStarred = await page.$eval('.starred button', $el => $el.offsetHeight > 0) 14 | 15 | if (isStarred) { 16 | await page.click('.starred button') 17 | await delay(500) 18 | } 19 | 20 | await page.close() 21 | } 22 | -------------------------------------------------------------------------------- /lib/verify-email.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const PuppeteerEmail = require('puppeteer-email') 4 | const cheerio = require('cheerio') 5 | const pRetry = require('p-retry') 6 | 7 | module.exports = async (browser, user, opts = { }) => { 8 | const client = new PuppeteerEmail(user.email) 9 | 10 | const session = await client.signin(user, { 11 | ...opts, 12 | browser 13 | }) 14 | 15 | // TODO: checking for a valid email should be part of the retry loop 16 | const emails = await pRetry(async () => session.getEmails({ 17 | query: 'from:noreply@github.com please verify' 18 | }), { 19 | retries: 4 20 | }) 21 | 22 | // console.log(JSON.stringify(emails, null, 2)) 23 | const validEmails = emails 24 | .filter((email) => email && email.html) 25 | 26 | if (validEmails.length) { 27 | const email = validEmails[0] 28 | const $ = cheerio.load(email.html) 29 | const url = $('a.cta-button').attr('href') 30 | 31 | const page = await browser.newPage() 32 | await page.goto(url) 33 | 34 | // "Your email was verified." OR " is already verified." 35 | // const flash = page.$('#js-flash-container', $el => $el.textContent.trim()) 36 | // if (flash && /verified/i.test(flash)) 37 | 38 | await page.close() 39 | } else { 40 | throw new Error(`unable to find verification email for "${user.email}"`) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppeteer-github", 3 | "version": "0.0.5", 4 | "description": "GitHub automation driven by headless chrome.", 5 | "main": "index.js", 6 | "repository": "transitive-bullshit/puppeteer-github", 7 | "author": "Travis Fischer ", 8 | "license": "MIT", 9 | "reveal": true, 10 | "scripts": { 11 | "docs": "update-markdown-jsdoc", 12 | "test": "ava -v && standard" 13 | }, 14 | "engines": { 15 | "node": ">=8" 16 | }, 17 | "keywords": [ 18 | "puppeteer", 19 | "github" 20 | ], 21 | "devDependencies": { 22 | "ava": "^0.25.0", 23 | "standard": "^11.0.0", 24 | "update-markdown-jsdoc": "^1.0.6" 25 | }, 26 | "dependencies": { 27 | "cheerio": "^1.0.0-rc.2", 28 | "delay": "^3.0.0", 29 | "faker": "^4.1.0", 30 | "get-repository-url": "^2.0.0", 31 | "ow": "^0.6.0", 32 | "p-retry": "^2.0.0", 33 | "parse-github-url": "^1.0.2", 34 | "puppeteer": "^1.6.0", 35 | "puppeteer-email": "^0.1.0", 36 | "puppeteer-extra": "^2.0.7", 37 | "puppeteer-extra-plugin-anonymize-ua": "^2.0.7", 38 | "puppeteer-extra-plugin-stealth": "^2.0.7" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # puppeteer-github 2 | 3 | > [GitHub](https://github.com) automation driven by headless chrome. 4 | 5 | [![NPM](https://img.shields.io/npm/v/puppeteer-github.svg)](https://www.npmjs.com/package/puppeteer-github) [![Build Status](https://travis-ci.com/transitive-bullshit/puppeteer-github.svg?branch=master)](https://travis-ci.com/transitive-bullshit/puppeteer-github) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 6 | 7 | This module also has a [CLI](https://github.com/transitive-bullshit/puppeteer-github-cli). 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install --save puppeteer-github 13 | ``` 14 | 15 | ## Usage 16 | 17 | This example signs into a [GitHub](https://github.com) account. 18 | 19 | ```js 20 | const PuppeteerGitHub = require('puppeteer-github') 21 | 22 | const github = new PuppeteerGitHub() 23 | 24 | await github.signin({ username: 'xxx', password: 'xxx' }) 25 | await github.starRepo('facebook/react') 26 | 27 | await github.close() 28 | ``` 29 | 30 | ## API 31 | 32 | 33 | 34 | #### Table of Contents 35 | 36 | - [PuppeteerGitHub](#puppeteergithub) 37 | - [isAuthenticated](#isauthenticated) 38 | - [user](#user) 39 | - [browser](#browser) 40 | - [signup](#signup) 41 | - [signin](#signin) 42 | - [signout](#signout) 43 | - [verifyEmail](#verifyemail) 44 | - [starPackage](#starpackage) 45 | - [unstarPackage](#unstarpackage) 46 | - [starRepo](#starrepo) 47 | - [unstarRepo](#unstarrepo) 48 | - [close](#close) 49 | 50 | ### [PuppeteerGitHub](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L20-L199) 51 | 52 | [GitHub](https://github.com) automation driven by headless chrome. 53 | 54 | Type: `function (opts)` 55 | 56 | - `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Options (optional, default `{}`) 57 | - `opts.browser` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Puppeteer browser instance to use 58 | - `opts.puppeteer` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Puppeteer [launch options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions) 59 | 60 | * * * 61 | 62 | #### [isAuthenticated](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L32-L32) 63 | 64 | Whether or not this instance is authenticated with GitHub. 65 | 66 | Type: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean) 67 | 68 | * * * 69 | 70 | #### [user](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L39-L39) 71 | 72 | Authenticated user if authenticated with GitHub. 73 | 74 | Type: [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) 75 | 76 | * * * 77 | 78 | #### [browser](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L46-L52) 79 | 80 | Puppeteer Browser instance to use. 81 | 82 | Type: `function ()` 83 | 84 | * * * 85 | 86 | #### [signup](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L66-L77) 87 | 88 | Automates the creation of a new GitHub account. 89 | 90 | Type: `function (user, opts): Promise` 91 | 92 | - `user` **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** User details for new account 93 | - `user.username` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Username 94 | - `user.email` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Email 95 | - `user.password` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Password 96 | - `opts` **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Options (optional, default `{}`) 97 | - `opts.verifyEmail` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Whether or not to verify email 98 | - `opts.emailPassword` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Email password for verification 99 | 100 | * * * 101 | 102 | #### [signin](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L91-L98) 103 | 104 | Signs into an existing GitHub account. 105 | 106 | Note: either username or email is required. 107 | 108 | Type: `function (user, opts): Promise` 109 | 110 | - `user` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** User details for new account 111 | - `user.username` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Username 112 | - `user.email` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Email 113 | - `user.password` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Password 114 | - `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Options (optional, default `{}`) 115 | 116 | * * * 117 | 118 | #### [signout](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L104-L111) 119 | 120 | Signs out of the currently authenticated GitHub account. 121 | 122 | Type: `function (): Promise` 123 | 124 | * * * 125 | 126 | #### [verifyEmail](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L121-L131) 127 | 128 | Verifies the authenticated GitHub account's email via pupeteer-email. 129 | 130 | Type: `function (opts): Promise` 131 | 132 | - `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Options 133 | - `opts.emailPassword` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Email password for verification 134 | - `opts.email` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Email verification (defaults to user's GitHub email) 135 | 136 | * * * 137 | 138 | #### [starPackage](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L145-L148) 139 | 140 | Stars an npm package's github repository. 141 | 142 | Type: `function (pkgName): Promise` 143 | 144 | - `pkgName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** NPM package name 145 | 146 | Example: 147 | 148 | ```javascript 149 | const gh = new PuppeteerGitHub() 150 | await gh.signin(...) 151 | await gh.starPackage('react') 152 | await gh.close() 153 | ``` 154 | 155 | * * * 156 | 157 | #### [unstarPackage](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L156-L159) 158 | 159 | Unstars an npm package's github repository. 160 | 161 | Type: `function (pkgName): Promise` 162 | 163 | - `pkgName` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** NPM package name 164 | 165 | * * * 166 | 167 | #### [starRepo](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L174-L177) 168 | 169 | Stars a github repository. 170 | 171 | Type: `function (repo): Promise` 172 | 173 | - `repo` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** GitHub repository identifier 174 | 175 | Example: 176 | 177 | ```javascript 178 | const gh = new PuppeteerGitHub() 179 | await gh.signin(...) 180 | await gh.starRepo('avajs/ava') 181 | await gh.starRepo('https://github.com/facebook/react') 182 | await gh.close() 183 | ``` 184 | 185 | * * * 186 | 187 | #### [unstarRepo](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L185-L188) 188 | 189 | Unstars a github repository. 190 | 191 | Type: `function (repo): Promise` 192 | 193 | - `repo` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** GitHub repository identifier 194 | 195 | * * * 196 | 197 | #### [close](https://github.com/transitive-bullshit/puppeteer-github/blob/26f3f7d2bf8f52fecd0024135c17fffd5251afe9/index.js#L195-L198) 198 | 199 | Closes the underlying browser instance, effectively ending this session. 200 | 201 | Type: `function (): Promise` 202 | 203 | * * * 204 | 205 | ## Related 206 | 207 | - [puppeteer-github-cli](https://github.com/transitive-bullshit/puppeteer-github-cli) - CLI for this module. 208 | - [puppeteer-email](https://github.com/transitive-bullshit/puppeteer-email) - Email automation driven by headless chrome. 209 | - [puppeteer](https://github.com/GoogleChrome/puppeteer) - Headless Chrome Node API. 210 | - [awesome-puppeteer](https://github.com/transitive-bullshit/awesome-puppeteer) - Curated list of awesome puppeteer resources. 211 | 212 | ## License 213 | 214 | MIT © [Travis Fischer](https://github.com/transitive-bullshit) 215 | 216 | Support my OSS work by following me on twitter twitter 217 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test } = require('ava') 4 | 5 | const PuppeteerGitHub = require('..') 6 | 7 | test('basic', (t) => { 8 | const github = new PuppeteerGitHub() 9 | t.is(github.isAuthenticated, false) 10 | t.is(github.user, null) 11 | }) 12 | --------------------------------------------------------------------------------